Unityでブロック崩し

Unityでブロック崩しをつくりました。
一通り作ったときの流れなんかを紹介したいと思います。
作成途中のスクショとかは撮ってないので殺風景な記事になってしまいますがよろしくお願いします。

ステージを作る

まずはステージを作ります。
今回作るブロック崩しはZ座標0のXY平面上をボールが移動するような動きをするので、z = 0上にcubeを適当に伸ばしたものを壁として、下を抜いた上、右、左に配置します。
そしてCreate Emptyで空のGameObjectを作り、名前をStageとします。
配置した壁'sを選択してStageにドロップしてStageの子オブジェクトとします。
最後にStageのInspectorView上のStaticにチェックを付けます。
これでステージの作成は完了です。

ボールオブジェクトを作る

3D ObjectのSphereをScene上に配置して、TransformのPositionをリセットして(0,0,0)に合わせます。
このボールに反射する動きを付けます。
Assets > Create > Physic MaterialでPhysicsMterialを作成し、名前をBallPhysicsMaterialとしました。
Assets内のBallPhysicsMaterialをシーン上のボールオブジェクトのColliderにアタッチします。
BallPhysicsMaterialのパラメータを次の様にして、ボールの弾性率を1に、摩擦を0にします。
f:id:TEAREitai:20171112164016p:plain
PhysicsMateriaalについて詳しくは以下ドキュメント参照

Unity - マニュアル: Physic Material

次にボールの動きを管理するスクリプトを追加します。AddComponent>New ScriptでBallController.csを作成。

public class BallController : MonoBehaviour {
    public float speed = 10;

    private Rigidbody rigidbody;

    // Use this for initialization
    void Start() {
        //初速与える。
        rigidbody = GetComponent<Rigidbody>();
        rigidbody.velocity = (Vector3.up + Vector3.right).normalized * speed;
    }

    private void Update() {
        //速度保存
        var current = rigidbody.velocity;
        rigidbody.velocity = current.normalized * speed;
    }
}

Vector3クラスのnormailizedプロパティは自分自身の単位ベクトルを返す変数です。
これにspeedを掛けてやれば、向きが同じで大きさがspeedのベクトルが得られます。

バーを作る

3D Objectのcubeを作って大きさを調節して、位置をステージの下と合わせます。
次にバーを動かすスクリプトを追加します。BallController.csと同じ要領でBarController.csをバーオブジェクトに追加します。

public class BarController : MonoBehaviour {
    // Update is called once per frame
    void Update () {
        //メインのカメラ取得
        var camera = Camera.main;
        //マウスの座標を取得してそれをワールド座標に変換
        var mousePosition = Input.mousePosition;
        mousePosition.z = -camera.transform.position.z;//変換後のz座標が0になるように調整
        var worldPosition = camera.ScreenToWorldPoint(mousePosition);
        transform.position = new Vector3(worldPosition.x, transform.position.y, transform.position.z);
    }
}

ポイントはマウスのスクリーン上の座標を、ゲーム空間上の座標に変換する"前"にz座標を指定してやる必要がある事です。
このあたりの座標系の変換はややこしいので、また別の機会に記事で書くかもしれないです。
またUnityで使われる座標系に関するまとめは以下の記事が参考になります。

UnityのCameraが使う3つの座標系 - テラシュールブログ

ブロックを作る

こちらも3D Objectのcubeを良いように大きさ調節します。 同じ要領でスクリプトを作ります。

public class BlockController : MonoBehaviour {
    private void OnCollisionEnter(Collision collision) {
        Destroy(gameObject);
    }
}

コードは3行だけです。衝突があった際に自分自身を消す、という処理だけです。
次にこのブロックオブジェクトをプロジェクトウィンドウのAssets配下にドロップしてプレハブ化します。
プレハブに関しては以下のドキュメント参照。

Unity - マニュアル: プレハブ
このプレハブブロックをシーン上にドロップして行くことでブロックを配置します。 最後にCreate Emptyでblocksという空のGameObjectを作り、配置したブロックをblocksの子オブジェクトに指定します。

クリア判定をつける

クリア判定にはいい方法が思いつかなかったので妥協しました。
クリアテキスト表示用にCreate>UI>TextでTextをシーンに配置します。
Textのポジションを調節してGame View上に文字が表示されるようにします。

次にCreate EmptyでSceneManagerObjectという空のオブジェクトを作成します。
そいつにSceneManager.csというスクリプトを追加します。

public class SceneManager : MonoBehaviour {
    //ブロックをまとめてるGameObject
    public GameObject blocksObject;
    public Text clearText;

    // Update is called once per frame
    void Update() {
        var childrens = blocksObject.GetComponentsInChildren<Transform>().Length;
        //blockObjectの子要素が1ならクリア※blocks自体のtransformはあるので常に1残る。
        if (childrens <= 1) {
            clearText.text = "Clear";
        }
    }
}

最後にSceneMangerObjectのInspectorからblockObjectプロパティにblocksをアタッチ、clearTextにはじめに作ったTextをアタッチして完成です。
Updateメソッド内でblocksの子要素を全て取得しているので、毎フレームその処理が走っていて少し気持ち悪いですね・・・。
クリア判定自体もblocksObjectのTransformが残ってるから1以下というぞわぞわする感じですね。

一緒にやっていた友達のやり方では新しくblockというタグを定義して、ブロックオブジェクトのTagにblockを指定。
Update内でTagがblockのものだけ取得して、数が0ならクリアという判定にしていました。
こちらはスマートな判定ですね。

最後に

作り初めはこんなんちょろいぜ、と思っていましたが作り始めてみると割りと初めて知った事とか詰まるようなポイントがありました。
特にマウス位置とバーの位置を同期させる部分でz座標の指定を忘れていて、動かねえ!!と15分くらい詰まってしまいました・・・。
作業時間的には3時間ほどで一息で完成まで駆け抜けられるのでモチベーション上がらなくて完成しねえ、とかもなく良い題材だったと思います。
今後もこんな感じの数時間くらいで終わる物を定期的に作っていけたらいいなぁ。

次はUnityの提供する仕組みについて色々まだ書いてない事があるのでその辺りの記事を書きたいなぁと思ってます。 ありがとうございました。