前回まででステージを作れるようになりましたが、まだ「大量のコインをどう配置するか?」という問題が残っています。もちろん一つ一つ手作業で配置してもいいのですが…それってものすごく面倒ですよね?そこで今回はコインを自動的に配置するスクリプトを作っていきます。
コインを自動的に配置するための考え方
それで今回のゲームでコインを自動的にステージに配置するためには、クリアすべき大きな条件が1つあります。それは「コインは取得可能な場所に配置する必要がある」ということです。したがって通路上にのみ配置されるべきで、ステージの外や壁の上などに生成されないようにしなければなりません。
そこで今回はUnityのRayCastという機能を使って、この条件をクリアできるような仕組みを作ることにしました。
RayCastについて
まずはじめにRayCastについてですが、これは「ある点から透明な光線(Ray)を飛ばして、それが当たったコライダーを検知する」ための機能です。例えばキャラクターの向いている方向に何かあるかを調べたいときなどに使うことが多いと思います。詳細を知りたい方はググってください。
考え方の基本:Rayを上空から床に向けて飛ばし、何があるかを調べる
さて、それでRayを使ってどうやって配置場所を決めるかということですが…今回私は「Rayを上空から床に向けて飛ばして、その先に何があるかを調べる」方法を考えました。言葉だけだとピンとこないと思うので次の図をご覧ください。
やることはシンプルで、上空から一定の間隔で床に向かってRayを飛ばし、Rayが当たったのが通路ならコインの配置場所として登録するという方法です。これなら難しいスクリプトを組まなくても簡単に実装できます。
コインを自動配置するC#スクリプト
では考え方が分かったところで、それをスクリプトに落とし込んでいきましょう。空のゲームオブジェクトを作り、名前を「CoinSpawner」にしたらPositionを(0,10,0)に設定して次のスクリプトをアタッチしてください。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CoinSpawner : MonoBehaviour { [SerializeField] GameObject coinPrefab; [SerializeField] float placementInterval = 4.0f; [SerializeField] float coinPositionY = 0.5f; [SerializeField] float coinRotationZ = 90; int numOfCoins = 0; GameObject floorObj; Vector3 floorSize; Ray ray; RaycastHit rayCastHit; Vector3 rayPosition; List<Vector3> positions = new List<Vector3>(); GameManager gameManager; void Start() { floorObj = GameObject.FindGameObjectWithTag("Floor"); GetFloorSize(); SetSpawnPosition(); CreateCoins(); gameManager = GameObject.FindGameObjectWithTag("GameController").GetComponent<GameManager>(); gameManager.NumOfCoins = numOfCoins; } void GetFloorSize() { Renderer floorRenderer = floorObj.GetComponent<Renderer>(); floorSize = new Vector3(floorRenderer.bounds.size.x, floorRenderer.bounds.size.y ,floorRenderer.bounds.size.z); } //コイン生成位置を決定するメソッド //このスクリプトをアタッチしたゲームオブジェクトと床が両方とも原点にあることが前提なので注意 void SetSpawnPosition() { //床の左上から決めていく float x = -floorSize.x / 2; float z = -floorSize.z / 2; //縦方向のループ for(int i = 0; i < Mathf.RoundToInt(floorSize.z / placementInterval); i++) { x = -floorSize.x / 2; for(int j = 0; j < Mathf.RoundToInt(floorSize.x / placementInterval); j++) { ray = new Ray(new Vector3(x, transform.position.y, z), transform.TransformDirection(Vector3.down)); if (Physics.Raycast(ray, out rayCastHit, transform.position.y)) { //Rayが当たったのが床か道なら、コインを生成する座標として登録 if(rayCastHit.collider.tag == "Floor" || rayCastHit.collider.tag == "Road") { positions.Add(rayCastHit.point); } } x += placementInterval; } z += placementInterval; } numOfCoins = positions.Count; } void CreateCoins() { foreach(Vector3 position in positions) { //Instantiateの時はプレハブのTransformが無効になるので、必要な部分に手を加えている Instantiate(coinPrefab, new Vector3(position.x, position.y + coinPositionY, position.z), Quaternion.Euler(0, 0, coinRotationZ)); } } }
これでコインが通路上に自動生成されるようになりました。
次のページ→カメラへのエフェクトの追加方法