Unity 5でコインプッシャーゲームを作ろう(中編)

前回に引き続き、コインプッシャーゲームを製作していきます。前回はUnity 5の基本操作を解説しながらコインを作成し、それに物理的な挙動を付けました。今回はスクリプトを記述して、ゲームオブジェクトの挙動を制御してみます。スクリプトについて説明する前に、コインプッシャーゲームで使用されるゲームオブジェクトの名前と役割を図1のように定義します。
スポーナー | コインを生成するオブジェクト。PCの[←][→]キーで移動、[スペース]キーでコインを生成 |
---|---|
ウォール | コインが土台とプッシャーから落ちないようにするオブジェクト |
プッシャー | コインを押し出すためのオブジェクト |
今回は、Unityのスクリプトを実際に記述して、ゲーム再生中にゲームオブジェクトを制御していきます。具体的には、以下のことを習得します。
- プッシャーをスクリプトで動かす(スクリプトの基本)
- コインを落とすためのスポーナーを作る(スポーナーの移動を実装)
- コインをPrefab化して同じ物を生成する(Prefabの方法、Instantiate関数で生成)
- スポーナーがウォールより外に行かないようにする(スクリプトで移動を制限)
プッシャーを作ろう
プッシャーをスクリプトで動かす前に、まずはプッシャーを作りましょう。連載第2回「コインプッシャーの土台を作る(オブジェクトをシーン上に生成)」を参考に、プッシャーを作成して土台から少しはみ出すように設置します(図2)。そして、名前も「Pusher」に変更しておきましょう。
プッシャーの大きさは、土台と同じ大きさ(X=5、Y=1、X=5)に設定しています。また、土台とプッシャーが同じ色だと見にくくなってしまうので、マテリアルで好きな色を付けておきましょう(色の付け方は連載第2回「コインに色をつける(マテリアルの作成と貼り付け)」を参照)。
そして、Pusherのインスペクタウィンドウ下部にある[Add Component]をクリックすると表示されるメニューから[Physics]→[Rigidbody]を選択し、Rigitboby内の「Use Gravity」のチェックマークを外してください。これでプッシャーに重力が掛からなくなり、自然落下しなくなります。
さらに「is Kinematic」にチェックマークを付けてください(図3)。チェックマークを付けると、そのオブジェクトは他のオブジェクトからの衝突や力を受けても影響されません。スクリプトを記述したときのみ、そのオブジェクトを操作できます。
スクリプトを記述してみよう
プッシャーが作成できたので、早速スクリプトを記述していきます。まず、[Project]→[Create]→[C#Script]と選択してスクリプトを作成しましょう(図4)。Assetsフォルダに「C#」と書かれたファイルが作成されるので、名前を「PusherScript」と名付けます(図5)。
作成したPusherScriptをダブルクリックすると「MonoDevelop」というエディタが起動します。これはUnityのインストール時に同梱されているエディタで、MonoDevelopを使用してスクリプトを記述していきます(図6)。
スクリプトを開くとStart関数とUpdate関数がありますが、この2つはUnityのスクリプトを記述する上で重要な関数です。
Start関数はスクリプトが呼び出された時に、初めの1回のみ呼ばれる関数です。初期設定などはこの関数内に記述していきます。
Update関数は、毎フレーム呼び出される関数です。操作の受け付けや時間経過で動くオブジェクトはこの関数内に記述していきます。
フレームとは、ゲーム内での時間の単位を表す用語です。例えば、「ストリートファイター」などの格闘ゲームでは、1秒間当たり60枚の画像を切り替えることで映像として見せています。この場合、画像1枚当たりの表示時間は1/60秒となります。また、1秒当たり60枚の画像を切り替えるとき、60fps(framespersecond)とも表現されます。
今回は使用しませんが、Update関数とよく似た関数でFixedUpdate関数もあります。物理演算に関わる部分を記述する場合、公式ではFixedUpdate関数内に記述することが推奨されていますが、今回はUpdate関数ですべて実装していきます。
UpdateandFixedUpdate −Unity公式−
https://unity3d.com/jp/learn/tutorials/modules/beginner/scripting/update-and-xedupdate
それでは、実際にプッシャーを動かすスクリプトを書いていきましょう(リスト1)。なお、ソース内のコメントにて、どのようなコードを記述しているかを説明しています。
リスト1:プッシャーを動かすスクリプト
using UnityEngine; using System.Collections; public class PusherScript : MonoBehaviour { Vector3 initPosition; Vector3 newPosition; // Use this for initialization void Start () { /* このスクリプトを付けたゲームオブジェクト(this)の トランスフォームコンポーネント(transform)の 位置(position)をinitPositionに格納している。 */ initPosition = this.transform.position; } // Update is called once per frame void Update () { /* プッシャーが反復運動するよう、フレーム毎に位置を更新している。 ここでは、z軸方向に反復運動するようにしている。 反復運動の移動モデルはsin関数である。 */ newPosition = new Vector3 (initPosition.x, initPosition.y, initPosition.z + Mathf.Sin (Time.time)); /* Start関数内ではthis.transform.positionにてゲームオブジェクトの トランスフォームコンポーネントの情報を取得していた。 トランスフォームコンポーネント以外のコンポーネントは GetComponent()の様に取得する必要がある(Unity5から)。 */ this.GetComponent<Rigidbody>().MovePosition(newPosition); } }
コードを記述し終えたら、Unityの画面に戻ってください。そして、PusherScriptをドラッグ&ドロップでプッシャーに貼り付け、画面上部の[Play]ボタンをクリックしてみましょう。プッシャーが反復運動するようになります。このように、ゲームシーンにスクリプトを付けたゲームオブジェクトが存在すると、そのスクリプトで記述されたプログラムが実行されます。
Rigidbodyコンポーネントには、MovePositionだけでなく力を加えるAddForce関数など様々な関数があります。どのようなことができるのかは、Unityの公式ドキュメントで調べることができます。ぜひ一度、下記リンクのUnity公式スプリクトリファレンスに目を通してみてください。
Rigidbody −Unity公式スクリプトリファレンス−
http://docs.unity3d.com/ja/current/ScriptReference/Rigidbody.html
コインを落とすためのスポーナーを作る(スポーナーの移動の実装)
次に、コインを落とすためのスポーナーを作成していきます。プッシャーを作成した同じ要領で、今度はプッシャーよりも上側(Y軸正方向)に適当な3Dオブジェクトを作成して配置しましょう。筆者はCubeでスポーナーを作り、「Spawner」と名付けました。
スポーナーを作成したら、まずはSpawnerのインスペクタウィンドウにあるBox Colliderの歯車アイコンをクリックし、表示される中から[Remove Component]を選択して当たり判定をなくしてください。そして[Add Component]→[Physics]→[Rigidbody]を選択し、Rigidbodyの「Use Gravity」からチェックマークを外してください。
最後に、Rigidbodyの「Constraints」で移動や回転を制限します。ConstraintsのFreeze Position(Rotation)では、チェックマークを付けた部分は物理演算で移動(回転)しなくなります。例えば、Freeze Positionの「Z」にチェックマークを付けると、オブジェクトが物理演算でZ軸方向に移動しなくなります。
このスポーナーはX軸方向だけに移動すれば良いので、Freeze Positionの「Y」と「Z」にチェックマークを付けます。また、スポーナーは回転することもないので、Freeze Rotationの「X」「Y」「Z」すべてにチェックマークを付けましょう(図7)。
これで準備が整いました。それでは、Rigidbobyコンポーネントを使ってSpawnerをキー入力で移動させるスクリプトを記述していきます(リスト2)。PusherScriptを作成した要領で今度は「SpawnerScript」ファイルを作成し、ダブルクリックしてMonoDevelopを起動します。
リスト2:スポーナーを移動させるスクリプト
using UnityEngine; using System.Collections; public class SpawnerScript : MonoBehaviour { float moveSpeed = 2.0f; Rigidbody rb; void Start () { // スクリプトを付けたゲームオブジェクトのRigidbodyコンポーネントを取得する rb = this.GetComponent<Rigidbody>(); } void Update () { /* * Inputクラスは入力システムに関する関数が含まれている。 * GetAxisでPCの矢印キーの入力を受け付けることができる。 * "Horizontal"だと、左右の矢印キーの入力を受け付け、"Vertical"だと 上下の矢印キーの入力を受け付けるようになる。 * http://docs.unity3d.com/ja/current/ScriptReference/Input.GetAxis.html */ float x = Input.GetAxis ("Horizontal"); // キー入力された際の移動する向きを決める。 // 今回はx軸方向に移動させたい Vector3 direction = new Vector3 (x, 0, 0); // velocity(速度)に代入することによって、このオブジェクトの移動速度が決定される rb.velocity = direction * moveSpeed; } }
記述し終えたらSpawnerにSpawnerScriptを貼り付け、画面上部の[Play]ボタンをクリックしてみましょう。PCの[←][→]キーでスポーナーが動くようになりました。
コインをPrefab化して同じ物を生成する(Prefabの方法、Instantiate関数で生成)
コインプッシャーゲームでは、コインは1枚だけでなく、たくさん出てきます。そこで、最初に作成したコインを再利用したいと考えるのではないでしょうか。
Unityには、オブジェクトを再利用するために「Prefab」という重要な機能があります。コインをPrefab化することで、まったく同じ形状、同じコンポーネントを持ったコインを生成できるようになります。
Prefab化する方法は簡単です。ヒエラルキーウィンドウにあるコインをプロジェクトウィンドウ内にドラッグ&ドロップするだけです(図8)。Prefab化したあと、プロジェクトウィンドウにコインの形をしたファイルができました。これでPrefab化に成功です(図9)。
それでは、[スペース]キーを押すとコインが生成されるスクリプトを記述していきます。先ほど編集したSpawnerScriptを再度開き、追加部分を打ち込んでください(リスト3)。
リスト3:コインをPrefab化して同じ物を生成するスクリプト(追加部分のみ)
(中略) float moveSpeed = 2.0f; Rigidbody rb; /* 追加 * public で変数宣言をすると、インスペクタ上に項目が表示される。 * ここではcoin という変数がインスペクタ上で表示される。 */ public GameObject coin; /* 追加ここまで*/ void Start () { // スクリプトを付けたゲームオブジェクトのRigidbodyコンポーネントを取得する rb = this.GetComponent<Rigidbody>(); } void Update () { (中略) rb.velocity = direction * moveSpeed; /* 追加 * スペースキーが押されたときにcoinを生成する。 * 第一引数は生成するオブジェクト、第二引数は生成する場所、 第三引数は生成する角度 */ if (Input.GetKeyDown("space")) { Instantiate(coin,this.transform.position,this.transform.rotation); } /* 追加ここまで*/ } }
記述を終えたらUnityエディタに戻り、Spawnerインスペクタ内のSpawn Script(Script)を見てみましょう。「Script」の下に「Coin」という項目が追加されています。Unityのスクリプト内で宣言したpublic変数はインスペクタウィンドウに表示されます。変数の型によっては数字を打ち込めたり、ゲームオブジェクトをドラッグ&ドロップで設定することもできます。それでは、Spawner Scriptの「Coin」部分に、先ほどPrefab化したコインをドラッグ&ドロップで設定してみましょう(図10)。
これで、コインをSpawnerScriptのCoinに紐付けすることができました。ゲームを再生して[スペース]キーを押すと、スポーナーからコインが落ちてくるようになります。
スポーナーがウォールより外に行かないようにする(スクリプトで移動を制限)
まずは、プッシャーゲームに必要なウォール(壁)を土台につけていきましょう。ウォールはこれまで通りに[Hierarchy]→[Create]→[3D Object]→[Cube]から作り、土台の左右と奥に設置します(図11)。奥のウォールには「Wall」、左右のウォールにはそれぞれ「LeftWall」「RightWall」と名付けました。
次に、今の状態ではスポーナーが左右のウォールよりも外側に移動できてしまうので、ウォールの外側には移動できないようにスポーナーの移動範囲をスクリプトで制限していきます。SpawnerScriptを開き、リスト4の追加部分を打ち込みましょう。
リスト4:スポーナーの移動範囲をスクリプトで制限するスクリプト(追加部分のみ)
(中略) public GameObject coin; /* 追加 * シーンに置いたLeftWall 、RightWall をこのスクリプトで使用する。 * また、それぞれのx 座標の変数も準備しておく。 */ public GameObject leftWall; public GameObject rightWall; float leftWallPositionX; float rightWallPositionX; /* 追加ここまで*/ void Start () { // スクリプトを付けたゲームオブジェクトのRigidbodyコンポーネントを取得する rb = this.GetComponent<Rigidbody>(); // 追加:leftWall とrightWall のx 座標を取得 leftWallPositionX = leftWall.transform.position.x; rightWallPositionX = rightWall.transform.position.x; // 追加ここまで /* 追加 * scoreText内にあるScoreScriptをgetComponentで取ってくる。 * これで、ScoreScript内にてpublicで宣言した関数を利用できるようになる。 */ scoreS = scoreText.GetComponent<ScoreScript>(); } // Update is called once per frame /* 追加ここまで*/ void Update () { /* 追加 * Mathf.Clamp である変数の最小値と最大値を設定することができる。 * 第一引数は設定したい変数、第二引数は最小値、第三引数は最大値である。 * Spawner の移動できるx 座標範囲をleftWallPosition のx 座標から、rightWallPostion のx 座標の範囲にしている。 */ Vector3 currentPosition = this.transform.position; currentPosition.x = Mathf.Clamp(currentPosition.x, leftWallPositionX, rightWallPositionX); this.transform.position = currentPosition; /* 追加ここまで*/ (中略) if (Input.GetKeyDown("space")) { Instantiate(coin,this.transform.position,this.transform.rotation); } } }
リスト4では、publicな変数としてleftWall、rightWallを宣言しました。記述が終わったら、LeftWallとRightWallをヒエラルキーウィンドウからAssetsフォルダにドドラッグ&ドロップします。さらに、SpawnerインスペクタウィンドウのSpawnerScriptにLeftWallとRightWallの項目が追加されているので、設置した左右のウォールをドラッグ&ドロップで設定しましょう(図12)。
画面上部の[Play]ボタンをクリックしてみましょう。これで、スポーナーがウォールより外側に移動しなくなりました。
これで基本的なコインプッシャーゲームの仕組みは完成です。次回の後編では、ゲーム製作の総仕上げに入っていきます。