サイトのトップへ戻る

libGDX ドキュメント 日本語訳

サイト内検索

メモリ管理

ゲームはリソースを大量に消費するアプリケーションです。画像と効果音でRAM内のかなりの領域を占有します。 また、これらリソースのほとんどはJava ガーベージコレクターでは管理されません。 代わりにネイティブドライバーで管理されます。 VRAM上から5MBのTextureを解放するタイミングをガーベージコレクターに決定させるというのは、あまり良い考えではありません。

ここではリソースの寿命を細かく制御します。 libgdx では、そうしたリソースを表すいくつかのクラスがあります。 それらリソースは全て、 破棄用のインタフェースを実装しています。これは、不要になった場合にはこのクラスのインスタンスを破棄する必要があるということです。 リソースの破棄を怠ると、深刻なメモリリークが発生する可能性があります!

以下のクラスは、手動での破棄が必要です (他にまだあるかもしれません):

  • AssetManager
  • Bitmap
  • BitmapFont
  • BitmapFontCache
  • CameraGroupStrategy
  • DecalBatch
  • ETC1Data
  • FrameBuffer
  • Mesh
  • Model
  • ModelBatch
  • ParticleEffect
  • Pixmap
  • PixmapPacker
  • Shader
  • ShaderProgram
  • Shape
  • Skin
  • SpriteBatch
  • SpriteCache
  • Stage
  • Texture
  • TextureAtlas
  • TileAtlas
  • TileMapRenderer
  • com.badlogic.gdx.physics.box2d.World
  • all bullet classes

リソースに関連付けられたメモリを解放するため、リソースが不要になったらすぐに破棄する必要があります。 破棄したリソースにアクセスすると未定義のエラーが発生するので、破棄したリソースへの全ての参照をクリアするようにしてください。

特定のクラスについてそれが破棄する必要があるのかどうか不明な場合は、そのクラスが disposed()メソッドを持っているかを確認してください。 持っていた場合、それは破棄が必要なネイティブリソースになります。



オブジェクトのプール処理

プール処理とは、毎回新規にオブジェクトを作成するのではなく、使われていないオブジェクトや"死んだ状態"のオブジェクトを再利用する原理です。 オブジェクトプールを作成して、新規オブジェクトが必要になった場合にプールからオブジェクトを取得することでこれが行えます。 プールに使用可能な(解放状態の)オブジェクトがある場合はそれが戻り値として返され、プールが空の場合や解放状態のオブジェクトがない場合は、オブジェクトの新規インスタンスが作成されて戻り値として返されます。 オブジェクトが必要なくなった場合は、それを"解放"します。そうすることで、プール内で再利用できるようになります。 これでオブジェクトに割り当てられたメモリが再利用され、ガーベッジコレクタは大助かりです。

弾丸や障害物やモンスターのようなオブジェクトを頻繁に生成するゲームでは、メモリ管理のためにこれが重要になります。

Libgdx では簡単にプール処理をするためのいくつかのツールが用意されています。

Poolable インタフェースを実装したオブジェクトには reset()メソッドが追加されます。 このreset()メソッドは、オブジェクトを解放した時に自動的に呼び出されます。

以下は、bullet オブジェクトをプール処理する簡単な例です。

public class Bullet implements Poolable {

    public Vector2 position;
    public boolean alive;

    /**
     * Bullet constructor. Just initialize variables.
     */
    public Bullet() {
        this.position = new Vector2();
        this.alive = false;
    }

    /**
     * Initialize the bullet. Call this method after getting a bullet from the pool.
     */
    public void init(float posX, float posY) {
        position.set(posX,  posY);
        alive = true;
    }

    /**
     * Callback method when the object is freed. It is automatically called by Pool.free()
     * Must reset every meaningful field of this bullet.
     */
    @Override
    public void reset() {
        position.set(0,0);
        alive = false;
    }

    /**
     * Method called each frame, which updates the bullet.
     */
    public void update (float delta) {

        // update bullet position
        position.add(1*delta*60, 1*delta*60);

        // if bullet is out of screen, set it to dead
        if (isOutOfScreen()) alive = false;
    }
}

以下は、ゲームの world クラス:

public class World {

    // array containing the active bullets.
    private final Array<Bullet> activeBullets = new Array<Bullet>();

    // bullet pool.
    private final Pool<Bullet> bulletPool = new Pool<Bullet>() {
    @Override
    protected Bullet newObject() {
        return new Bullet();
    }
    };

    public void update(float delta) {

        // if you want to spawn a new bullet:
        Bullet item = bulletPool.obtain();
        item.init(2, 2);
        activeBullets.add(item);

        // if you want to free dead bullets, returning them to the pool:
        Bullet item;
        int len = activeBullets.size;
        for (int i = len; --i >= 0;) {
            item = activeBullets.get(i);
            if (item.alive == false) {
                activeBullets.removeIndex(i);
                bulletPool.free(item);
            }
        }
    }
}

Pools クラスには、任意のオブジェクトのプールを動的に作成するための静的メソッドが用意されています (ReflectionPool と black magicを使います)。上記の例の場合だと、以下のように使用できます。

private final Pool<Bullet> bulletPool = Pools.get(Bullet.class);


プールの使い方

Pool<> では一つの型のオブジェクトを管理するので、型ごとにパラメータ化します。 obtainを実行することで指定した Pool インスタンスからオブジェクトを取得し、 freeを実行することでオブジェクトをPool へ戻します。 プール処理をするオブジェクトは必要に応じてPool.Poolableインタフェースを実装できます (実装するとオブジェクトに reset() メソッドを追加する必要があります)。 Pool.Poolableを実装すると、オブジェクトがpoolに戻された時にPoolが自動的にそのオブジェクトのresetを呼び出します。 最初は要求に応じてオブジェクトが割り当てられます( obtainを実行しなければ、 Poolはオブジェクトを保持しません)。

newObjectは抽象メソッドなので、Pool<>のサブクラスで実際の処理を実装しなければなりません。



Pool 使用上の警告

Poolした オブジェクトへの参照がリークしないよう気をつけてください。 Pool で"free"メソッドを実行したからといって、不要な参照が無効になるとは限りません。 注意しないと細かいバグを引き起こす可能性があります。 オブジェクトをプールへ戻す時に状態を完全にリセットしない場合にも、細かいバグが発生する可能性があります。