cocos2d-xのrunActionについて

今年の春〜夏頃からcocos2d-xを本格的にはじめることになりました。
そもそもC++11の知識が必要であったりAndroid NDKで一部ビルドが通らなかったり、、
いろいろなことがありましたが一番つまづいたcocos2d-xのアニメーションについて書きます。


## その1 いくつかのActionを呼び出す時の話

３つのスプライトを動かしたい場合、同時に動かしたい場合、初心者の時に
以下のように記述していました。

~~~1.1
auto circle = Sprite::create("circle.png");
auto triangle = Sprite::create("triangle.png");
auto square = Sprite::create("square.png");

this->addChild(circle);
this->addChild(triangle);
this->addChild(square);

circle->runAction(MoveTo::create(0.3, Vec2(100, 100)));
triangle->runAction(MoveTo::create(0.3, Vec2(100, 100)));
square->runAction(MoveTo::create(0.3, Vec2(100, 100)));

~~~

が、、
ActionをとめたいときやAction完了のcallbackを取りたい場合、
上記の書き方はあまり適してはいませんでした。
上記のようなActionをしたい場合は以下のようにも書くことができます。

~~~1.2
this->runAction(Spawn::create(TargetedAction::create(circle, MoveTo::create(0.3, Vec2(100, 100)),
                              TargetedAction::create(triangle, MoveTo::create(0.3, Vec2(100, 100)),
                              TargetedAction::create(square, MoveTo::create(0.3, Vec2(100, 100)),
                              nullptr));
~~~

1.2の書き方だとアニメーションを止めたい場合、this->stopAllActions()で一括で止めることができます。
逆にその2ですでにthis->runAction()を行っている場合、Actionが上書きされてしまいます。

TargetedActionはとても有用でActionの可読性があがるだけでなく、
どのNodeがどのActionを管理しているのか意識することができます。

## その２ いくつかのActionを呼び出される時の話

以下の様な場合がありました。

~~~
・エフェクトAを再生する
・エフェクトAのアクション完了時にエフェクトBとCを同時再生する
・エフェクトBかCのアクション完了時にエフェクトDを再生する
~~~

~~~
・ここのエフェクトだけちょっとスロー再生で
~~~


このときにエフェクトのクラスをどう書くかかを迷ったのですが、
もろもろあって以下の点をおさえるとすっきりしました。(個人の感想です)

・そのクラスからActionの処理を取得できること
・そのクラスからActionの処理を投げっぱなしにできること
・durationは外出し

具体的にはこんなかんじのを書くと

~~~HogeEffect.cpp
：

FiniteAction* HogeEffect::getPlayAction(float duration)
{
	return TargetedAction::create(this, MoveTo::create(duration, Vec2(100, 100)),
}

void HogeEffect::runPlayAction(float duration)
{
	this->runAction(getPlayAction(duration));
}
~~~

以下のように呼び出すことができます

~~~effect

this->runAction(Sequence::create(effectA->getPlayAction(0.1f),
						         Spawn::create(effectB->getPlayAction(0.2f),
                                               effectC->getPlayAction(0.3f),
                                               nullptr),
                                 effectD->getPlayAction(0.4f),
                                 nullptr));
~~~

Actionには多くの派生クラスがありますが、
個人的にはSpawn、Sequenceをよく使うのでFiniteAction*で定義していました。

![スクリーンショット 2014-12-18 2.52.46.png](https://qiita-image-store.s3.amazonaws.com/0/19692/b93e8482-ed14-a35c-ac0d-750a979b00d1.png)

http://www.cocos2d-x.org/reference/native-cpp/V3.3rc0/db/d61/classcocos2d_1_1_action.html
http://www.cocos2d-x.org/reference/native-cpp/V3.3rc0/dc/df1/classcocos2d_1_1_spawn.html
http://www.cocos2d-x.org/reference/native-cpp/V3.3rc0/d7/d30/classcocos2d_1_1_sequence.html

## その3 他の言語などと比べた話 (+ラッパーつくりました)

| 言語/ツール | 名前 | 特徴 |
|:-----------|:------------|:------------|
| swift | SKActionクラス全般 | cocos2dとほぼ同じ |
| Unity標準 | Animation | カーブアニメーション指定。Animator(Mecanim)との連携が強力 |
| Unity Plugin     | iTween | http://itween.pixelplacement.com/index.php |
| ActionScript       | BetweenAS3 | http://www.libspark.org/wiki/BetweenAS3 |
| ActionScript         | Tweener | https://code.google.com/p/tweener/ |

Tweenアニメーションに関して各ツールではだいたい以下のものは標準化されていました。(ざっくりですみません。。)
・移動(絶対位置/相対位置)
・拡大
・回転
・Easingの指定(Quad, Elastic, Backなどの各種に関してIn, InOut, Out)

作成中はEasing早見表が便利だったので貼っておきます。
http://easings.net/ja

ちなみにiTween, BetweenAS3, cocos2d-xそれぞれの書き方が以下のようになっています
(x:100, y:100をQuadOutイージングで0.2secで移動する場合)

[iTween]

~~~iTween
iTween.MoveTo(gameObject, iTween.Hash("x", 100, "y", 100, "time", 0.2f, "easetype", iTween.EaseType.easeOutQuad));
~~~

[BetweenAS3]

~~~BetweenAS3
BetweenAS3.to(frog, {x:100, y:100}, 0.2f, Quad.easeOut);
~~~

[cocos2d-x]

~~~cocos2d-x
this->runAction(EaseQuadraticActionOut::create(MoveTo::create(Point(100, 100), 0.2f));
~~~

・・・

・・・他に比べてEasingの指定方法がちょっとちがうような・・
あと同じ動きを同じ間隔、同じEasing指定で行うときになんども指定しなければならず面倒・・
そして微妙にcocos2d-xのEasing名称が長い・・

そこで！

BetweenAS3ライクなラッパー(Betweenクラス)作りました↓
https://github.com/keidroid/advent2014

こんなかきかた

~~~cocos2d-x(Between)
this->runAction(Between::to(this, "x:100, y:100", 0.2f, Easing::QuadOut));
~~~

ラッパーなしの場合とありの場合、同時再生で回転・拡大を加えるとそれぞれ以下になります

[素で書いた場合]

~~~cocos2d-x
this->runAction(Spawn::create(EaseQuadraticActionOut::create(MoveTo::create(Point(100, 100), 0.2f),
                              EaseQuadraticActionOut::create(RotateTo::create(30, 0.2f),
                              EaseQuadraticActionOut::create(ScaleTo::create(2.0f, 0.2f));
~~~

[ラッパーあり]

~~~cocos2d-x(Between)
this->runAction(Between::to(this, "x:100, y:100, rot:30, scale:2", 0.2f, Easing::QuadOut));
~~~

(ﾟДﾟ)

そもそもアニメーションをスクリプトで記述する機会は今後もあるのかよくわかりませんが
さくっとアニメーション書きたいときに合えば使ってやってください
(自分はFlashをcocos2d-xに移植するために作りました)

----

あしたはネイティブ連携の話！とても楽しみです！