キューを使ったプール
CoddyのUnityジャーニー「Unityのゲームアーキテクチャ」セクションの一部。レッスン 47/64。
プールには予備のオブジェクトを保管する場所が必要です。C#にはそのためのコレクション、Queue<T>があります。Enqueueは項目を末尾に追加し、Dequeueは先頭の項目を取り出し、Countは何個待っているかを示します。この水槽では0.3秒ごとに泡が1つ上がり、それぞれの泡は水面でプールに戻ります。
using System.Collections.Generic;
using UnityEngine;
public class BubblePool : MonoBehaviour
{
public GameObject bubblePrefab;
private Queue<GameObject> pool = new Queue<GameObject>();
private float timer;
void Update()
{
timer += Time.deltaTime;
if (timer >= 0.3f)
{
timer = 0f;
Get(new Vector3(Random.Range(-2f, 2f), 0f, 0f));
}
}
public GameObject Get(Vector3 position)
{
GameObject bubble;
if (pool.Count > 0)
{
bubble = pool.Dequeue();
}
else
{
bubble = Instantiate(bubblePrefab);
bubble.GetComponent<Bubble>().pool = this;
}
bubble.transform.position = position;
bubble.SetActive(true);
return bubble;
}
public void Return(GameObject bubble)
{
bubble.SetActive(false);
pool.Enqueue(bubble);
}
}// Bubble.cs、bubble prefab に設定
public BubblePool pool;
void Update()
{
transform.Translate(0f, 2f * Time.deltaTime, 0f);
if (transform.position.y > 5f) pool.Return(gameObject);
}GetはまずCountを確認します。空のキューに対してDequeueを呼び出すとInvalidOperationExceptionが発生するためです。その後でのみInstantiateを行い、新しく作られた泡にだけpoolフィールドを設定します。再利用する泡には、すでに設定されています。キューは、最も長く待っていた泡から順に取り出します。先入れ先出しです。水面に達した泡はDestroyではなくReturnを呼び出し、シーン内に非アクティブな状態でとどまり、次にGetが呼ばれるのを待ちます。
GetはSetActive(true)を呼び出す前に泡を配置します。オブジェクトを有効にするとOnEnableがすぐに実行され、その中の処理では、泡が最後に消えたときの位置ではなく、新しい位置が参照されるべきだからです。再利用されたオブジェクトが最初から動作を始めるのもこのタイミングです。AwakeとStartは泡が作られたときに一度だけ実行され、OnEnableはGetのたびに実行されます。
チャレンジ
中級足跡を破棄する代わりに、プールして再利用しましょう。
Trailでは、予備をQueue<GameObject>に保持します。Return(footprint)で足跡を無効にしてキューに追加し、Startでprewarm個の足跡を作成して返します。Get(position)で予備をキューから取り出す(または新しく作成する)と、配置して有効にします。Footprintでは、Destroyを置き換えます。OnEnableで経過時間をリセットし、life秒後にtrail.Return(gameObject)を呼び出します。
「プレイ」を押して歩きましょう。足跡は1.5秒後に消えます。その後、「確認」を押します。
自分で試してみよう
using System.Collections.Generic;
using UnityEngine;
public class Trail : MonoBehaviour
{
public GameObject footprintPrefab;
public float step = 1f;
public int prewarm = 6;
private Vector3 lastDrop;
void Start()
{
lastDrop = transform.position;
// プールに事前生成した足跡を入れる
}
void Update()
{
if (Vector3.Distance(transform.position, lastDrop) >= step)
{
lastDrop = transform.position;
Get(new Vector3(transform.position.x, 0.02f, transform.position.z));
}
}
public GameObject Get(Vector3 position)
{
GameObject footprint = Instantiate(footprintPrefab, position, Quaternion.identity);
footprint.GetComponent<Footprint>().trail = this;
return footprint;
}
public void Return(GameObject footprint)
{
// 予備の足跡はプール内で待機する
}
}
Unityのゲームアーキテクチャのすべてのレッスン
自分で練習してみよう: Unityプレイグラウンド