-
Notifications
You must be signed in to change notification settings - Fork 3
第01回 オブジェクトプーリング
オブジェクトプーリングは最適化の1つでオブジェクトを再利用する仕組みのことを指します。今回は大量に発射する弾を再利用することによって最適化を測りましょう。
この回はゲーム制作編第01回終了時点のプロジェクトをオブジェクトプーリングの向けに改変したものを使用します。
上記のUnityプロジェクトを開いたらStageシーンを開いてください。
図1.1: ひたすら弾を打ち続け、DestroyAreaで削除される
Bullet.cs
using UnityEngine;
public class Bullet : MonoBehaviour
{
// 弾のスピード
public int speed = 10;
void Start ()
{
// 弾の移動
rigidbody2D.velocity = transform.up.normalized * speed;
}
// 弾が何らかのトリガーに当たった時に呼び出される
void OnTriggerExit2D (Collider2D other)
{
// 弾の削除
Destroy (gameObject);
}
}
Spaceship.cs
using UnityEngine;
using System.Collections;
public class Spaceship : MonoBehaviour
{
// 弾のプレハブ
public GameObject bulletPrefab;
// 弾を撃つ間隔
public float shotDelay;
void Start ()
{
// 弾をうつ(コルーチン)
StartCoroutine (Shoot ());
}
IEnumerator Shoot ()
{
while (true) {
// shotDelay秒待つ
yield return new WaitForSeconds (shotDelay);
// 子要素を全て取得する
foreach (Transform child in transform) {
long start = System.DateTime.Now.Ticks;
// ShotPositionの位置/角度で弾を撃つ
Instantiate (bulletPrefab, child.transform.position, child.transform.rotation);
// 処理時間でInstantiateとObjectPoolを比較してみる
Debug.Log (System.DateTime.Now.Ticks - start);
}
}
}
}
ここで「コストが高い」とあるのはオブジェクトプーリングを行わない場合より高いということで、今回の弾ように「同じゲームオブジェクトを繰り返し大量に生成する」ゲームであれば考慮しなければなりませんが、その他のゲームでは考慮する必要はありません。
InstantiateやDestroyのコストが高いといっても、オブジェクトプーリングを行うコストもあるわけですから場合によってはオブジェクトプーリングを行わない場合のほうが結果的に良い場合もあります。
ObjectPool.csを作成しましょう。
ObjectPool.csでは、使用/未使用をゲームオブジェクトのアクティブ状態で判断しています。
ObjectPool.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ObjectPool : MonoBehaviour
{
private static ObjectPool _instance;
// シングルトン
public static ObjectPool instance {
get {
if (_instance == null) {
// シーン上から取得する
_instance = FindObjectOfType<ObjectPool> ();
if (_instance == null) {
// ゲームオブジェクトを作成しObjectPoolコンポーネントを追加する
_instance = new GameObject ("ObjectPool").AddComponent<ObjectPool> ();
}
}
return _instance;
}
}
// ゲームオブジェクトのDictionary
private Dictionary<int, List<GameObject>> pooledGameObjects = new Dictionary<int, List<GameObject>> ();
// ゲームオブジェクトをpooledGameObjectsから取得する。必要であれば新たに生成する
public GameObject GetGameObject (GameObject prefab, Vector2 position, Quaternion rotation)
{
// プレハブのインスタンスIDをkeyとする
int key = prefab.GetInstanceID ();
// Dictionaryにkeyが存在しなければ作成する
if (pooledGameObjects.ContainsKey (key) == false) {
pooledGameObjects.Add (key, new List<GameObject> ());
}
List<GameObject> gameObjects = pooledGameObjects [key];
GameObject go = null;
for (int i = 0; i < gameObjects.Count; i++) {
go = gameObjects [i];
// 現在非アクティブ(未使用)であれば
if (go.activeInHierarchy == false) {
// 位置を設定する
go.transform.position = position;
// 角度を設定する
go.transform.rotation = rotation;
// これから使用するのでアクティブにする
go.SetActive (true);
return go;
}
}
// 使用できるものがないので新たに生成する
go = (GameObject)Instantiate (prefab, position, rotation);
// ObjectPoolゲームオブジェクトの子要素にする
go.transform.parent = transform;
// リストに追加
gameObjects.Add (go);
return go;
}
// ゲームオブジェクトを非アクティブにする。こうすることで再利用可能状態にする
public void ReleaseGameObject (GameObject go)
{
// 非アクティブにする
go.SetActive (false);
}
}
Bullet.csにあるDestroyをObjectPool.instance.ReleaseGameObjectに、SpaceshipにあるInstantiateをObjectPool.instance.GetGameObjectに変更します。
Bullet.cs
using UnityEngine;
public class Bullet : MonoBehaviour
{
// 弾のスピード
public int speed = 10;
// 弾が表示された時に呼び出される
void OnEnable ()
{
// 弾の移動
rigidbody2D.velocity = transform.up.normalized * speed;
}
// 弾が何らかのトリガーに当たった時に呼び出される
void OnTriggerExit2D (Collider2D other)
{
// 弾の削除。実際には非アクティブにする
ObjectPool.instance.ReleaseGameObject (gameObject);
}
}
Spaceship.cs
using UnityEngine;
using System.Collections;
public class Spaceship : MonoBehaviour
{
// 弾のプレハブ
public GameObject bulletPrefab;
// 弾を撃つ間隔
public float shotDelay;
void Start ()
{
// 弾をうつ(コルーチン)
StartCoroutine (Shoot ());
}
IEnumerator Shoot ()
{
while (true) {
// shotDelay秒待つ
yield return new WaitForSeconds (shotDelay);
// 子要素を全て取得する
foreach (Transform child in transform) {
long start = System.DateTime.Now.Ticks;
// ShotPositionの位置/角度で弾を撃つ
ObjectPool.instance.GetGameObject (bulletPrefab, child.transform.position, child.transform.rotation);
// 処理時間でInstantiateとObjectPoolを比較してみる
Debug.Log (System.DateTime.Now.Ticks - start);
}
}
}
}
これでゲームを再生した時に見た目は代わりませんが、オブジェクトプーリングの実装が出来ました。
今回はここで終了です。つまずいてしまった方はプロジェクトファイルをダウンロードして新たな気持ちで次の回へ進みましょう。
Before you start this tutorials
01:Make sprite and sprite-animation
02:Move player
03:Shooting bullet from player
04:Make enemy
05:Collision Detection, Animation Event, Layer
06:Make background
07:Mechanism making of Wave type
08:Put on sounds
09:Limit player's move and some adjustment
10:Title
11:Enemy HP, bullet power, animation
12:More Waves and add Score
第01回 複数の解像度に対応する(黒帯を追加する)
第02回 複数の解像度に対応する(引き伸ばす)
第03回 タッチパネル対応
第04回 バーチャルジョイスティック対応