2014年12月5日金曜日

大量のEntityをGenericPoolを使って再利用する

シューティングゲームの弾幕や、アクションゲームで敵を倒した際に敵に重なるように表示される得点のような、「表示する機会は多いけど、すぐに画面から消えるオブジェクト」を考えます。

Andengineでは画像を表示するならSprite、テキストならTextクラスのオブジェクトを生成します。

ですが、オブジェクトの生成は重くなるので、大量のオブジェクトを「必要になったら生成。不必要になったら削除。」といった処理で利用しているとFPSが落ちてしまいます。

このようなオブジェクトはAndengineのGenericPoolを使って再利用することで処理を軽くすることができます。

GenericPoolのイメージ

「Pool」とは泳ぐプールの他に「蓄える」といった意味があります。つまりGenericPoolはオブジェクトを蓄えておくのに利用するクラスということです。

ここでは、Spriteをシューティングゲームの弾として使っている場合を考えます。
シューティングゲームの弾は画面上に沢山現れますが、その全てはすぐに画面外に出て行ったり、キャラクターに当たったりして画面から消えます。
このようなゲームでは弾のSpriteをGenericPoolで再利用する場合、以下のような処理になります。

  1. 弾を画面上から消す際に、そのSpriteのオブジェクトを削除せずに、GenericPoolに格納する。
  2. 次に、新しいが必要になった際に、新しいSpriteオブジェクトを生成せずに、GenericPoolに格納されているSpriteを取り出す。
  3. GenericPoolが空の時に弾が必要な場合は、GenericPoolにSpriteオブジェクトを作らせる。
ここで、プールに格納することをRecycle。プールから取り出すことをObtain。新しくオブジェクトを作ることをAllocateといいます。

GenericPoolでの弾の再利用のイメージ

GenericPoolの使い方

GenericPoolの準備
このくらいの処理なら、弾を管理するクラスを自作してしまっても良いのですが、「Generic」の名の通り、Sprite以外のあらゆるクラスのオブジェクトの再利用に使えます(ただし1つのプールには1種類のクラスのみ)。プログラムで書く必要があるのは
  1. プールに格納する時にオブジェクトに施す処理
  2. プールから取り出すときにオブジェクトに施す処理
  3. オブジェクトを生成する時にオブジェクトに施す処理
だけです。弾を蓄えるbulletPoolというオブジェクトを作ってみます。コードは以下のような感じ。


GenericPoolを継承した新たなクラスを作らなくても、3つのメソッドをオーバーライドすれば良いです。また、batchAllocatePoolItemsで指定した個数のオブジェクトをプールに格納することができます。
オブジェクトの取り出し、格納
プールしたオブジェクトを得たい場合は準備したプールに対してobtainPoolItem()をします。 オブジェクトを格納したい場合はプールに対してrecyclePoolItem()をします。以下のような感じ。

まとめ

格納、取り出し、生成をオーバーライドすればプールを簡単に用意できます。利用も簡単。

注意すべき点は、再利用前のオブジェクトの状態が残ってしまわないようにすることです。
リサイクルするオブジェクトは、再び利用されるまでにonHandleRecycleItem()とonHandleObtainItem()の処理を受けます。新たに生成されるオブジェクトは利用されるまでにonHandleObtainItem()の処理を受けます。
これらのメソッドを使って、再利用するオブジェクトをしっかり新品同様にするように注意しましょう。
逆に、何もせずとも新品同様ならonAllocatePoolItem()のオーバーライドのみでよいです。

また、頻繁に同じオブジェクトを生成、削除する場合はEntityに限らずModifierとかでも応用可能です。

参考リンク

0 件のコメント:

コメントを投稿