2013年3月22日金曜日

磯野ー!2Dゲーム開発しようぜー! Android編 その4

長々と続いてきたカツオと中島のゲーム開発も今回で終了だ。今回はTextとEntityModifier周りを解説する。前回までは以下。

磯野ー!2Dゲーム開発しようぜー! HTML5編 その1
磯野ー!2Dゲーム開発しようぜー! HTML5編 その2
磯野ー!2Dゲーム開発しようぜー! HTML5編 その3
磯野ー!2Dゲーム開発しようぜー! Android編 その1
磯野ー!2Dゲーム開発しようぜー! Android編 その2
磯野ー!2Dゲーム開発しようぜー! Android編 その3


Text
private Font _font;
@Override
    protected void onCreateResources() {
        _font = FontFactory.create(this.getFontManager(), this.getTextureManager(), 256, 256, Typeface.create(Typeface.DEFAULT, Typeface.BOLD), 24, Color.WHITE_ARGB_PACKED_INT);
        _font.load();
    }
    @Override
    protected Scene onCreateScene() {
        _scene = new Scene();
        Text text = new Text(100, 10, _font, "Click on the characters!", this.getVertexBufferObjectManager());
        text.setColor(new Color(0, 0, 0));  // 色ごとにFontを用意するのは難儀なので、WhiteでFontを読み込み、後から色を設定する
        _scene.attachChild(text);
    }
文字を表示するのは一見簡単そうなのだけど柔軟さに欠けるので中々に面倒くさい。第一にフォント読み込み時に色を指定するので文字色を後から任意に変更できない。また色毎にフォントを読み込むのは難儀だ。それなので白色フォントで読み込んだ後にEntityの色変更メソッドを通して任意の色へと設定している。第二にフォント読み込み時の領域確保に指定する値の最適値が判然としない。文字の大きさと文字数から判断するしかないのだけど直感的でないので良く分からない。多量の文字を表示するなら大きくなければならないし、かといって無駄に領域を確保するとメモリを無駄に圧迫するのでやっかいな部分だ。最後にこれはバグなのだけど、コンストラクタで指定した文字列よりも長い文字列をsetTextで後から設定すると例外が発生する。そういう状況が起こりえる場合の回避策はコンストラクタで長い文字列を渡しておくしかないだろう。

フォントを指定して読み込む方法は下記を参照してほしい。
src/org/andengine/examples/CustomFontExample.java


EntityModifier
final ButtonSprite ship = new ButtonSprite(50, 50, _shipRegion.getTextureRegion(0), this.getVertexBufferObjectManager());
        final AnimatedSprite shipAnimation = new AnimatedSprite(10, 10, _shipRegion, this.getVertexBufferObjectManager());
        shipAnimation.animate(50);
        shipAnimation.setPosition(50, 50);  // コンストラクタで指定しているけれど、このコードがないとRotationの挙動がおかしい
        ship.setOnClickListener( new ButtonSprite.OnClickListener(){
            @Override
            public void onClick(ButtonSprite pButtonSprite, float pTouchAreaLocalX, float pTouchAreaLocalY) {
                _scene.detachChild(pButtonSprite);
                shipAnimation.setRotation(0);
                SequenceEntityModifier shipModifier =
                    new SequenceEntityModifier(
                        new IEntityModifier.IEntityModifierListener() {
                            @Override
                            public void onModifierStarted(IModifier<ientity> pModifier, IEntity pItem) {
                            }
                            @Override
                            public void onModifierFinished(final IModifier<ientity> pEntityModifier, final IEntity pEntity) {
                                MainActivity.this.mEngine.runOnUpdateThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        _scene.attachChild(ship);
                                        _scene.detachChild(shipAnimation);
                                        pEntity.unregisterEntityModifier((IEntityModifier)pEntityModifier);
                                    }
                                });
                            }
                        },
                        new RotationByModifier(0.5f, 90)
                        ,new MoveModifier(1.0f, 50, CAMERA_WIDTH+50, 50, 50, EaseExponentialOut.getInstance())
                        ,new RotationByModifier(0.1f, 180)
                        ,new MoveModifier(1.0f, CAMERA_WIDTH+50, 50, 50, 50, EaseExponentialOut.getInstance())
                        ,new RotationByModifier(0.5f, 90)
                    );
                shipAnimation.registerEntityModifier(shipModifier);
                _scene.attachChild(shipAnimation);
            }
        });
        _scene.registerTouchArea(ship);
        _scene.attachChild(ship);
EntityModifier周りだけを抜き取ると余計にわけが分からなくなりそうなのでそこらへんをごっそりと持ってきているけれど、注目して欲しいのは10行目から。SequenceEntityModifierを指定すると順番にEntityの値を変更していくことができる。12行目から27行目までは開始と終了のイベントハンドラーでそれ以降が値の変更を記述している。Tweenjsと同様の働きをする。同時に2つ以上の値を変更したい場合は下記のようにParallelEntityModifierを使用する。
// 省略
new RotationByModifier(0.5f, 90),
new ParallelEntityModifier(
 new ScaleModifier(3, 0.5f, 5),
 new RotationByModifier(3, 90)
),
new RotationByModifier(0.5f, 90),
// 省略
またTweenjsと同様にどのように値を変更させるかEase関数を使って指定ができる。Ease関数の種類は下記を参照してほしい。
AndEngine / src / org / andengine / util / modifier / ease /
実際に挙動を見るのが一番早いのでAndEngineExamplesをローカルに設定してEase関数周りのサンプルを動作させてみるのが一番良いだろう。その際にはExtensionsも必要なので忘れずに取得しよう。詳しくはAndEngineのReadMeを参照してほしい。

EntityModifierの開始、終了のイベントハンドラーでUI要素をいじくる場合は注意してほしい。左記2つの関数はワーカスレッドから呼び出されるのでUI要素をいじくるとアプリがクラッシュする。それなので17行目のようにmEngine.runOnUpdateThreadでUIスレッドを呼び出して処理しよう。

EntityModifierのざっくりとした説明は以上になるけれど、今回EntityModifierをいじくりまわしていていくつか不明な点があった。1つ目は、EnityModifierを使いまわせないかとEnityModifier.reset()をいじくりまわしてみたけれど上手くいかなかったこと。2つ目は、unregisterEntityModifierの使いどころ。Entityが不要なEntityModifierをいつまでも参照しているのが嫌だったので一応呼んでいるけれど、AndEngineのサンプルコードを参考にしてもunregisterEntityModifierを呼んでいるものがなかった。AndEngineのコードを追っかけていけば分かることなのだろうけれど現状はどうするのが一番良いのかは不明。


これでAndEngineの解説を終わりとする。AndEngineの基本的な使い方がどういったものか理解できたかと思う。今回は実装しなかったけれどもちろんゲームのメインループにあたる機能や、あたり判定の便利機能などなどゲーム開発に必要な機能は大体そろっている。そういった今回解説しなかった機能はAndEngineExamplesで詳説されているのでそちらを参照してほしい。

0 件のコメント:

コメントを投稿