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の提供する仕組みについて色々まだ書いてない事があるのでその辺りの記事を書きたいなぁと思ってます。 ありがとうございました。

UnityのRaycast

Unity公式のLet's Try: Shooting with raycastsというチュートリアルをやっていたらRaycastという技術が出てきたのでRaycastについての自分なりのまとめです。
以下チュートリアル

unity3d.com

ざっくりと

RayとCastに分解して意味を考えてみます。
Ray:光線
Cast:放つ
つまり光線を放つことです。

概要

ちゃんとした概要です。
Rayとはゲーム上の空間で、ある地点からある方向へ引いた半直線です。 このRayという直線を瞬間的に引く事をRaycastと言います。
チュートリアルでは下記のような図が出てきますが、実際には地点A、地点Bといった指定ではなく、始点、方向を決めて半直線を引くようなイメージです。 f:id:TEAREitai:20171108230016p:plain (公式チュートリアルから引用)

RaycastすることによってRayに当たったり貫通したGameObjectの情報を取得することが出来ます。
この情報はRaycastHitというクラスのオブジェクトとして受け取れます。

具体的な使い方

まずRaycastで検知されてほしいGameObjectにColliderコンポーネントを付けます。
ColliderコンポーネントとはGameObject同士の衝突を検知するためのコンポーネントです。(多分格ゲーの当たり判定みたいなものだと思います。)
Raycastで放つRayがこのColliderに触れることでヒットしたことになります。

次にPhysics.Raycastメソッドを呼び出しましょう。

公式リファレンス:Unity - スクリプトリファレンス: Physics.Raycast
リファレンスには何個か種類がありますが、基本的には同じです。
引数originにRayの始点、directionに方向ベクトル、maxDistanceにRayの長さを渡します。
戻り値はRayがコライダーにヒットしたかどうかがBoolで返ってきます。
ヒットしたコライダーの情報を受け取りたい場合は引数hitInfoにRaycastHit型の変数の参照を渡し、ヒットしていた場合は渡した変数の中にRaycastHitオブジェクトが格納されています。

コードの例として以下のように使います

public class RaycastExample : MonoBehaviour
{
    public Missile missile;

    void FixedUpdate()
    {
        //Raycast型変数hitを宣言
        RaycastHit hit;
        
        //out修飾子を付けてhit変数の参照を渡す
        if (Physics.Raycast(transform.position, -Vector3.up, out hit))
        {
            //hit変数内のコライダー情報にアクセス        
            print("Found an object - distance: " + hit.distance);
        }
    }
}  

ちなみにPhysics.Raycastメソッドでは最初にヒットしたコライダー情報しか取得出来ませんが、
Physics.RaycastAllメソッドを使えばRayにヒットした全てのコライダー情報がRaycastHit型配列として取得出来ます。

Unity - スクリプトリファレンス: Physics.RaycastAll

最後に

公式チュートリアルFPSの銃撃の当たり判定として使っていたように、FPS視点でカーソルをあわせたオブジェクトの情報を取得するような状況で良く使う仕組みだと思います。 FPSゲーム以外でもプレイヤーの真下にRayを発射して接地判定を行ったりなどの使いみちもありそうですね!

こんな感じでこれから自分なりに調べたことをまとめたり、作ったゲームなんかの紹介を行っていけたら良いなと思います! よろしくお願いします!!!!