シューティングゲームなどで多くのオブジェクト生成を繰り返してしまうと、
CPUの処理能力に負荷をかけてしまします。
そこで今回は最近Unityに標準搭載されたオブジェクトプールを使用してシューティングゲームの一部を作成し
処理負荷を軽減してみたいと思います。
過去にブログで紹介した
・スムーズに移動させる方法
【Unity】プレイヤー、オブジェクトをスムーズに移動させる方法 「transform.Translate」の紹介 - はとの豆知識
・移動範囲を制限する方法
【Unity】プレイヤー、オブジェクトの移動範囲を制限させる方法「Mathf.Clamp」の紹介 - はとの豆知識
・画面外に弾が出たら非表示
【Unity】画面外に出た、カメラから見えなくなったオブジェクトを検知する方法。 - はとの豆知識
これらを組み合わせて
作成します。
手っ取り早い実装方法
少し見えづらいですが、右上の弾のオブジェクトが表示になったり非表示になったりして
オブジェクトを再利用できていますね。
下のコードをプレイヤーに張り付けることで
このような挙動ができます。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Pool; /*これが必要*/ public class Player : MonoBehaviour { [SerializeField, Header("弾オブジェクト")] GameObject bullet; public IObjectPool<GameObject> bulletPool; //移動制限処理用変数 private Vector2 playerPos; private readonly float playerPosXClamp = 5.0f; private readonly float playerPosYClamp = 5.0f; //移動速度 private readonly float moveSpeed = 5f; private void Update() { //移動系 if (Input.GetKey(KeyCode.UpArrow)) { this.Moving(Vector2.up); } if (Input.GetKey(KeyCode.DownArrow)) { this.Moving(Vector2.down); } if (Input.GetKey(KeyCode.LeftArrow)) { this.Moving(Vector2.left); } if (Input.GetKey(KeyCode.RightArrow)) { this.Moving(Vector2.right); } //弾を発射 if (Input.GetKeyDown(KeyCode.Space)) { this.Shooting(); } } /*プレイヤーを動かす*/ private void Moving(Vector2 vector) { //引数で受け取った方向に移動する transform.Translate(vector * this.moveSpeed * Time.deltaTime); /*ここから下は移動制限の処理です*/ //変数に自分の今の位置を入れる this.playerPos = transform.position; //playerPos変数のxとyに制限した値を入れる //playerPos.xという値を-playerPosXClamp~playerPosXClampの間に収める this.playerPos.x = Mathf.Clamp(this.playerPos.x, -this.playerPosXClamp, this.playerPosXClamp); this.playerPos.y = Mathf.Clamp(this.playerPos.y, -this.playerPosYClamp, this.playerPosYClamp); transform.position = new Vector2(this.playerPos.x, this.playerPos.y); } /*弾を発射*/ private void Shooting() { this.Pool.Get();//オブジェクトプールの処理を呼び出す } /*オブジェクトプール*/ public IObjectPool<GameObject> Pool { get { if (this.bulletPool == null) { bool collectionChecks = true; int maxSize = 10; /* CreatePooledItem :弾を生成するとき OnTakeFromPool :生成せずプールしたものを表示するとき OnReturnedToPool :弾がプールに返却されるとき OnDestroyPoolObject:プールできる最大値に達したとき collectionChecks :Releaseの際すでにpoolにあるかチェック maxSize :プールできる最大数 */ this.bulletPool = new ObjectPool<GameObject>(this.CreatePooledItem, this.OnTakeFromPool, this.OnReturnedToPool, this.OnDestroyPoolObject, collectionChecks, maxSize); } return this.bulletPool; } } /*プールしたいオブジェクトを生成する処理*/ private GameObject CreatePooledItem() { //弾のオブジェクトを,プレイヤーと同じ位置,プレイヤーと同じ回転 で生成する。 GameObject bullet = Instantiate(this.bullet, transform.position, transform.rotation); //生成した弾オブジェクトに弾用のコンポーネントを付ける var returnToPool = bullet.AddComponent<BulletComponent>(); returnToPool.pool = this.Pool; return bullet; } /*弾がプールに返却されるときに呼び出される*/ //NOTE:BulletComponentのOnBecameInvisible()から呼び出しています void OnReturnedToPool(GameObject bullet) { bullet.SetActive(false); } /*弾がプールから取得されるとき呼び出される*/ void OnTakeFromPool(GameObject bullet) { //非表示になった位置で止まっているのでプレイヤーの位置に戻す bullet.transform.position = transform.position; bullet.SetActive(true); } /*プールできる最大値に達したときに呼び出される*/ void OnDestroyPoolObject(GameObject bullet) { Destroy(bullet); } } /*これを発射する弾オブジェクトに付けます CreatePooledItem()のタイミングで付けてます。*/ [RequireComponent(typeof(GameObject))] public class BulletComponent : MonoBehaviour { private GameObject bullet; public IObjectPool<GameObject> pool; //移動速度 private readonly float moveSpeed = 10f; private void Start() { //自分をセット this.bullet = this.gameObject; } private void Update() { //自分が表示されているなら if (gameObject.activeSelf) { //上に移動する transform.Translate(Vector3.up * this.moveSpeed * Time.deltaTime); } } //画面外に出たら呼ばれます private void OnBecameInvisible() { //プールに返却する this.pool.Release(this.bullet); } }