今日の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
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 {
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
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();
}
@Override
public void hide() {
assetMng.dispose();
stage.dispose();
}
@Override
public void resize(int width, int height) {
camera.update();
}
}
コンストラクタでGame自体を受け取らないと、次のスクリーンを設定出来ないので、引数でもらいます。
コンスラクタでStageやAssetManagerをnewしているのですが、こういう初期処理は、show()でも出来るみたいです。
リソースの解放のお話も教えていただいたここ↓のサイトが詳しかった。
mikio.github.io
自分の今作ってるゲームの場合だと、シーケンス単位よりも細かい単位でリソースの解放(読み込みも)をするつもりなので、
自分で解放処理を書く、というのは都合良く使えそう。
コメントありがとうございました!