2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

配列やリストを使わない MonoBehaviour プール

Last updated at Posted at 2024-10-30

👇 コチラの Unity MonoBehaviour 対応版。

つかいかた

Unity の事情に合わせたので少し特殊。メインスレッドからしか呼べません。

※ 必要な GameObjectRent すると勝手に作られます。

public class MyBehaviour : PoolableBehaviour<MyBehaviour>  // 👈
{
    // オーバーライドの必要ナシ。継承すれば動く
}

// 貸出時に GameObject を有効化するか選ぶ。false なら Awake, OnEnable, Start は実行されない
var component = MyBehaviour.Rent(activateGameObject: false, parent: null);
component.InitializeBeforeActivate();
component.gameObject.SetActive(true);

// 任意のタイミングで返却。または CancellationToken への紐づけ
component.ReturnToPool();
component.ReturnToPoolIfCanceled(otherComponent.destroyCancellationToken);

// いわゆる Warmup メソッド。インスタンス化だけでなく Awake 等も事前に実行しておきたいならパラメーターで指定する
MyBehaviour.Populate(count: 10, true);

// プールに残す未使用インスタンス数を指定して溢れたものは削除する(OnDestroy が実行される)
// ※ 貸し出し中のインスタンスは数に含まれない
MyBehaviour.TrimExcess(3);

メニューコマンド

Unity エディター > DEBUG > Poolable Behaviour から各種テストが可能。

TODO/各種仕様

プレハブをプーリングしたい

PoolableBehaviour を継承し「Awake メソッドでプレハブをインスタンス化して抱える」コンポーネントとして実装すれば実現できると思います。

※ プールに残っている未使用インスタンスを探して存在しなかったらプレハブから持ってくる、という実装は必要ありません。どのようなリソースもプーリング可能な MonoBehaviour に管理を委ねれば間接的にプールの管理下に入ることになります。

プレハブに PoolableBehaviour が含まれる場合

貸し出された Poolable インスタンスは完全にプールの管理下から外れます。これは AddComponent メソッドで追加したり、Unity エディターのインスペクターでマウスを使ってアタッチしたときと同じ状態です。

プレハブに含まれる Poolable は「貸し出されてプール管理下から外れた」状態のインスタンスです。なので、何もしなければ普通のコンポーネントと同じ振る舞いをします。

Poolable のインスタンス再利用に必要なのは「ReturnToPool を実行すること」です。実行しなかった場合は普通のコンポーネントと同じ挙動を示します。なので、親 Transform やシーンが削除されれば巻き込まれて消えます。

※ インスタンスをプールに返却し損ねても特にペナルティーはありません。

プール返却時の階層構造

  • PoolableA
    • PoolableB
      • PoolableC
    • NonPoolableComponent

このような階層構造で PoolableA.ReturnToPool() を実行すると、B C もプールに返却されます。つまり、階層構造が維持されずフラットになります。ただし Poolable ではない NonPoolableComponentA の階層に残ります。

貸出時の挙動をカスタマイズしたい

Rent メソッドにオーバーロードを追加します。シグネチャが同じなら new します。

// 引数無しで常に良い感じにするメソッドに出来ないならカスタマイズしなくて良い
public static MyBehaviour Rent()
{
    var result = Rent(false, parent: appropriateTransform);
    result.AwesomeSetup();
    result.gameObject.SetActive(true);
    return result;
}

// new 宣言を Obsolete して戻り値の型を変えておけば元のオーバーロードが間違って使われるのを防げる
// Rent() 内では継承元のメソッドを直接呼んで迂回する → PoolableBehaviour<MyBehaviour>.Rent(true);
[Obsolete("Use Rent() instead", true)]
new public static void Rent(bool x, Transform? y = null, bool z = true)
    => throw new NotImplementedException();

返却時/削除時の挙動をカスタマイズしたい

OnDisable OnDestroy を実装します。別の方法でカスタマイズした場合、親 Transform やシーンに巻き込まれて削除された時の動作を保証できません。

Rent を使わずに Poolable を GameObject にアタッチした場合

多分動きます。

複数の PoolableBehaviour をひとつの GameObject にアタッチした場合

動く気がします。

ソースコード

--

サクッと作れるかと思ったら意外と大変でした。

以上です。お疲れ様でした。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?