Spriteを使った描画

memo~~

Spriteを使った描画

/**
 * Spriteを使った描画
 */
public class SpriteBatchSampleListener extends ApplicationAdapter {
    private SpriteBatch batch = null;
    Texture t = null;

    @Override
    public void create() {
        batch = new SpriteBatch();

        t = new Texture("./profile.gif");
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT);  //画面クリア

        batch.begin();
        batch.draw(t, 150, 200);

        batch.end();
    }

    @Override
    public void dispose () {
    }
}

libgdxのFileHandleでファイル操作(実機で動かしてつまったとこ。

ここ数日は、操作感を実機でためしてみたかったので作ったアプリを実機(android)で動かすことをしてみています。

今まではdesktop上でしか動かしていませんでしたが、androidで動かすためもあって、

gdx-setup.jar でプロジェクト構成を作りなおし → 今まで作ったものをのせかえ

をやったわけなんだけど、ファイルの読み込みに引っかかったのでメモ。

まずおきた問題

File f = new File(FILENAME);

このFileクラスは java.io.File です。

desktopで動かしていた(eclipse上 and javaコマンドで実行)場合は動いていましたが、これがandroidで動かすと FileNotFound になった。

desktopではカレントディレクトリはプロジェクトのあるフォルダだったのですが、androidだと違ったみたいです。

http://snoopopo.hatenablog.com/entry/2015/04/29/112310 の出力結果が、 / って出たのでroot???)

ただ機種によって違うとかもあったらやだので、あまりカレントディレクトリがどこか?を自分が意識したくないと思いました。

そこで、Libgdxの com.badlogic.gdx.files.FileHandle クラスです。

画像ファイルなどの置き場所

android開発をまったくしたことないのでよくわからないですが(>_<)、

android/assets/ 配下に、画像ファイルなどの資材をおいておくと都合がよかったです。なのでそこにおくことにした。

libgdxのFileHandleでファイル操作

https://github.com/libgdx/libgdx/wiki/File-handling にあるとおり、

Gdx.files.internal(FILENAME);

でとることが出来ます。

ここのFILENAMEは、 android/assets の下からのファイル名、またはフォルダ名を指定することが出来ました。

今までjava.io.Fileでファイルとって、InputStreamReaderで1文字ずつ読み込んで〜 みたいなことをしていたのですが、

FileHandle file = Gdx.files.internal(FILENAME);
Reader reader = file.reader();

while(render.read() != -1) { /* shori... */ }

みたいに、FileHandle#reader()でReaderクラス(中身はFileInputStream)をとることも出来ます。

desktop(たぶんhtmlとかで動かすときも)で動かすときは

android/assets/ の配下に画像ファイルをおいてしまったので、

desktop上で動かすときはパスを通してあげる必要がありましたー(メモ

プレイヤーの動きに合わせてカメラを移動させる

↑の状態、

プレイヤーの歩く動きに合わせてカメラが移動しているように見えるが、

カメラがマス単位(このゲームだと32dot)移動→そのあとにプレイヤーがドット単位で移動 という風になってしまっています。(カメラの動きがかくかくしてるかんじする。)

今回はこれを改善します。

関連記事

http://snoopopo.hatenablog.com/entry/2015/07/26/145013

http://snoopopo.hatenablog.com/entry/2015/07/16/170043

今回は、上記の内容で出来てなかった、アクション実行時もカメラをプレイヤーに合わせることです。

プレイヤーの動きに合わせてカメラを移動させる

移動と攻撃はどちらも、scene2dのActionクラスを使って移動させています。

/**
 * アクションの動きごと追跡するカメラ
 */
public class TrackActionCameraSampleListener extends ApplicationAdapter {

    private Stage stage = null;
    private OrthographicCamera cam; 

    private int cnt = 1;
    private boolean preEnter = false;
    private boolean preDown = false;
    
    private Array<Image> imagelist = null;
    private Image player = null;
    
    private float startPositionWidth;
    private float startPositionHeight;
    
    @Override
    public void create() {
        
        stage = new Stage();
        Gdx.input.setInputProcessor(stage);

        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();
            
        cam = (OrthographicCamera) stage.getViewport().getCamera();
        cam.setToOrtho(false, w/2, h/2); //カメラのサイズを画面サイズの半分にしてる
    
        imagelist = new Array<>();
        Texture p = new Texture("player_x0_y0.png");
        player = new Image(p);
        imagelist.add(player);  //ひとつ目
        imagelist.add(new Image(new Texture("player_x1_y0.png")));   //2つ目
        imagelist.add(new Image(new Texture("player_x2_y0.png")));   //3つ目
    
        startPositionWidth = cam.viewportWidth/2 - p.getWidth()/2;
        startPositionHeight = cam.viewportHeight - p.getHeight();
    
        for (Image image : imagelist) {
            image.setPosition(startPositionWidth, startPositionHeight);
            stage.addActor(image);
        }
        
        //移動したのがわかりにくいので、適当に表示
        Texture c = new Texture("enemy2_x0_y0.png");
        Image cat1 = new Image(c);
        cat1.setPosition(startPositionWidth + c.getWidth(), startPositionHeight);
        stage.addActor(cat1);
        
        Texture c2 = new Texture("enemy2_x0_y1.png");
        Image cat2 = new Image(c2);
        cat2.setPosition(startPositionWidth - c2.getWidth(), startPositionHeight - 120);
        stage.addActor(cat2);

        Texture c3 = new Texture("enemy2_x0_y2.png");
        Image cat3 = new Image(c3);
        cat3.setPosition(startPositionWidth + c3.getWidth(), startPositionHeight - 180);
        stage.addActor(cat3);
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(0, 1, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);   //画面クリア
        
        stage.act(Gdx.graphics.getDeltaTime()); //よばないとアクションが実行されなかった
        
        if (!preDown && Gdx.input.isKeyPressed(Keys.DOWN)) {
            for (Image image : imagelist) {
                image.addAction(Actions.moveBy(0, -100, 2)); //2秒間で100px前に前進
            }
        }
        
        if (!preEnter && Gdx.input.isKeyPressed(Keys.ENTER)) {
            for (Image image : imagelist) {
                SequenceAction seqAct = Actions.sequence();
                seqAct.addAction(Actions.moveBy( 0, player.getHeight() /2 * -1, 0.2F ));
                seqAct.addAction(Actions.moveBy( 0, player.getHeight() /2, 0.2F ));
                image.addAction(Actions.repeat(2, seqAct)); //攻撃っぽい動き
            }
        }
        
        preDown = Gdx.input.isKeyPressed(Keys.DOWN);
        preEnter = Gdx.input.isKeyPressed(Keys.ENTER);
        
        for (Image image : imagelist) {
            image.setVisible(false); //いったん全部を非表示にして
        }

        //今のフレームで表示させる画像だけ表示する
        int rest = cnt % 10;
        if (rest <= 2) {
            imagelist.get(0).setVisible(true);
        } else if (rest <= 6) {
            imagelist.get(1).setVisible(true);
        } else {          
            imagelist.get(2).setVisible(true);
        }
        cnt++;
    
        //カメラの中心をプレイヤーに合わせる
        cam.position.x = player.getX();
        cam.position.y = player.getY();
        
        stage.draw();
    }
    
    @Override
    public void resize(int width, int height) {
        cam.update();
    }

    @Override
    public void dispose() {
        stage.dispose();
    }
}

カメラのポジションにプレイヤーの位置をそのまま設定してあげればよい

カメラのポジションにプレイヤーの位置をそのまま設定してあげればよいだけでした。

       //カメラの中心をプレイヤーに合わせる
        cam.position.x = player.getX();
        cam.position.y = player.getY();

アクションが実行された際に、Actor#getX, Actor#getY でとれる値がちゃんと変わってくれてるわかるので、 この値を直接指定してあげればいきます。

というわけで

上記の内容を作っているゲームに反映させるとこのようになります。

歩行はよさそうですが、攻撃のときもカメラが動いているのでせわしない気もします。(SFC風來のシレンでは、攻撃時にはカメラが動いてないように見える)

歩行のときだけカメラを動かすとか、そのあたりの調整は必要なかんじ。

FPSLoggerでフレームレートをはかる

FPSLoggerでフレームレートをはかる

やってく過程で異常に処理が重いことに気づいたので、FPS(ゲームのジャンルのほうじゃなくて、Frame per secondのほう)をはかってみます。

com.badlogic.gdx.graphics.FPSLogger というクラスがLibGDXに存在したので、今回はこれを使ってみました。

FPSLogger (libgdx API)

・FPSLoggerSample.java

/**
 * FPSを計測出来るクラスFPSLoggerの使い方サンプル.
 * コンソールにFPS値のログがはかれます.
 */
public class FPSLoggerSampleListener extends ApplicationAdapter {

    private FPSLogger fpslogger = null;
    
    @Override
    public void create() {
        fpslogger = new FPSLogger();
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);   //画面クリア
        
        fpslogger.log();    //ここ呼んであげればよい
    }
}

インスタンスを生成して、ApplicationLisner#render()でlogを呼んであげればよいようです。

出力結果は以下のようなかんじで、1秒単位でログに出力されます。

FPSLogger: fps: 61
FPSLogger: fps: 60

LibGdxは可変フレームレートかと思っていたのですが、どんなに軽い処理(上のように何もしてない処理でも)60〜62位を保っていて、 それ以上にはあがらないですね。固定??

・パフォーマンス改善前(平均10FPS…)

ゲームに組み込むと、上のようにかなり重くてもちろん環境に依存すると思うのですが古いPCでやったら平均10FPS位でした。。

原因は、newをループの中でしていた為です。FPSが時間経過ごとのどんどん落ちていったのでそれで気づけました。

・パフォーマンス改善後(平均60FPS)

FPS60を保つのが目標でやっていくとよい、とひらしょー本にかいてあった。

キャラの歩行パターンで画像切り替えながらアクション

キャラの歩行パターンで画像切り替えながらアクション

この記事はキャラ画像を切り替えながらアクションさせる方法を書きます。

まずは画像を用意します

今回から素材サイトさんから画像おかりしてます! かなりかわいい。

ぴぽや

f:id:snoopopo:20150726132759p:plain f:id:snoopopo:20150726132812p:plain f:id:snoopopo:20150726132814p:plain

これは前向きだけですが、上下左右の4方向 * 3パターンで、時間経過ごとにパターンを切り替えます。

上記のサイトでは、ウディタ用に素材をまとめてくれているのですが、今回はLibGdxのテクスチャアトラスとしてまとめたいので、 元の素材を別々にばらしてから、テクスチャアトラス画像にまとめています。

・分割ツール

超画像魂コンバイン - k本的に無料ソフト・フリーソフト

いくつかほかの分割ツールをためしましたが、アルファ情報も保持したまま分割してくれるので、goodです。

・画像をテクスチャアトラス化するのは前にやった↓

http://snoopopo.hatenablog.com/entry/2015/06/06/173336

ソースのほう

/**
 * キャラ画像を切り替えながらアクション(キャラが歩行する処理のサンプル)
 */
public class Scene2dActionWalkSampleListener extends ApplicationAdapter {

    private Stage stage = null;

    private int cnt = 1;
    private boolean preEnter = false;

    private Array<Image> imagelist = null;

    @Override
    public void create() {
        stage = new Stage();
        Gdx.input.setInputProcessor(stage);

        imagelist = new Array<>();
        imagelist.add(new Image(new Texture("./player_x0_y0.png"))); //ひとつ目
        imagelist.add(new Image(new Texture("./player_x1_y0.png"))); //2つ目
        imagelist.add(new Image(new Texture("./player_x2_y0.png"))); //3つ目

        for (Image image : imagelist) {
            image.setPosition(200,300);
            stage.addActor(image);
        }
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT);  //画面クリア

        stage.act(Gdx.graphics.getDeltaTime()); //よばないとアクションが実行されなかった

        if (!preEnter && Gdx.input.isKeyPressed( Input.Keys.ENTER)) {
            for (Image image : imagelist) {
                image.addAction(Actions.moveBy(0, -100, 2)); //2秒間で100px前に前進
            }
        }

        preEnter = Gdx.input.isKeyPressed( Input.Keys.ENTER);

        for (Image image : imagelist) {
            image.setVisible(false); //いったん全部を非表示にして
        }

        //今のフレームで表示させる画像だけ表示する
        int rest = cnt % 10;
        if (rest <= 2) {
            imagelist.get(0).setVisible(true);
        } else if (rest <= 6) {
            imagelist.get(1).setVisible(true);
        } else {
            imagelist.get(2).setVisible(true);
        }
        cnt++;

        stage.draw();
    }

    @Override
    public void dispose() {
        stage.dispose();
    }
}

すべてパターンの画像に、それぞれアクションを設定してあげて、 画像の表示非表示を切り替えている、かんじです。

なので、非表示になっていて見えないけど、すべての画像が同じ動き(アクション)をしていることになります。

表示、非表示の切り替えてる部分 Image#setVisible

ここでImage#setVisibleを使わずに、はじめは、image#remove()してstage#add(image)をしていたのですが、 アクションの設定など、消えたくないものも消えて大量にぬるぽが発生したので、今回はこのようにしています。

       for (Image image : imagelist) {
            image.setVisible(false); //いったん全部を非表示にして
        }

        //今のフレームで表示させる画像だけ表示する
        int rest = cnt % 10;
        if (rest <= 2) {
            imagelist.get(0).setVisible(true);
            //〜略〜
        }

アクションはイメージのオブジェクトそれぞれに作ってあげないとだめっぽい

           for (Image image : imagelist) {
                image.addAction(Actions.moveBy(0, -100, 2)); //2秒間で100px前に前進
            }

アクションのインスタンスは、画像それぞれに必要なようでした。

・動かないソース↓

           Action action = Actions.moveBy(0, -100, 2);
            for (Image image : imagelist) {
                image.addAction(action);
            }

上記では動きませんでした。(libgdxのライブラリ内でぬるぽなので、中の動きをみるひつようある TODO

OrthographicCameraで2Dカメラ

OrthographicCameraで2Dカメラ

今回はカメラの処理をやります。2Dです。

ビューポートが絡んでいるみたいなので実機でのどのように表示されているか確認をしたいところですが、 確認できていないので後ほど。

→20150810 add とくに問題なかった!

公式Wikiはここ↓だと思います。

https://github.com/libgdx/libgdx/wiki/Orthographic-camera

以下のソースも上のコードを改変したものです。

/**
 * 2Dカメラ処理のサンプル
 */
public class OrthographicCameraSampleListener extends ApplicationAdapter {

    private Stage stage = null;
    private OrthographicCamera cam;
    private float rotationSpeed;

    @Override
    public void create() {

        rotationSpeed = 0.5f;

        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();

        stage = new Stage();
        Gdx.input.setInputProcessor(stage);

        cam = (OrthographicCamera) stage.getViewport().getCamera(); //stageが持っているカメラを使う
        cam.setToOrtho(false, w/2, h/2);

        Texture t2 = new Texture("./profile.gif");
        Image image = new Image(t2);
        image.setPosition(0, 0);
        stage.addActor(image);
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);   //画面クリア

        handleInput();
        stage.draw();
    }

    private void handleInput() {
        if (Gdx.input.isKeyPressed( Input.Keys.A)) {
            cam.zoom += 0.02;
        }
        if (Gdx.input.isKeyPressed(Input.Keys.Q)) {
            cam.zoom -= 0.02;
        }
        if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
            cam.translate(-3, 0, 0);
        }
        if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
            cam.translate(3, 0, 0);
        }
        if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
            cam.translate(0, -3, 0);
        }
        if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
            cam.translate(0, 3, 0);
        }
        if (Gdx.input.isKeyPressed(Input.Keys.W)) {
            cam.rotate(-rotationSpeed, 0, 0, 1);
        }
        if (Gdx.input.isKeyPressed(Input.Keys.E)) {
            cam.rotate(rotationSpeed, 0, 0, 1);
        }
    }

    @Override
    public void resize(int width, int height) {
        cam.update();
    }

    @Override
    public void dispose() {
        stage.dispose();
    }
}

以下、自分がつまずいた部分を書いておく。

OrthographicCameraを作るとき

2D用に使えるクラスはOrthographicCameraというクラスらしい。

このインスタンスを作るとき、

OrthographicCamera camera = new OrthographicCamera(w, h);

コンストラクタの引数でカメラのサイズを渡すと、カメラ座標の中心が原点のまま

OrthographicCamera camera = new OrthographicCamera();
camera.setToOrtho(false, w, h);

↑上のようにsetToOrthoメソッドにすると、左下が原点になってくれる。

Scene2dを使っていてカメラを使うとき

//...
SpriteBatch batch = new SpriteBatch();
Sprite sprite = new Sprite(texture);
//...

batch.setProjectionMatrix(camera.combined);
batch.begin();
sprite.draw(batch);

↑のようにSpriteBacth#setProjectionMatrixにカメラオブジェクトを渡してdrawする例はあるのだけど、

自分はScene2d*1を使って描画をしているので、

Stageのメンバがもっているカメラを使います。

Stageをnewするときに引数を指定しなければ、StageクラスでSpriteBatchとViewport(とカメラ)をnewしてくれてます。

cam = (OrthographicCamera) stage.getViewport().getCamera();    //stageが持っているカメラを使う

あと、Stage#drawの中で Sprite#draw(Batch)やCamera#update をやっているので、 Stage#drawをやっていればこれらのメソッドを呼ぶ必要はないみたいです。わかってなかったけどこういうのがあるので、seane2dは便利。

今のとこの理解だと、 描画をするのはSpriteを使うのが基本みたいで、StageのdrawはSpriteをラッピングして便利にしているクラスってこと、っていう認識。

こんなかんじ↓

上記の内容をもとに、今作ってるゲームに取り込んでみた。

画像なのでわかりにくいけど、プレイヤー(棒人間)が動くとプレイヤーも移動していますが、 描画の度にプレイヤーがカメラの中心になるようにカメラ座標を合わせているので、 背景が逆方向に動いているようなかんじになります。

f:id:snoopopo:20150716154935p:plain

今いるところから…(↑)

左に移動するとこんな見た目になります。(↓)

f:id:snoopopo:20150716154936p:plain

昔のポケモン(GBのとか)とかも主人公がずっと画面の中心にいたので、そういうかんじです。

*1:ここらへんのやつをいいたい。https://github.com/libgdx/libgdx/wiki/Scene2d

今日のlibGDX(20150706):E. 開発記 9 シーケンス遷移を改善した【20150715コメント頂いた分追記】

今日のlibGDX目次

http://snoopopo.hatenablog.com/entry/2015/04/27/220545

今日のテーマ:E. 開発記 9 シーケンス遷移を改善した

人に助言を頂けたのもありシーケンス遷移を改善したので、メモしておきます。

ちょいちょいシーンとシーケンスってまざって言ってるけど、僕は同じ意味だと思って書いてるので注意。

やったことは以下です。

・それぞれのシーンをクラス化

・swich文,シーンを定義してた列挙型の削除

それぞれのシーンをクラス化

今までメソッドでそれぞれシーンを作っていましたが、クラスにした。以下理由。

・処理のながれ一緒

初期処理(遷移直後の1フレームだけ) → 更新処理

・シーンそれぞれに書いていた処理でだぶってる部分ある

というわけで上記の同じ部分は基底クラス(抽象クラス)で行い、固有の処理だけ子クラス側でやるように修正。

package jp.snoopopo.e.core;

import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;

import static jp.snoopopo.e.core.MainLisner.assetMng;
import static jp.snoopopo.e.core.MainLisner.PATH_SE_ENTER;
import static jp.snoopopo.e.core.MainLisner.PATH_IMG_GAME;

/**
 * シーン基本クラス.
 * @author snoopopo
 *
 */
public abstract class BaseScene {
        
    private static final int ROUTINE_INIT = 0;
    private static final int ROUTINE_PROCESSING = 1;   
    private static int routineNo = ROUTINE_INIT;
    
    protected static boolean spTutch = false;
    
    protected Image retry = null;
    
    protected TextureAtlas gameResource = assetMng.get(PATH_IMG_GAME, TextureAtlas.class);    

    /**
    * このシーンの処理を実行する
    * @param stage このシーンで生成されたアクターを設定するステージ
    */
    public void update(Stage stage) {
        switch (routineNo) {
        case ROUTINE_INIT :           
            init(stage);
            routineNo = ROUTINE_PROCESSING;
            break;
        case ROUTINE_PROCESSING :
            processing();
            break;
        }
    }

    abstract void init(Stage stage);
    abstract void processing();

    protected void transition(BaseScene scene) {

        //シーケンス設定
        MainLisner.scene = scene;
        routineNo = ROUTINE_INIT;
        spTutch = false;

        assetMng.get(PATH_SE_ENTER, Sound.class).play();
    }
}

switch文,シーンを定義してた列挙型の削除

・改善前のMainLisner(ApplicationAdapter)

public class MainLisner extends ApplicationAdapter {

    //actor.
    private Stage stage = null;

    @Override
    public void render() {
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);   //画面クリア
        updateGame();
        draw();
    }

    private void updateGame() {
        stage.act(Gdx.graphics.getDeltaTime());

        switch (status){
        case TITLE :
            sceneTitle();
            break;
        case PLAYING :
            sceneMain();
            break;
        case CLEAR :
            sceneClear();
            break;
        case GAMEOVER :
            sceneGameOver();
            break;
        }
    }

    private static enum GameState {
        TITLE, PLAYING, CLEAR, GAMEOVER;
    }
}

今までのソース↑はswitchで列挙型で定義してた分を分岐してたわけなのだが、 これだと、シーンが増える度にケースと、列挙型の定義を増やす必要が出てくる。

かなり小規模だから今はいいけど、大きいゲームだとまずいので、以下のように修正。

・MainLisner(ApplicationAdapter)

public class MainLisner extends ApplicationAdapter {

    static BaseScene scene = null;

    private Stage stage = null;

    @Override
    public void render() {
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);   //画面クリア
        updateGame();
        draw();
    }
    
    /**
    * ゲームの更新
    */
    private void updateGame() {
        stage.act(Gdx.graphics.getDeltaTime());

        if (!assetMng.update()) {
            System.out.println("Now Loading");
            return;
        }

        if (null == scene) {
            //初回のみ
            scene = new SceneTitle();
        }
        scene.update(stage);
    }

・それぞれのシーンクラス(これはタイトル用のクラス)

package jp.snoopopo.e.core;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;

import static jp.snoopopo.e.core.MainLisner.assetMng;
import static jp.snoopopo.e.core.MainLisner.PATH_BGM;
import static jp.snoopopo.e.core.MainLisner.preEnter;

/**
 * タイトルクラス.
 * @author snoopopo
 *
 */
public class SceneTitle extends BaseScene {

    private Image start = null;
    private Image title = null;
    
    @Override
    void init(Stage stage) {    /* 初期処理してる*/     }
        
    @Override
    void processing() {
        if (spTutch || (!preEnter && Gdx.input.isKeyPressed(Keys.ENTER))) {
            //「タイトル」→「プレイ中」
            transition(new SceneMain());
            title.remove();
            start.remove();
        }       
    }
}

遷移するタイミングで次のシーンクラスをnewして設定します。 ここでnewしたSceneMainはBaseSceneの子クラスです。

設定後は、次のフレームでMainLisnerが子クラスのシーンクラスを意識せずにBaseSceneのupdateをよんでくれるかんじです。

ここまでのソース

https://github.com/snoopopo/e/tree/v0.11

↓↓↓↓ 20150715 ここから追記分 ↓↓↓↓

Gameクラスでシーケンス遷移

ApplicationListenerを実装してるGameクラス(abstract)というクラスがLibGDXに用意されているというコメントを頂きました!ありがとうございます。

というわけでさっそく試してみた。

Gameクラスの中にはScreen(インタフェース)のメンバがいて、これが今のシーケンス(シーン?スクリーン?)になるみたいです。

・今までApplicationAdaptorを継承していたクラスは、Gameにかわる

public class MainLisner extends Game {

    @Override
    public void create() {
        setScreen(new ScreenTitle(this));
    }
    
}

はじめに遷移したいタイトル画面相当のクラスScreenTitleをnewして、設定してあげます。(以前はSceneTitleだったクラス)

・それぞれのスクリーンクラスはScreenインタフェースを実装すればいい。

今回は、Screenを実装したScreenAdapter(abstract)がいたので、これを継承しました。 ScreenAdapterの中身は全部空メソッドなので、上書きしたいとこだけ書きます。

以下はタイトル画面から遷移したゲームのメイン画面相当のクラスです。

public class ScreenGame extends ScreenAdapter {

    private Game game = null;
    public ScreenGame(Game game){
        this.game = game;

        stage = new Stage();
        Gdx.input.setInputProcessor(stage);
        
        camera = new OrthographicCamera();        
        
        assetMng = new AssetManager();
        assetMng.load(ATLAS_FIELD_CONTENT, TextureAtlas.class);

        // いかりゃく
    
    }

    @Override
    public void render (float delta){
        
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);   //画面クリア

        stage.act(delta);

        if (assetMng.update()) {
            draw(stage, assetMng);
        }
        
        update();
        stage.draw();
        
        // シーケンス遷移 setScreenする
    }

    @Override
    public void hide() {
        assetMng.dispose();
        stage.dispose();
    }

    @Override
    public void resize(int width, int height) {
        camera.update();
    }

    //something....
}

コンストラクタでGame自体を受け取らないと、次のスクリーンを設定出来ないので、引数でもらいます。

コンスラクタでStageやAssetManagerをnewしているのですが、こういう初期処理は、show()でも出来るみたいです。

リソースの解放のお話も教えていただいたここ↓のサイトが詳しかった。

mikio.github.io

自分の今作ってるゲームの場合だと、シーケンス単位よりも細かい単位でリソースの解放(読み込みも)をするつもりなので、 自分で解放処理を書く、というのは都合良く使えそう。

コメントありがとうございました!