unity1week お題「密」にてオブジェクトプールを使いたいと思って調べたところUniRxを使うと簡単に作れることがわかりました。
なので本当に簡単です。
では作っていきましょう。
(ここではUnity2Dでバージョン2019.3.12f1で作っていきます。)
UniRxをインポート
AssetSrore UniRxでダウンロードして、Unityプロジェクトへインポート
すると、projectフォルダは以下のように「Plugins」が追加され、その中に「UniRx」というものができます。
オブジェクトプールの基本構成
オブジェクトプールは・プールされるオブジェクト(沢山つくられる物)
・プール
・マネージャー
の3つのスクリプトで構成されます。・プール
・マネージャー
なので、まずは3つ、C#スクリプトを作りましょう。
それぞれ、「PooledObjects.cs」、「Pool.cs」、「Manager.cs」としました
最もシンプルなスクリプトを作ってみる
PooledObjects.cs
これは「プールされるオブジェクト」何も書かなくて良い
using UnityEngine; public class PooledObjects : MonoBehaviour { void Start() { } void Update() { } }
Pool.cs
「プール」ObjectPoolの本体です。
using UniRx.Toolkit; using UnityEngine; public class Pool : ObjectPool<PooledObjects> { private GameObject _prefab; public Pool(GameObject prefab) { _prefab = prefab; } protected override PooledObjects CreateInstance() { return GameObject.Instantiate(_prefab).GetComponent<PooledObjects>(); } }
スクリプトの説明
1行目
using UniRx.Toolkit;オブジェクトプールの定義はUniRx.Toolkitというnamespaceにある。
4行目
public class Pool : ObjectPool<PooledObjects>{のようにObjectPool<PooledObjects>を継承する
ObjectPool<T>
定義はpublic abstract class ObjectPool<T> : IDisposable where T : UnityEngine.Component { //省略 }のようになっている。
このクラスには
protected abstract T CreateInstance();が定義されているので、
11~13行
protected override PooledObjects CreateInstance() { return GameObject.Instantiate(_prefab).GetComponent<pooledobjects<(); }を実装する。
名前の通りインスタンスを生成するメソッド。
プール内のオブジェクトが足りなくなった時に勝手に呼ばれるらしい。
ちょっと前後してしたけど5~9行目
private GameObject _prefab; public Pool(GameObject prefab) { _prefab = prefab; }コンストラクタ
たくさん生成したい物(今回ではPooledObjects)を渡して、privateフィールドに保存する
Manager.cs
マネージャースクリプト。オブジェクトプールを作って、オブジェクトの生成を行う。
using UniRx; using UniRx.Triggers; using UnityEngine; public class Manager : MonoBehaviour { public GameObject Prefab; private Pool pool; public static SubjectReturnPool = new Subject (); void Start() { pool = new Pool(Prefab); this.OnDestroyAsObservable().Subscribe(_ => pool.Dispose()); ReturnPool.Subscribe(_obj => pool.Return(_obj)).AddTo(this.gameObject); } void Update() { if (Input.GetMouseButtonDown(0)) pool.Rent(); } }
スクリプトの説明
1,2行目
using UniRx; using UniRx.Triggers;UniRx名前空間は7行目のSubjectの定義に
UniRx.Triggersは11、14~18行目のクリック入力を実装するのに必要
6行目
public GameObject Prefab;prefabにはインスペクターから量産したいGameObject(今回はPooledObjectをアタッチした物)をアウトレット接続するする
8行目
public static Subject<PooledObjects> ReturnPool = new Subject<PooledObjects>();ReturnPoolは引数にPooledObjectsをとる「関数名の宣言だけ(delegate)」みたいな物
12行目で内容を記述している
他の場所から呼び出す(後述)のでpublic static にしておく
11行目
Pool pool = new Pool(Prefab);プールをインスタンス化
Pool.csの7~9行目のコンストラクタで引数にGameObject型を要求しているので、量産したいオブジェクト(プレハブ)を渡す。
12行目
this.OnDestroyAsObservable().Subscribe(_ => pool.Dispose());PoolはMonoBehaviourを継承していないので、このゲームオブジェクトが破棄(Destroy)されても、そのインスタンスが残ってしまう。
なので、このゲームオブジェクトが破棄された時にDispose()を呼んで破棄するようにする。
13行目
ReturnPool.Subscribe(_obj => pool.Return(_obj)).AddTo(this.gameObject);7行目で宣言したReturnPoolの内容を追加(登録)する。
_objは渡されたインスタンス
これをpool.Return()することで、そのインスタンス(_obj)がSetActive(false)される。
これもUniRxのクラスなので、11行目と同様にこのゲームオブジェクトが破棄(Destroy)されても、ReturnPoolが残ってしまう。
なので最後にAddTo()して、このゲームオブジェクトが破棄されたら一緒に破棄されるようにする。
15~17行目
void Update() { if (Input.GetMouseButtonDown(0)) pool.Rent(); }左クリックの時、pool.Rent()を呼んでオブジェクトプールから1つインスタンスを取り出す(SetActive(true))。
インスタンスが無い時はInstantiate()される。
Unityで実行する
量産するものを作り、PooledObject.csをアタッチマネージャーを作成してManager.csをアタッチ
実行して、左クリックすると、このように増えていきます。
まだこの段階では生成したオブジェクトをオブジェクトプールに戻すことができません。
では、生成後1秒経過すると消えるようにしてみます。
今まで何も書いていなかったPooledObjects.csを以下のように変更します。
この時、Start()を使うのではなく、OnEnable()を使ういます。
PooledObjects.cs
using UnityEngine; public class PooledObjects : MonoBehaviour { void OnEnable() { Invoke("destroy", 1); } void destroy() { Manager.ReturnPool.OnNext(this); } }これで、以下のようにオブジェクトプールが完成しました。
このようにプールから取り出した後に実行したい(開始したい)処理はOnEnable()に書くのが良いのかなと思います。
実際に使っていく
ここまでで作ったオブジェクトプールを実際のゲーム作成で使うには機能が足りないと思います。なので、Awake()、Start()、Update()に加えてOnEneble()に追加記述していくのが基本になるかと思います。
最後に参考記事等を紹介して終わります。
このページ(UniRx付属のObjectPoolが割とガチで使える件)では
Poolを空にする
あらかじめObjectを作成しておく
現在のプール数を一定まで減らす
などのUniRxのObjectPool付属の機能を紹介してくれています。
Poolを空にする
あらかじめObjectを作成しておく
現在のプール数を一定まで減らす
などのUniRxのObjectPool付属の機能を紹介してくれています。
また、このQiita(UniRxのObjectPoolを利用する)ではエフェクト作成を例にプログラム全体を書いてくれています。
このQiitaでのオブジェクtプール本体(ここでのPool.csこと)ではコンストラクタの引数にTransform型も加えてとり、「ヒエラルキーが散らからないように一箇所にまとめる」ということも行っています。
このQiitaでのオブジェクtプール本体(ここでのPool.csこと)ではコンストラクタの引数にTransform型も加えてとり、「ヒエラルキーが散らからないように一箇所にまとめる」ということも行っています。
私自身はunity1week「密」にて使いました。
その時のスクリプトの話をすると、
・マネージャースクリプトには入力処理などのほか量産したインスタンスをList<T>で管理などする
・量産するオブジェクトのスクリプトはAwake()にObservableを定義してそのオブジェクトのイベント処理を書き、OnEnable()に初期化処理的なことを書きいた
・オブジェクトプール本体は今回紹介したPool.csと全く同じ
その時のスクリプトの話をすると、
・マネージャースクリプトには入力処理などのほか量産したインスタンスをList<T>で管理などする
・量産するオブジェクトのスクリプトはAwake()にObservableを定義してそのオブジェクトのイベント処理を書き、OnEnable()に初期化処理的なことを書きいた
・オブジェクトプール本体は今回紹介したPool.csと全く同じ
0 件のコメント:
コメントを投稿