アクターが動きおわったら別のアクターを動かす【20150818追記】

アクターが動きおわったら別のアクターを動かす

今回やりたいのは、不思議のダンジョン系ゲームで「プレイヤーが攻撃→敵が攻撃」 という動作です。

歩行はプレイヤーと敵が同時に動きますが、それとちがって、「プレイヤーが動いたあとに敵が動く」=「アクターが動き終わったら別のアクターを動かす」を実装します。

関連記事

http://snoopopo.hatenablog.com/entry/2015/05/13/154146

上ではひとつのアクターに対して複数のアクションを順次実行することをやりました。

今回は別のアクターが動き終わったら動かすことするので、複数アクターで、順次シーケンスするような動きについてやります。

アクションが終わったタイミング

https://github.com/libgdx/libgdx/wiki/Scene2d から抜粋。

actor.addAction(sequence(fadeIn(2), run(new Runnable() {
    public void run () {
        System.out.println("Action complete!");
    }
})));

SequenceActionでアクションの動きが終わったあとに、終了時の処理であるオリジナルのアクションを実装すればいいっぽい。

つまり、Runnable#runの実装で別のアクターにアクションを設定してあげればよい!

20150818追記こっから↓↓↓

なんでこんなよくありそうな処理がこんなややこしいプログラムになるのかと思ったけど、落ち着いたら全然もっと簡単にかけたw ので、修正っす。

アクションが実行されるタイミング(超復習)

アクターに設定されたタイミング && (stageにactorが設定されてる前提)

actor.addAction(action);

なので、上の「アクションが終わったタイミング」で次に実行したいアクションをするアクターにアクションを設定してあげればよい。

直接設定しようとするとややこしくなるから今回はフラグでやるのでこんなイメージ

if (!move && 0 != list.size) {        //動いてないけど、動くの待ちのアクターがいる
        move = true;
        Actor actor = list.pop();
        actor.addAction(seqAction); //シーケンスアクションにして最後のアクションでにmoveフラグ解除
}

↓↓↓↓修正版↓↓↓↓

/**
 * 複数のアクターを順次アクションさせるサンプル
 * アクターAがアクションし終わったあとに、アクターBがアクションする
 * リスト可変対応版
 * Scene2dActionAttack2SampleListener からソース簡単にしたバージョン
 */
public class Scene2dActionAttack3SampleListener extends ApplicationAdapter {

    private Stage stage = null;
    private boolean move = false;

    private Array<ActionAndImage> list = new Array<>();

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

        //ひとつ目のイメージ
        Image image1 = new Image(new Texture("./profile.gif"));
        image1.setPosition(200,300);

        SequenceAction seqAct = Actions.sequence();
        seqAct.addAction(Actions.moveBy( 16 , 16, 0.1F ));
        seqAct.addAction(Actions.moveBy( 16 * -1 , 16 * -1, 0.1F ));
        RepeatAction action1 = Actions.repeat(2, seqAct);
        list.add(new ActionAndImage(image1, action1));
        stage.addActor(image1);

        //2つ目
        Image image2 = new Image(new Texture("./profile.gif"));
        image2.setPosition(200,200);
        Action action2 = Actions.moveBy(-100, 0, 1);
        list.add(new ActionAndImage(image2, action2));
        stage.addActor(image2);

        //3つ目
        Image image3 = new Image(new Texture("./profile.gif"));
        image3.setPosition(200,100);
        Action action3 = Actions.moveBy(-150, 0, 1);
        list.add(new ActionAndImage(image3, action3));
        stage.addActor(image3);

    }

    class ActionAndImage{
        Image image = null;
        Action action = null;
        ActionAndImage(Image image, Action a) {
            this.image = image;
            this.action = a;
        }
    }

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

        stage.act(Gdx.graphics.getDeltaTime()); //よばないと動かなかった

        if (!move && list.size != 0) {
            move = true;

            Action endAction = (Actions.run(new Runnable() {
                public void run () { move = false;}
            }));

            ActionAndImage obj = list.pop();    //これだと詰めた逆順からになるからここは場合で変える

            SequenceAction seqAction = Actions.sequence();
            seqAction.addAction(obj.action);
            seqAction.addAction(endAction);
            obj.image.addAction( seqAction );
        }

        stage.draw();
    }

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

↓↓↓ここから下は追記(修正)前の内容なので見ないほうよい↓↓↓

まずベタに書く

/**
 * 複数のアクターを順次アクションさせるサンプル
 * アクターAがアクションし終わったあとに、アクターBがアクションする
 */
public class Scene2dActionAttackSampleListener extends ApplicationAdapter {

    private Stage stage = null;
    private boolean move = false;

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

        Array<ActionAndImage> list = new Array<>();

        //ひとつ目のイメージ
        Image image1 = new Image(new Texture("./profile.gif"));
        image1.setPosition(200,300);

        SequenceAction seqAct = Actions.sequence();
        seqAct.addAction(Actions.moveBy( 16 , 16, 0.1F ));
        seqAct.addAction(Actions.moveBy( 16 * -1 , 16 * -1, 0.1F ));
        RepeatAction action1 = Actions.repeat(2, seqAct);
        list.add(new ActionAndImage(image1, action1));
        stage.addActor(image1);

        //2つ目
        Image image2 = new Image(new Texture("./profile.gif"));
        image2.setPosition(200,200);
        Action action2 = Actions.moveBy(-100, 0, 1);
        list.add(new ActionAndImage(image2, action2));
        stage.addActor(image2);

        //3つ目
        Image image3 = new Image(new Texture("./profile.gif"));
        image3.setPosition(200,100);
        Action action3 = Actions.moveBy(-150, 0, 1);
        list.add(new ActionAndImage(image3, action3));
        stage.addActor(image3);

        //----------------↑ここまで↑もらうデータの想定------------------------

        Action endAction = (Actions.run(new Runnable() {
            public void run () { move = false;}
        }));

        Action startAction = Actions.run(new Runnable() {
            public void run () { move = true;}
        });

        //3つめのActorに設定 Action3 -> end
        Action chainE_3 = Actions.run(new Runnable() {
            public void run () {
                SequenceAction seq3 = Actions.sequence();
                seq3.addAction(action3);
                seq3.addAction(endAction);
                image3.addAction(seq3);
            }
        });

        //2つめのActorに設定 Action2 -> ( Action3 -> end )
        Action chainE_3_2 = Actions.run(new Runnable() {
            public void run () {
                SequenceAction seq2 = Actions.sequence();
                seq2.addAction(action2);
                seq2.addAction(chainE_3);
                image2.addAction(seq2);
            }
        });

        //1つめのActorに設定 start -> Action1 -> { Action2 -> ( Action3 -> end ) }
        SequenceAction seq1 = Actions.sequence();
        seq1.addAction(startAction);
        seq1.addAction(action1);
        seq1.addAction(chainE_3_2);
        image1.addAction(seq1);
    }

    class ActionAndImage{
        Image image = null;
        Action action = null;
        ActionAndImage(Image image, Action a) {
            this.image = image;
            this.action = a;
        }
    }

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

        stage.act(Gdx.graphics.getDeltaTime()); //よばないと動かなかった
        stage.draw();

        System.out.println("アクション実行中?→ " + (move ? "yes." : "no!"));
    }

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

3つのアクションとアクションを設定するアクターのリストを用意しておいて、それを順次動かしています。

startActionで実行中フラグをたてて、endActionでフラグを戻します。

これでアクションが動いている間をチェックするつもりです。

アクターの数が可変でも対応

リストの数は可変にしたいので、上の状態を改変して以下のようなコードになります。

/**
 * 複数のアクターを順次アクションさせるサンプル
 * アクターAがアクションし終わったあとに、アクターBがアクションする
 * リスト可変対応版
 */
public class Scene2dActionAttack2SampleListener extends ApplicationAdapter {

    private Stage stage = null;
    private boolean move = false;

    private Array<ActionAndImage> list = new Array<>();

    @Override
    public void create() {

        /* おなじなので省略 */

        //----------------↑ここまで↑もらうデータの想定------------------------

        if (list.size == 0) return;    //0個だったら何もしない

        Action endAction = (Actions.run(new Runnable() {
            public void run () { move = false;}
        }));

        Action startAction = Actions.run(new Runnable() {
            public void run () { move = true;}
        });

        Action chainAction = null;
        if (list.size != 1) {
            ActionAndImage obj = list.pop();
            chainAction = chainAction( obj.action, endAction, obj.image );
        }

        SequenceAction seq = Actions.sequence();
        ActionAndImage first = list.pop();
        seq.addAction(startAction);
        seq.addAction(first.action);

        if (null == chainAction) {
            seq.addAction(endAction);
        } else {
            seq.addAction(chainAction);
        }
        first.image.addAction(seq);

    }

    /**
    * @param origin もともと設定する想定のアクション
    * @param chain ほかのアクターのアクションとつなぐためのアクション
    * @param image 設定対象のアクター
    * @return
    */
    private Action chainAction (Action origin, Action chain, Image image) {
        Action chainAction =  Actions.run(new Runnable() {
            public void run () {
                SequenceAction seq = Actions.sequence();
                seq.addAction(origin);
                seq.addAction(chain);
                image.addAction(seq);

            }
        });

        if (1 == list.size) {
            return chainAction;
        } else {  //listがひとつになるまで繰り返す
            ActionAndImage obj = list.pop();
            return chainAction(obj.action, chainAction, obj.image );
        }
    }

    class ActionAndImage{/* 同じなので省略 */ }

    @Override
    public void render() {/* 同じなので省略 */}

    @Override
    public void dispose() {/* 同じなので省略 */}
}