Unityでゲーム開発_公式チュートリアルで使い方の復習

1年以上前にUnity公式のチュートリアルをいくつか試してみたことがあるが、その後Unityを触らなくなり、使い方を忘れてしまった。シンプルな2Dゲームのチュートリアル(2D UFO Tutorial)で基本的な部分を復習したので、その流れを記してみる。

Unityの使い方を細かいところまで説明しているわけではないので、チュートリアルの代わりにはならないが、自分なりに作業の流れをアウトプットすることで理解が深まった部分がたくさんあった。もし、チュートリアルを試してみたけど、よく分からない部分があるという方がいたならば、オリジナルのチュートリアル作りをお勧めしたい。

本来、2Dのゲームは2Dオブジェクトを使って作るが、Unityで2Dオブジェクトを作成するためには画像ファイルを準備する必要があるので、ここではUnity内で生成できる3Dオブジェクトを使って2Dのゲームを作ってみた。

新規2Dプロジェクトを作成する

  1. Unityを起動し、新規のプロジェクトを作成する。
    • 2D/3Dの選択で2Dを選び、プロジェクト名、プロジェクトファイルの保存場所を指定してプロジェクトを作成する。
      • Unityの2Dと3Dのプロジェクトの違いは、主にカメラのデフォルトの設定で、2Dの場合はSceneタブの表示を2Dモードにした状態で作業がしやすいように出来ている。カメラは水平に設置され、3D空間のZ軸と同じ方向を向いている。サイドビューのゲームを作ったときに、オブジェクトが重力の影響を受けるようにすると、自動で重力の影響を計算してくれるようになっている。トップビューのゲームを作る場合は、オブジェクトの重力の影響を無効にする。

ゲームのフィールドを作る

  1. 背景を作成
    • Position(0, 0, 1)にScale(9, 9, 1)のCubeを作成する。
      • CubeなどのオブジェクトはHierarchyタブ内に作られ、Sceneタブ内で空間的に見た目を確認できる。
    • 適当な色のMaterialを作って適用する。
      • MaterialはProjectタブ内に作られる。オブジェクトを選択してInspectorタブ内のMesh RendererのMaterialsで適用すると、オブジェクトの見た目が変わる。
  2. 壁を作成
    • Position(4,0,0)にScale(1,9,1)のCubeを作成する。
    • プレイヤーが壁にぶつかったときに衝突処理をして、フィールドからプレイヤーが飛び出すのを防ぐよう、CubeにBox Colliderを追加する。設定の変更は特になし。
      • Box ColliderなどのコンポーネントはInspectorタブ内に作られ、オブジェクトの設定に用いられる。
    • 背景とは異なる適当な色のMaterialを作って適用する。
    • Position(-4, 0, 0)にScale(1, 9, 1)の壁の複製を作成する。
      • このケースではサイズと位置を変更したが、回転と位置を変更してもよい。
    • Position(0, 4, 0)にScale(9, 1, 1)の壁の複製を作成する。
    • Position(0, -4, 0)にScale(9, 1, 1)の壁の複製を作成する。
  3. 光源を作成
    1. Rotation(50, -30, 0)のDirectional lightを作成する。

すると次のようなものが出来上がる。

カメラの位置を決める

  1. 新規プロジェクトを作成したときから配置されているMain Cameraの位置を調節してフィールド全体がカメラの描画範囲に入るようにする。
    • 2Dのカメラモード(Orthographic)ではオブジェクトが奥行方向に動いてもゲーム画面でのサイズが変わらない。ただ、オブジェクトがカメラの描画範囲に 入っていないとゲーム画面で表示されないので、2Dオブジェクトを使う場合でも奥行方向におけるカメラの描画範囲には気を付ける必要がある。
    • Main Cameraを選択し、Inspectorタブ内のTransformにあるPositionを(0, 0, -1)に設定する。

回収するオブジェクトを作る

  1. 回収オブジェクトを作成
    • Z=0の平面上にRotation(45, 45, 0)、Scale(0.5, 0.5, 0.5)のCubeを作成する。
    • 適当な色のMaterialを作って適用する。
    • Box Colliderを追加する。
      • 衝突判定の処理が必要になるので、”Is Trigger”のチェックをOnにする。
    • Inspectorタブ内で回収オブジェクトのTagを”Pickup”に設定する。
      • 衝突判定でオブジェクトの判別に使用する。
      • まずは、tagのプルダウンメニューのAdd Tagを使い”Pickup”というタグを追加。
      • そして、プルダウンメニューから”Pickup”を選ぶ。
    • Rigidbodyを追加する。
      • チュートリアルの説明によると、物理演算を行わないオブジェクトでもRigidbodyを追加した方が処理の効率が上がるケースがあるとのこと。そのため、回収オブジェクトにもRigidbodyを追加しておく。
  2. 回収オブジェクトを回転させる
        • 演出として回収オブジェクトを回転させるために、スクリプト(RotatePickup.csという名前のC#スクリプトファイル)を追加する。
          • スクリプトのファイル自体はProjectタブ内に作られる。それをオブジェクトの制御に使うためには、オブジェクトにスクリプトのコンポーネントを追加してスクリプトファイルを指定する。
        • 回転処理のスクリプトを編集する。
          • 初期化処理は不要なので、Start関数のブロックを削除する。
          • 画面の描画更新に合わせてオブジェクトを回転させるために、Update関数にオブジェクトの回転を制御するコードを追加する。
        void Update () {
            transform.Rotate(new Vecrot3(0, 0, 45) * Time.deltaTime);
        }
    
  3. 回収オブジェクトを複製して配置する
    • Hierarchyタブの回収オブジェクトをProjectタブにドラッグ&ドロップしてプリファブに変換する
      • プリファブはオブジェクトやオブジェクトの設定をひとまとめにしたひな形で、同じものを複数作成するときに便利。プリファブを編集すると、同じプリファブは一括して設定を変更できる。
      • プリファブを作成するとHierarchyタブのオブジェクトもプリファブに変換される。
    • Hierarchyタブ又はSceneタブ内で回収オブジェクトを選択し、複製してZ=0の平面上の適当な位置に12個配置する。

ここまでで、次のような状態になる。

プレイヤーのオブジェクトを作る

  1. プレイヤーを作成する
    • Position(0, 0, 0)にScale(1, 1, 1)のSphereを作成する。
      • 衝突判定に必要なSphere Colliderは最初から組み込まれている。
    • 適当な色のMaterialを作って適用する。
    • オブジェクトの移動に物理演算を利用するためにRigidbodyを追加する。
      • Use GravityのチェックをOFFにする。
  2. プレイヤーを制御するためのコントローラーを追加する
      • プレイヤーオブジェクトにスクリプト(PlayerController.csという名前のC#スクリプトファイル)を追加して、まずはプレイヤーの動きを制御するためのコードを書く。
          • プレイヤーのRigidbodyを格納するための変数(playerRb)をPlayerControllerクラス内に作成する。
            • Rigidbodyが物理演算を担当するので、コードの中でプレイヤーのRigidbodyにアクセスしてコントロールをする。
          • プレイヤーの移動の速さを調整するための変数(speed)をPlayerControllerクラス内に作成する。
            • 変数をスクリプトの外でも使えるようにpublicにする。そうすると、UnityエディタのInspectorタブ内でスクリプトコンポーネントのところに変数が表示されるようになり、エディタで値を変更できるようになる。Unityエディタで値を5に設定してみる。
        public class PlayerController : MonoBehaviour {
            private Rigidbody playerRb;
            public float speed;
            …
        
          • Start関数内でプレイヤーのRigidbodyを変数に格納して初期化する。
            void start() {
                playerRb = getComponent<Rigidbody>();
            }
        
        • Update関数をFixedUpdateに書き変えて、プレイヤーを動かすコードを書く。
          • FixedUpdate関数は一定の間隔で呼び出される。力を加えて加速、減速を行うなど、物理演算でオブジェクトを動かすコードなどをここに書く。
          • Updateは画面の描画が更新されるときに呼び出される関数で、ハードウェア環境や処理負荷によって画面の更新が遅くなると呼び出される間隔が変わってくる。回収オブジェクトの回転のように、物理演算は使わずに描画の更新に合わせて処理を行う場合に使う。
          • Input.getAxisで縦横の方向キーの入力を検出し、押された場合にmoveHorizontal、moveVerticalに値を格納する。
          • 縦横方向キーの入力をVector2を使ってゲーム空間内のx、y平面上の方向に変換する。
          • RigidbodyのaddForceを使ってキー入力の方向に力を加える。力を加えるとUnityが物理演算を行ってオブジェクトが動く。
        void FixedUpdate() {
            float moveHorizontal = Input.getAxis("Horizontal");
            float moveVertiacl = Input.getAxis("Vertical");
            Vector2 movement = new Vector2(moveHorizontal, moveVertiacl);
    
            playerRb.addForce(movement * speed);
        }
    
    • 回収オブジェクトと衝突したらオブジェクトを回収するコードを書く。
        • スクリプトを適用しているオブジェクト(ここではPlayer)が”Is Trigger”がOnになっているColliderに衝突したときに呼び出されるOnTriggerEnter関数を使って衝突した回収オブジェクトを消すコードを書く。
        • 衝突したColliderのTagをチェックして、”Pickup”だった場合に衝突したオブジェクトの表示をOffにする。
          void OnTriggerEnter(Collider other) {
              if(other.CompareTag("Pickup") {
                  other.gameObject.SetActive(false);
              }
          }
      

ここまでで、プレイヤーを動かして回収オブジェクトを回収することが出来るようになる。Playを実行すると矢印キーでプレイヤーを動かして回収オブジェクトを回収できる。

表示インターフェースを作る

  1. 回収したオブジェクトの数を表示するカウンタを作成する
    • Hierarchyタブ内にTextを作る。
      • TextはUIの一種。
      • Textを作ると、Canvasが自動で作成され、その中にTextが出来る。
      • CanvasはSceneタブでは見にくいので、Gameタブに切り替えると、どのように表示されるかがよく分かる。
      • Textを選択して、Inspectorタブ内のRect TransformでTextの位置を画面の左上辺りに配置し、Text(Script)で文字の色を見やすく調整する。
  2. ゲームの終了メッセージを作成する
    • Hierarchyタブ内にもう一つTextを作る。
      • すでにCanvasが存在する場合は新しいTextはその中に作られる。
      • 位置を中央やや上、文字のサイズを大きく、文字の色を見やすく調整する。
  3. プレイヤーの制御スクリプトにTextをコントロールするためのコードを追加する。
      • スクリプト内でUIをコントロールするためには、UIコントロール用のコードをまとめてある名前空間を呼び出す。
    …
    using UnityEngine;
    // ここから追加
    using UnityEngine.UI;
    // ここまで追加
    
    public class PlayerController : MonoBehaviour {
    …
    
        • Unityエディタ内で表示に使うTextを指定できるようにするため、PlayerControllerクラス内でpublicなText用変数(scoreTextとwinText)を作成する。また、回収したオブジェクトのカウンタ用の変数(score)も作成する。
    …
    public class PlayerController : MonoBehaviour {
        public float speed;
        private Rigidbody playerRb;
    
        // ここから追加
        public Text scoreText;
        public Text winText;
        private int score;
        // ここまで追加
    
    …
    
      • Unityエディタでプレイヤーを選択すると、InspectorタブのPlayer ControllerのコンポーネントにTextを指定する枠が2つ出来ているので、カウンタ用と終了メッセージ用のTextを指定する。
      • PlayerControllerクラス内でカウンタのテキスト更新のための関数を定義する。
        void updateScore() {
            scoreText.text = "score: " + score.ToString();
        }
    
      • Start関数内で、カウンタ、カウンタ用テキスト、終了メッセージを初期化する。
        void Start () {
            playerRb = GetComponent<Rigidbody>();
    
            // ここから追加
            score = 0;
            updateScore();
            winText.text = "";
            // ここまで追加
        }
    
      • OnTriggerEnter関数内で、回収オブジェクトに衝突したときにカウンタを更新し、全て回収した場合に終了メッセージを表示するコードを書く。
        void OnTriggerEnter (Collider other) {
            if(other.CompareTag("Pickup")){
                other.gameObject.SetActive(false);
    
                // ここから追加
                score = score + 1;
                updateScore();
                if(score >= 12) {
                    winText.text = "You Win!!";
                }
                // ここまで追加
            }
        }
    

これで、プレイヤーが回収オブジェクトにぶつかるとカウンタが加算され、全て回収し終えたら終了メッセージが表示される。

カメラをプレイヤーに追従させる

  1. Cameraをコントロールするためのコントローラーを作成する。
    • カメラをプレイヤーの動きに追従させるために、Main Cameraにスクリプト(CameraControll.csという名前のC#スクリプトファイル)を追加する。
    • カメラを動かすためのコードを書く。
        • ゲームの初期状態におけるプレイヤーのPositionが(0, 0, 0)、カメラのPositionが(0, 0, -10)なので、カメラの位置をプレイヤーの位置からZ軸方向に-10離れたところにキープし続けるとカメラがプレイヤーに追従するようになる。
        • CameraControllクラス内にカメラとプレイヤーの位置関係を格納する変数(offset)を作成する。
        • プレイヤーの位置を取得するためにプレイヤーのGameObjectをUnityエディタ内で指定できるようにpublicなGameObjectの変数(player)も作成する。
      public class CameraController : MonoBehaviour {
          private Vector3 offset;
          public GameObject player;
          …
      
        • Start関数内でゲームスタート時のプレイヤーとカメラの位置関係を変数に格納する。
          void Start () {
              offset = transform.position - player.transform.position;
          }
      
      • Update関数をLateUpdateに書き変えて、プレイヤーの動きに合わせてカメラの位置を更新するコードを書く。
          • プレイヤーの位置に対してZ軸方向に-10離れたところにカメラを置くという処理を行う。このとき、プレイヤーの移動を確定させて、その後カメラの位置を更新するという順番で処理をしないと、カメラはスムーズにプレイヤーを追従できない。LateUpdate関数はゲーム内の様々な更新処理のサイクルの最後に実行されるので、この中でカメラの位置を更新する処理を行うと、処理の順番が前後してカメラがプレイヤーに追従する位置からズレるということが起こらない。
            void LateUpdate () {
                transform.position = player.transform.position + offset;
            }
        
      • Unityエディタ内でカメラを選択し、Camera Controllerコンポーネントのplayer変数にプレイヤーのオブジェクトを指定するとカメラがプレイヤーに追従するようになる。

これで公式チュートリアルの2D UFO Tutorialを3Dオブジェクトに置き換えたものが完成する。後から確認して気が付いたことだが、2D UFO Tutorialを3Dのプロジェクトとして作ると、公式チュートリアルのRoll-a-ball tutorialと同じものになる。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です