libGDXとはWindows、Linux、Mac、Android、iPhone、HTMLに対応したクロスプラットフォームゲームライブラリです。有名なIngressでも利用されています。欧米ではわりとメジャーどころです。
#アクションとは
アクションとはActorに一定の動きをさせるものです。
非常に便利なのでぜひ使いましょう。
キャラクターを1秒間かけて右方向に60ピクセル動かしたとします。
Actorではactメソッドが毎フレーム呼ばれます。基本60fps程度で動きますので、処理落ち等がなければX座標をずらすだけで動きます。
おおむね以下のような形です。
Image image = new Image(tex){
int count=0;
@Override
public void act(float delta) {
super.act(delta);//必ず呼ぶこと
if(count < 60){
setX(getX() +1);
count++;
}
}
};
しかし、これでは移動する時間を計測するなどやりたいことがかなり直感的ではない。
そこでActionを使う。
#ソース
package test.libgdx;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.scenes.scene2d.Action;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.actions.Actions;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
public class MainListener8 implements ApplicationListener{
TextureAtlas atlas;
Stage stage;
@Override
public void create() {
stage = new Stage();
{
FileHandle fh = Gdx.files.internal("enemy.png");
Texture tex = new Texture(fh);
Image image = new Image(tex);
Action action = Actions.moveBy(60, 0, 1);
image.addAction(action);
image.setPosition(50, 50);
stage.addActor(image);
}
}
@Override
public void resize(int w, int h) {
}
@Override
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl.glClearColor(0, 0, 0, 1);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public void dispose() {
}
//起動部分
public static void main(String[] args) {
new LwjglApplication(new MainListener8());
}
}
ポイントは以下のところです。
Action action = Actions.moveBy(60, 0, 1);
image.addAction(action);
Actionを生成してActorにaddActionメソッドで関連付けるだけ。
Actionは様々なものがあり、newで生成することができますがほとんどはActionsクラスにstaticに用意されていますのでそれを使うだけで済みます。
ここで利用しているのは以下の引数となっています。
Actions.moveBy(Xの移動量, yの移動量, 移動完了するまでの時間)
#順次処理
せっかく右に移動したので元の位置に戻してみましょう。
これもactメソッドなどで処理を行う場合、今右に動いているモードなのか、左に動いているモードなのかの判断などが入りますが、Actionを使うと簡単に書けます。
Image image = new Image(tex);
Action toRight = Actions.moveBy(60, 0, 1);
Action toLeft = Actions.moveBy(-60, 0, 1);
SequenceAction seq = Actions.sequence();
seq.addAction(toRight);
seq.addAction(toLeft);
image.addAction(seq);
SequenceAction は順次処理を行うためのものです。
これにaddしていくとそのアクションが終わった後、次のアクションが呼ばれていきます。
#ループ処理
せっかく元の位置に戻ったのでまた右に移動させる、無限ループさせたいですね。
Action toRight = Actions.moveBy(60, 0, 1);
Action toLeft = Actions.moveBy(-60, 0, 1);
SequenceAction seq = Actions.sequence();
seq.addAction(toRight);
seq.addAction(toLeft);
RepeatAction forever = Actions.forever(seq);//無限ループ
image.addAction(forever);
無限ループ用のもActionsに用意されています。
戻り値の型を見るとわかるとおり、指定回数のループも可能なものです。
たとえば以下のように変更すると、2回だけループします。
RepeatAction forever = Actions.repeat(2, seq);
#並列処理
アクションは順序良く処理したいだけではありません。並列に処理をしたい場合があります。
たとえば、右に移動しながら拡大するとします。そして、左に戻るにつれて拡大率が元に戻るとします。
そういう場合は以下のようなコードになります。
Action toRight = Actions.parallel(
Actions.moveBy(60, 0, 1),
Actions.scaleTo(2, 2, 1)
);
Action toLeft = Actions.parallel(
Actions.moveBy(-60, 0, 1),
Actions.scaleTo(1, 1, 1)
);
SequenceAction seq = Actions.sequence();
seq.addAction(toRight);
seq.addAction(toLeft);
RepeatAction forever = Actions.forever(seq);
image.addAction(forever);
Actions.parallelを利用します。
SequenceActionと同様に生成して、addする方法もありますが、このように可変長引数で設定することが可能です。もちろん、SequenceActionも同様に記述することは可能です。
#加速減速、スイング、バウンドなど
いままでは一定の速度で動いていました。実際は出だしの速度は遅く、だんだんと加速、そして、目的地に着くまえに減速、停止、としたいです。
そういうことももちろん可能です。
Action toRight = Actions.moveBy(300, 0, 1, Interpolation.fade);
Action toLeft = Actions.moveBy(-300, 0, 1, Interpolation.fade);
SequenceAction seq = Actions.sequence();
seq.addAction(toRight);
seq.addAction(toLeft);
RepeatAction forever = Actions.forever(seq);
image.addAction(forever);
ここではわかりやすくするために移動量を300に増やしています。Interpolationがポイントです。
デフォルトでは等速、Interpolation.linear
を設定しているのと同等になります。
fadeと切り替えてみてください。動きが滑らかになった感じがするでしょう。
Action toRight = Actions.moveBy(300, 0, 1, Interpolation.swing);
Action toLeft = Actions.moveBy(-300, 0, 1, Interpolation.swing);
このように書くとスイングします。いったん、移動方向とは少し後ろに下がって勢いをつけて加速、目的地を通り越してから少し戻って目的地に着くというものです。
Action toRight = Actions.moveBy(300, 0, 1, Interpolation.swingOut);
Action toLeft = Actions.moveBy(-300, 0, 1, Interpolation.swingOut);
このように書くと、導入部はそのまま、終端のところだけswingの処理を行います。逆にswingInは導入のところのみにその処理をかけることになります。swing以外のものも同等です。
Action toRight = Actions.moveBy(300, 0, 1, Interpolation.bounceOut);
Action toLeft = Actions.moveBy(-300, 0, 1, Interpolation.bounceOut);
これでバウンドします。固いものにぶち当たったかのような動きになります。
Action、Interpolationともに自作することも可能なので好きなような動きを作ることが可能です。
#アクションの削除
Action toRight = Actions.moveBy(300, 0, 1, Interpolation.bounceOut);
Action toLeft = Actions.moveBy(-300, 0, 1, Interpolation.bounceOut);
Action end = Actions.run(new Runnable() {
@Override
public void run() {
image.clearActions();
}
});
SequenceAction seq = Actions.sequence();
seq.addAction(toRight);
seq.addAction(toLeft);
seq.addAction(end);
RepeatAction forever = Actions.forever(seq);
image.addAction(forever);
カスタムなアクションを作るまでもない、特殊な処理を入れたい場合はActions.runメソッドでRunnableインターフェースを実装したものを実行することが可能です。
本来無限ループするはずのアクションですが、途中で止まっているのがわかると思います。
Actor#clearActions()
はすべてのアクションを破棄するメソッドです。指定したアクションだけを削除する場合は Actor#removeAction(アクション)
を利用します。
ちなみにActionの中で決まった動きをする場合、Actions.removeAction()というものもありますので、それを使ってもかまいません。もっとも、決まりきった動きの場合今回のように自分を止めるために利用することはあまりないかと思いますが。
他のActorを操作する場合もそのActorが現在どのような状態にあるか走りませんので、直接removeActionなどを呼ぶというよりは、特定の状態にしたいメソッドをそのActorに追加、実装し、他のActorからはそのメソッドを呼ぶとするほうがいいですね。
複雑な状態を管理したい場合は状態管理専用に集中管理できるActorを作るのもよいでしょう。