over 5 years ago

ゲーム上に表示する物体を扱う方法としてスプライトという仕組みがある。libGDX では Sprite クラスで実現する。SimpleApp では、Rectangle を利用していたが Sprite と Rectangle は基本的に同じように利用できる。違いは、Sprite がテクスチャを持っていること前提に使用するものである。Rectangle はテクスチャを持たなくてもいい。基本的にゲームに表示する物体はテクスチャが必要であるから、特別な事情がなければ Rectangle ではなく Sprite の方が適切だろうし、感覚的に扱いやすい。

Sprite インスタンスを生成する方法はいくつかあるが、スプライト毎に異なるテクスチャを持つ必要があるなら、TextureAtlas インスタンスから生成する方法がいいだろう。

TextureAtlas クラスは、テクスチャアトラスを扱うためのクラス。スプライト毎にテクスチャを一枚の小さな画像から生成して描写する処理はフレームワーク側にとっては無駄の多い処理とされる。この問題に対処するため、複数の小さなテクスチャを一枚の大きな画像から生成して扱えるようにする仕組みが TextureAtlas だ。

TextureAtlas で使用するテクスチャの集合画像は、libgdx-texturepacker-gui で簡単に作成できる。input用ディレクトリにパッキング前の画像を用意すれば、パッキング後の画像と画像マッピングファイルを適当なディレクトリに出力できる。また、テクスチャーパッキングをするために設定した一連の情報はプロジェクトという形で保存することができる。

画像が用意できたら、スプライトを下記のコードで生成できる。

TextureAtlas atlas;
Sprite drop;
Sprite bucket;
atlas = new TextureAtlas(Gdx.files.internal("images.atlas"));
drop = atlas.createSprite("droplet");
bucket = atlas.createSprite("bucket");

マルチスクリーン対応したSimpleAppをスプライトとテクスチャアトラスを利用して修正したコード

package com.badlogic.drop;

import java.util.Iterator;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;

public class GameLoopScreen extends DropScreen {
    TextureAtlas atlas;
    Sprite drop;
    Sprite bucket;
    Sound dropSound;
    Music rainMusic;
    SpriteBatch batch;
    OrthographicCamera camera;
    Array<Sprite> raindrops;
    long lastDropTime;

    public GameLoopScreen() {
        atlas = new TextureAtlas(Gdx.files.internal("images.atlas"));
        drop = atlas.createSprite("droplet");
        bucket = atlas.createSprite("bucket");

        // load the drop sound effect and the rain background "music"
        dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
        rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));

        // start the playback of the background music immediately
        rainMusic.setLooping(true);
        rainMusic.play();

        // create the camera and the SpriteBatch
        camera = new OrthographicCamera();
        camera.setToOrtho(false, Defines.WINDOW_WITDH, Defines.WINDOW_HEIGHT);
        batch = new SpriteBatch();

        // create a Rectangle to logically represent the bucket
        bucket.setX(Defines.WINDOW_WITDH / 2 - 48 / 2);
        bucket.setY(20);
        bucket.setSize(48, 48);

        // create the raindrops array and spawn the first raindrop
        raindrops = new Array<Sprite>();
        spawnRaindrop();
    }

    private void spawnRaindrop() {
        Sprite raindrop = new Sprite();
        raindrop.setX(MathUtils.random(0, Defines.WINDOW_WITDH - 48));
        raindrop.setY(Defines.WINDOW_HEIGHT);
        raindrop.setSize(48, 48);
        raindrops.add(raindrop);
        lastDropTime = TimeUtils.nanoTime();
    }

    @Override
    public void update(float delta) {
        // process user input
        if (Gdx.input.isTouched()) {
            Vector3 touchPos = new Vector3();
            touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
            camera.unproject(touchPos);
            bucket.setX(touchPos.x - 48 / 2);
        }

        if (Gdx.input.isKeyPressed(Keys.LEFT))
            bucket.setX(bucket.getX() - 200 * Gdx.graphics.getDeltaTime());
        if (Gdx.input.isKeyPressed(Keys.RIGHT))
            bucket.setX(bucket.getX() + 200 * Gdx.graphics.getDeltaTime());

        // make sure the bucket stays within the screen bounds
        if (bucket.getX() < 0)
            bucket.setX(0);
        if (bucket.getX() > Defines.WINDOW_WITDH - 48)
            bucket.setX(Defines.WINDOW_WITDH - 48);

        // check if we need to create a new raindrop
        if (TimeUtils.nanoTime() - lastDropTime > 1000000000)
            spawnRaindrop();

        // move the raindrops, remove any that are beneath the bottom edge of
        // the screen or that hit the bucket. In the later case we play back
        // a sound effect as well.
        Iterator<Sprite> iter = raindrops.iterator();
        while (iter.hasNext()) {
            Sprite raindrop = iter.next();
            raindrop.setY(raindrop.getY() - 200 * Gdx.graphics.getDeltaTime());
            if (raindrop.getY() + 48 < 0)
                iter.remove();
            if (raindrop.getBoundingRectangle().overlaps(bucket.getBoundingRectangle())) {
                dropSound.play();
                iter.remove();
            }
        }
    }

    @Override
    public void draw(float delta) {
        // clear the screen with a dark blue color. The
        // arguments to glClearColor are the red, green
        // blue and alpha component in the range [0,1]
        // of the color to be used to clear the screen.
        Gdx.gl.glClearColor(0, 0, 0.2f, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        // tell the camera to update its matrices.
        camera.update();

        // tell the SpriteBatch to render in the
        // coordinate system specified by the camera.
        batch.setProjectionMatrix(camera.combined);

        // begin a new batch and draw the bucket and
        // all drops
        batch.begin();
        batch.draw(bucket, bucket.getX(), bucket.getY());
        for (Sprite raindrop : raindrops) {
            batch.draw(drop, raindrop.getX(), raindrop.getY());
        }
        batch.end();
    }

    @Override
    public void dispose() {
        atlas.dispose();
        dropSound.dispose();
        rainMusic.dispose();
        batch.dispose();
    }

}
← libGDX - 文字表示とフォント libGDX - 入力イベント →