今回はUnityのゲームで床のゲームオブジェクトを前方に生成し、その床を使って進むキャラクターを作成したいと思います。
何もない空間に床を積み上げて階段を作り、目的地に進んでいくようなゲームを作成する時に役立つと思います。
今回の機能を作成すると、
↑のような感じで自分で床を生成して進めることが出来ます。
作り方概要
今回の機能は床に対応するキーを押した時にキャラクターの前方に床のプレハブをインスタンス化します。
床のゲームオブジェクトにはRigidbodyを取り付け重力で落下するようにします。
床の大きさの違いによっては主人公キャラクターの上に落ちてきてしまう可能性もあるので、床ごとにどの位置から落下するかを保持するスクリプトを持たせ落下位置を変更出来るようにします。
プレハブのインスタンス化のやり方がわかっていればそれほど難しい処理をするわけではありません(オブジェクトの向きに関してはちょっと難しいかも?)。
プレハブに関しては以下の記事を参照してください。
使用するゲームオブジェクトの準備
機能を作成する前に使用するゲームオブジェクトを準備します。
キャラクターの準備
まずは床を生成するキャラクターを準備します。
キャラクターにはスタンダードアセットのEthanを用いて、CharacterControllerの取り付けコライダのサイズの調整をし、AnimatorControllerでIdle状態とWalk状態を作成しAnimatorに設定します。
ここら辺は
を参照してください。
床のゲームオブジェクトの準備
次はキーを押した時にインスタンス化する床のゲームオブジェクトのプレハブを作成していきます。
使用する床のゲームオブジェクトはなんでもいいですが、今回はCubeのScaleを調整した床と、スタンダードアセットのPrototypingにあるゲームオブジェクトを使用してみます。
今回は4つの床オブジェクトのプレハブを用意し、対応するキーを押した時にその床を生成する事にします。
ヒエラルキー上に床のゲームオブジェクトを配置した後、それをAssetsエリアにドラッグ&ドロップしプレハブにします。
床のゲームオブジェクトのTransformのPositionは全て0にし、Rotationは必要に応じて変更します。
これはインスタンス化した時にそのPositionとRotationで登場させそのまま落下させる為です。
プレハブにしたゲームオブジェクトはヒエラルキー上から削除します。
作成した床のプレハブは
↑のような感じで4つ作成しました。
間にマテリアルが入って邪魔だなぁ・・・・(-_-)
機能を作成する
使用するゲームオブジェクトの作成が出来たので、キャラクターのスクリプト、床のスクリプト、コンポーネントを取り付け機能を作成していきます。
床のプレハブにコンポーネントを取り付け
床のプレハブは4つ作成しましたが、全部同じように作成し取り付けてください。
まずはFloor1の床を選択し、インスペクタのAdd Component→Physics→Rigidbodyを選択し取り付けます。
床はRigidbodyの重力を使って落下させるのでUse Gravityにチェックを入れます。
また、床のゲームオブジェクトが落下する際に回転すると他のゲームオブジェクトと接触した際に回転してしまい床の意味を持たなくなってしまうので回転しないようにConstraintsにチェックを入れます。
これで床は登場してから重力で落下しますが、回転をしないようになります。
次にFloorScriptスクリプトとFloorDataスクリプトを作成しFloor1プレハブに取り付けます。
FloorScriptスクリプトの作成
FloorScriptスクリプトはその床が登場してからずっと残っていると邪魔だったり処理速度が遅くなってしまうので、一定時間が経過したら削除するものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class FloorScript : MonoBehaviour { // フロアを消すまでの時間 [SerializeField] private float deleteTime = 5f; // 経過時間 private float elapsedTime; // Update is called once per frame void Update () { elapsedTime += Time.deltaTime; if(elapsedTime >= deleteTime) { Destroy(this.gameObject); } } } |
登場してから5秒が経過したら自身のゲームオブジェクトを削除しています。
FloorDataスクリプト
FloorDataスクリプトは床をキャラクターからどの程度相対的な位置に登場させるかのデータを保持しておくものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class FloorData : MonoBehaviour { // キャラクターのどれだけ前方に登場させるか [SerializeField] private float fowardDistance; // キャラクターのどれだけ上方に登場させるか [SerializeField] private float upDistance; public float GetForwardDistance() { return fowardDistance; } public float GetUpDistance() { return upDistance; } } |
床のプレハブを選択しインスペクタでfowardDistance(前方距離)、upDistance(上方距離)を指定します。
これで床のプレハブに取り付けるスクリプトの作成が終了しました。
Floor1~Floor4まで同じようにRigidbodyの取り付けとスクリプトの取り付けをし、床の大きさに応じてインスペクタのFloorDataのfowardDistanceとupDistanceの値を設定します。
キャラクター用スクリプトの作成
次にキャラクターの移動と床を生成をするスクリプトを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class CreateFloorChara : MonoBehaviour { private CharacterController characterController; private Animator animator; private Vector3 velocity = Vector3.zero; [SerializeField] private float walkSpeed = 2f; // フロアを作成する時に押すキー名 [SerializeField] private string[] floorKey = { "1", "2", "3", "p" }; // 作成するフロアのプレハブ [SerializeField] private GameObject[] floorPrefab; // キャラクターのどれだけ前にフロアを作成するか [SerializeField] private float forwardDistance = 2f; // キャラクターのどれだけ上にフロアを作成するか [SerializeField] private float upDistance = 2f; // Use this for initialization void Start () { characterController = GetComponent<CharacterController>(); animator = GetComponent<Animator>(); } // Update is called once per frame void Update () { if(characterController.isGrounded) { velocity = Vector3.zero; var input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); if(input.magnitude > 0f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity = input.normalized * walkSpeed; } else { animator.SetFloat("Speed", 0f); } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); // 何かのキーを押した時だけ実行 if (Input.anyKeyDown) { // フロア作成キーを押したかどうかの判定 for (int i = 0; i < floorKey.Length; i++) { if (Input.GetKeyDown(floorKey[i])) { CreateFloor(i); } } } } // 指定したフロアを作成する処理 private void CreateFloor(int index) { // 指定したフロアプレハブの配列が作成されており、かつフロアプレハブが設定されている時 if (index < floorPrefab.Length && floorPrefab[index] != null) { // それぞれのフロアの登場先データを取得する var floorData = floorPrefab[index].GetComponent<FloorData>(); // フロアを登場させる位置を計算 var floorPos = transform.position + transform.forward * floorData.GetForwardDistance() + transform.up * floorData.GetUpDistance(); // インスタンス化するゲームオブジェクトはキャラクターの向いている向きに元のゲームオブジェクトの向きをかけて角度を計算 Instantiate(floorPrefab[index], floorPos, transform.rotation * floorPrefab[index].transform.rotation); } } } |
Floor1~Floor4までに対応するキーの文字列をfloorKeyとして配列で持っておきます。
今回は1、2、3、pのキーを押した時にそれぞれ対応する床のゲームオブジェクトを生成することにします。
floorPrefabにはインスペクタで床のプレハブを指定します。
1 2 3 4 5 6 7 8 9 10 11 | // 何かのキーを押した時だけ実行 if (Input.anyKeyDown) { // フロア作成キーを押したかどうかの判定 for (int i = 0; i < floorKey.Length; i++) { if (Input.GetKeyDown(floorKey[i])) { CreateFloor(i); } } } |
何らかのキーを押したらfloorKey配列に保持しているキーが押されたかどうか判定し、押されていたらCreateFloorメソッドに対応する番号を渡して呼び出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 指定したフロアを作成する処理 private void CreateFloor(int index) { // 指定したフロアプレハブの配列が作成されており、かつフロアプレハブが設定されている時 if (index < floorPrefab.Length && floorPrefab[index] != null) { // それぞれのフロアの登場先データを取得する var floorData = floorPrefab[index].GetComponent<FloorData>(); // フロアを登場させる位置を計算 var floorPos = transform.position + transform.forward * floorData.GetForwardDistance() + transform.up * floorData.GetUpDistance(); // インスタンス化するゲームオブジェクトはキャラクターの向いている向きに元のゲームオブジェクトの向きをかけて角度を計算 Instantiate(floorPrefab[index], floorPos, transform.rotation * floorPrefab[index].transform.rotation); } } |
CreateFloorメソッドでは受け取った床番号に対応するプレハブが設定されていたらその床のプレハブに取り付けてあるFloorDataスクリプトを取得します。
取得したデータから床を登場させる位置を計算します。
床をインスタンス化する時の位置は先ほど計算した位置で床の回転はtransform.rotation(この場合キャラクターの角度)に床の角度をかけたものを指定します。
こうすることでキャラクターの位置を基点に床の角度分回転させるので、キャラクターの向いている向きにかかわらず床の向きが一定になります。
これで機能が完成しました!
終わりに
今回作成した機能は床のゲームオブジェクトを使って通れない位置を通る時に使いますが、単純に色々なブロック生成を出来るようにして遊ぶだけのゲーム(おもちゃ?)とかも作れそうですね。
ただ単に普段は通れないけどキーアイテムを取得したら通れるような床を作りたい場合は
↑の記事で作った透明な床の機能を使った方がいいです。
今回の機能は自由に床を生成したい時等に使えると思います。