Action
オブジェクトとは、その名前が示す通りのものです。
Node
のプロパティの変更を行います。
Action
オブジェクトを使うことで、 Node
のプロパティを時間経過ごとに変化させることができます。
Node
クラスを継承したオブジェクトは全て、自身に対して実行できる Action
オブジェクトを持つことができます。
例として、 Sprite
をある位置から別の位置へ、時間経過ごとに移動させることができます。
MoveTo
アクションとMoveBy
アクションの例:
// Move sprite to position 50,10 in 2 seconds.
auto moveTo = MoveTo::create(2, Vec2(50, 10));
mySprite1->runAction(moveTo);
// Move sprite 20 points to right in 2 seconds
auto moveBy = MoveBy::create(2, Vec2(20,0));
mySprite2->runAction(moveBy);
JavaScript
// Move sprite to position 50,10 in 2 seconds.
var moveTo = new cc.MoveTo(2, cc._p(50, 10));
mySprite1.runAction(moveTo);
// Move sprite 20 points to right in 2 seconds
var moveBy = new cc.MoveBy(2, cc._p(20,0));
mySprite2.runAction(moveBy);
各Action
は Byバージョン とTo バージョンを持っていることに気づいたでしょう。
なぜでしょうか?理由は、それらの実行結果が異なったものになるからです。
By は現在のNode
の状態を基準とした相対値を指定します。
Toアクションには絶対値を指定します。つまり、現在の Node
の状態は考慮しません。
具体的な例を見てみましょう:
auto mySprite = Sprite::create("mysprite.png");
mySprite->setPosition(Vec2(200, 256));
// MoveBy - lets move the sprite by 500 on the x axis over 2 seconds
// MoveBy is relative - since x = 200 + 200 move = x is now 400 after the move
auto moveBy = MoveBy::create(2, Vec2(500, mySprite->getPositionY()));
// MoveTo - lets move the new sprite to 300 x 256 over 2 seconds
// MoveTo is absolute - The sprite gets moved to 300 x 256 regardless of
// where it is located now.
auto moveTo = MoveTo::create(2, Vec2(300, mySprite->getPositionY()));
// Delay - create a small delay
auto delay = DelayTime::create(1);
auto seq = Sequence::create(moveBy, delay, moveTo, nullptr);
mySprite->runAction(seq);
JavaScript
var mySprite = new cc.Sprite(res.mysprite_png);
mySprite.setPosition(cc._p(200, 256));
// MoveBy - lets move the sprite by 500 on the x axis over 2 seconds
// MoveBy is relative - since x = 200 + 200 move = x is now 400 after the move
var moveBy = new cc.MoveBy(2, cc._p(500, mySprite.y));
// MoveTo - lets move the new sprite to 300 x 256 over 2 seconds
// MoveTo is absolute - The sprite gets moved to 300 x 256 regardless of
// where it is located now.
var moveTo = new cc.MoveTo(2, cc._p(300, mySprite.y));
// Delay - create a small delay
var delay = new cc.DelayTime(1);
var seq = new cc.Sequence(moveBy, delay, moveTo);
mySprite.runAction(seq);
基本的なActionは通常単一のアクションを行い、一つの目的を達成します。 それではいくつか例を見てみましょう:
設定した時間経過ごとに Node
を 移動させます。
auto mySprite = Sprite::create("mysprite.png");
// Move a sprite to a specific location over 2 seconds.
auto moveTo = MoveTo::create(2, Vec2(50, 0));
mySprite->runAction(moveTo);
// Move a sprite 50 pixels to the right, and 0 pixels to the top over 2 seconds.
auto moveBy = MoveBy::create(2, Vec2(50, 0));
mySprite->runAction(moveBy);
JavaScript
var mySprite = new cc.Sprite(res.mysprite_png);
// Move a sprite to a specific location over 2 seconds.
var moveTo = new cc.MoveTo(2, cc._p(50, 0));
mySprite.runAction(moveTo);
// Move a sprite 50 pixels to the right, and 0 pixels to the top over 2 seconds.
var moveBy = new cc.MoveBy(2, cc._p(50, 0));
mySprite.runAction(moveBy);
2秒間に Node
を時計回りに回転させます。
auto mySprite = Sprite::create("mysprite.png");
// Rotates a Node to the specific angle over 2 seconds
auto rotateTo = RotateTo::create(2.0f, 40.0f);
mySprite->runAction(rotateTo);
// Rotates a Node clockwise by 40 degree over 2 seconds
auto rotateBy = RotateBy::create(2.0f, 40.0f);
mySprite->runAction(rotateBy);
JavaScript
var mySprite = new cc.Sprite(res.mysprite_png);
// Rotates a Node to the specific angle over 2 seconds
var rotateTo = new cc.RotateTo(2.0, 40.0);
mySprite.runAction(rotateTo);
// Rotates a Node clockwise by 40 degree over 2 seconds
var rotateBy = new cc.RotateBy(2.0, 40.0);
mySprite.runAction(rotateBy);
2秒間で Node
を10倍に拡大します。
auto mySprite = Sprite::create("mysprite.png");
// Scale uniformly by 3x over 2 seconds
auto scaleBy = ScaleBy::create(2.0f, 3.0f);
mySprite->runAction(scaleBy);
// Scale X by 5 and Y by 3x over 2 seconds
auto scaleBy = ScaleBy::create(2.0f, 3.0f, 3.0f);
mySprite->runAction(scaleBy);
// Scale to uniformly to 3x over 2 seconds
auto scaleTo = ScaleTo::create(2.0f, 3.0f);
mySprite->runAction(scaleTo);
// Scale X to 5 and Y to 3x over 2 seconds
auto scaleTo = ScaleTo::create(2.0f, 3.0f, 3.0f);
mySprite->runAction(scaleTo);
JavaScript
var mySprite = new cc.Sprite(res.mysprite_png);
// Scale uniformly by 3x over 2 seconds
var scaleBy = new cc.ScaleBy(2.0, 3.0);
mySprite.runAction(scaleBy);
// Scale X by 5 and Y by 3x over 2 seconds
var scaleBy = new cc.ScaleBy(2.0, 3.0, 3.0);
mySprite.runAction(scaleBy);
// Scale to uniformly to 3x over 2 seconds
var scaleTo = new cc.ScaleTo(2.0, 3.0);
mySprite.runAction(scaleTo);
// Scale X to 5 and Y to 3x over 2 seconds
var scaleTo = new cc.ScaleTo(2.0, 3.0, 3.0);
mySprite.runAction(scaleTo);
Node
をフェードイン/フェードアウトさせます。
フェードインでは不透明度を0から255へ変更します。このアクションの 反対 が フェードアウト
です。
auto mySprite = Sprite::create("mysprite.png");
// fades in the sprite in 1 seconds
auto fadeIn = FadeIn::create(1.0f);
mySprite->runAction(fadeIn);
// fades out the sprite in 2 seconds
auto fadeOut = FadeOut::create(2.0f);
mySprite->runAction(fadeOut);
JavaScript
var mySprite = new cc.Sprite(res.mysprite_png);
// fades in the sprite in 1 seconds
var fadeIn = new cc.FadeIn(1.0);
mySprite.runAction(fadeIn);
// fades out the sprite in 2 seconds
var fadeOut = new cc.FadeOut(2.0);
mySprite.runAction(fadeOut);
Tint
では、 NodeRGB
プロトコルを実装したNode
を現在の色合いからカスタムした色合いへ変更します。
auto mySprite = Sprite::create("mysprite.png");
// Tints a node to the specified RGB values
auto tintTo = TintTo::create(2.0f, 120.0f, 232.0f, 254.0f);
mySprite->runAction(tintTo);
// Tints a node BY the delta of the specified RGB values.
auto tintBy = TintBy::create(2.0f, 120.0f, 232.0f, 254.0f);
mySprite->runAction(tintBy);
JavaScript
var mySprite = new cc.Sprite(res.mysprite_png);
// Tints a node to the specified RGB values
var tintTo = new cc.TintTo(2.0, 120.0, 232.0, 254.0);
mySprite.runAction(tintTo);
// Tints a node BY the delta of the specified RGB values.
var tintBy = new cc.TintBy(2.0, 120.0, 232.0, 254.0);
mySprite.runAction(tintBy);
Animate
を使用すると、 Sprite
オブジェクトで簡単なフリップブック アニメーションを行うことができます。
これは単に、アニメーション時間の間に設定した間隔ごとに 表示フレームを切り替えるものです。
それでは、以下の例について考えてみましょう:
auto mySprite = Sprite::create("mysprite.png");
// now lets animate the sprite we moved
Vector<SpriteFrame*> animFrames;
animFrames.reserve(12);
animFrames.pushBack(SpriteFrame::create("Blue_Front1.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Front2.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Front3.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Left1.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Left2.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Left3.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Back1.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Back2.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Back3.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Right1.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Right2.png", Rect(0,0,65,81)));
animFrames.pushBack(SpriteFrame::create("Blue_Right3.png", Rect(0,0,65,81)));
// create the animation out of the frames
Animation* animation = Animation::createWithSpriteFrames(animFrames, 0.1f);
Animate* animate = Animate::create(animation);
// run it and repeat it forever
mySprite->runAction(RepeatForever::create(animate));
JavaScript
var mySprite = new Sprite(res.mysprite_png);
// now lets animate the sprite we moved.
var animFrames;
animFrames.push(new cc.SpriteFrame(res.Blue_Front1_png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Front2.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Front3.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Left1.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Left2.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Left3.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Back1.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Back2.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Back3.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Right1.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Right2.png), cc.Rect(0,0,65,81)));
animFrames.push(new cc.SpriteFrame(res.Blue_Right3.png), cc.Rect(0,0,65,81)));
// create the animation out of the frames
var animation = cc.Animation.createWithSpriteFrames(animFrames, 0.1);
var animate = new cc.Animate(animation);
// run it and repeat it forever
mySprite.runAction(cc.RepeatForever(animate));
アニメーションについて言葉で説明するのは難しいので、プログラマーガイドサンプルのコード例を実行して動きを見てください!
イージング とはアニメーションに加速度を指定して動きをスムーズにすることです。 A few things to keep in mind is that regardless of speed, ease actions always start and finish at the same time. Ease アクションは、ゲーム中で物理演算を 擬似再現する のに良い方法です! Perhaps you want a few simulated physics effects but don't want the overhead and complexity of adding it all for a few very basic actions. もう一つの良い例は、メニューとボタンです。
以下は、一般的なイージング機能をグラフに表したものです:
Cocos2d-x では上記グラフのほとんどのイージング機能をサポートしています。
それらも簡単に実装できます。 それでは、特定のユースケースについて見てみましょう。
Sprite
オブジェクトを画面の上から落としてバウンドさせます。
// create a sprite
auto mySprite = Sprite::create("mysprite.png");
// create a MoveBy Action to where we want the sprite to drop from.
auto move = MoveBy::create(2, Vec2(200, dirs->getVisibleSize().height -
newSprite2->getContentSize().height));
auto move_back = move->reverse();
// create a BounceIn Ease Action
auto move_ease_in = EaseBounceIn::create(move->clone() );
// create a delay that is run in between sequence events
auto delay = DelayTime::create(0.25f);
// create the sequence of actions, in the order we want to run them
auto seq1 = Sequence::create(move_ease_in, delay, move_ease_in_back,
delay->clone(), nullptr);
// run the sequence and repeat forever.
mySprite->runAction(RepeatForever::create(seq1));
JavaScript
プログラマーガイドサンプルのコード例を実行して動きを見てください!
Sequenceとは、連続して実行される Action
オブジェクトの連なりのことです。
これには任意の数の Action
オブジェクト、Functions、別のSequence
でさえ設定できます。
Functionsとは? はい! Cocos2d-x には、function()を作成してそれをSequence
に渡して実行できるCallFunc
オブジェクトがあります。
これを使うことで、 Cocos2d-xが用意しているAction
オブジェクト以外にもあなた独自のアクションをSequence
オブジェクトに追加することができます。
実行時のSequence
は以下のようになります:
auto mySprite = Sprite::create("mysprite.png");
// create a few actions.
auto jump = JumpBy::create(0.5, Vec2(0, 0), 100, 1);
auto rotate = RotateTo::create(2.0f, 10);
// create a few callbacks
auto callbackJump = CallFunc::create([](){
log("Jumped!");
});
auto callbackRotate = CallFunc::create([](){
log("Rotated!");
});
// create a sequence with the actions and callbacks
auto seq = Sequence::create(jump, callbackJump, rotate, callbackRotate, nullptr);
// run it
mySprite->runAction(seq);
JavaScript
では、この Sequence
アクションは何をしているのでしょうか?
これは、以下のアクションを続けて実行しています。:
Jump -> callbackJump() -> Rotate -> callbackRotate()
プログラマーガイドサンプルのコード例を実行して動きを見てください!
全てのアクションが同時に実行されるということを除けば、Spawn は Sequence
と非常に似ています。
Spawnには望む数のAction
オブジェクトや、他のSpawn
オブジェクトでさえ設定することができます!
Spawn
を使うと、複数回連続して runAction()を実行するのと同じ結果になります。
しかしSpawn
の本領は、 Sequence
に設定することでこれまでできなかった特別なエフェクトを実現できるということです。
Spawn
とSequence
を組み合わせることで、非常に強力な効果を発揮します。
例を示します:
C++// create 2 actions and run a Spawn on a Sprite
auto mySprite = Sprite::create("mysprite.png");
auto moveBy = MoveBy::create(10, Vec2(400,100));
auto fadeTo = FadeTo::create(2.0f, 120.0f);
以下のコードでは Spawn
を使っています:
// running the above Actions with Spawn.
auto mySpawn = Spawn::createWithTwoActions(moveBy, fadeTo);
mySprite->runAction(mySpawn);
そして以下のコードでは runAction() を連続して実行しています:
C++// running the above Actions with consecutive runAction() statements.
mySprite->runAction(moveBy);
mySprite->runAction(fadeTo);
両方とも結果は同じです。However, one can use Spawn
in a Sequence
.
以下フローチャートではこの流れを視覚的に示しています:
// create a Sprite
auto mySprite = Sprite::create("mysprite.png");
// create a few Actions
auto moveBy = MoveBy::create(10, Vec2(400,100));
auto fadeTo = FadeTo::create(2.0f, 120.0f);
auto scaleBy = ScaleBy::create(2.0f, 3.0f);
// create a Spawn to use
auto mySpawn = Spawn::createWithTwoActions(scaleBy, fadeTo);
// tie everything together in a sequence
auto seq = Sequence::create(moveBy, mySpawn, moveBy, nullptr);
// run it
mySprite->runAction(seq);
プログラマーガイドサンプルのコード例を実行して動きを見てください!
Cloneとはまさに名前通りのものです。Action
の用意ができたら、 clone()
を使うことでそのアクションを複数のNode
オブジェクトに適用することができます。
なぜクローンを作らなければならないのでしょうか? 良い質問です。
Action
オブジェクトは 内部ステータスを持っています。
アクションが実行されると、それらは実際に Node
オブジェクトのプロパティを変更します。
clone()
を使用していないと、 Node
に他のNode
と重複したAction
を適用してしまいます。
Action
のプロパティが現在どのような設定になっているかを確実に知ることはできないので、これは予期せぬ結果を生み出すことになります。
それでは、例を使って細かく見てみましょう。あなたは heroSpriteを用意し、それの位置が(0,0)とします。そして以下の Action
を実行します:
MoveBy::create(10, Vec2(400,100));
上記コードでは heroSpriteを(0,0)から(400, 100)まで10秒かけて動かします。
これで heroSprite の持つ新しい位置情報は (400, 100) となり、さらに重要なことにAction
も内部ステータスにその位置情報を持つのです。
さて、あなたが(200, 200)の位置情報を持つemenySprite を用意したとします。
このenemySpriteに前述と同じアクションを適用したとします:
MoveBy::create(10, Vec2(400,100));
するとあなたの予想に反して (800, 200) の位置へ移動してしまうでしょう。
なぜだか分かりますか? それはMoveBy
を実行した時に Action
が既にスタート地点の 内部ステータスを持っていたためです。
Action
の クローンを作成することでこれを防げます。
クローンによって重複していないAction
を Node
に適用することができます。
それではまず最初に間違った例を見てみましょう。
C++// create our Sprites
auto heroSprite = Sprite::create("herosprite.png");
auto enemySprite = Sprite::create("enemysprite.png");
// create an Action
auto moveBy = MoveBy::create(10, Vec2(400,100));
// run it on our hero
heroSprite->runAction(moveBy);
// run it on our enemy
enemySprite->runAction(moveBy); // oops, this will not be unique!
// uses the Actions current internal state as a starting point.
clone()を使って正しくしました!:
C++// create our Sprites
auto heroSprite = Sprite::create("herosprite.png");
auto enemySprite = Sprite::create("enemysprite.png");
// create an Action
auto moveBy = MoveBy::create(10, Vec2(400,100));
// run it on our hero
heroSprite->runAction(moveBy);
// run it on our enemy
enemySprite->runAction(moveBy->clone()); // correct! This will be unique
Reverse も正に名前通りのものです。一連のアクションを実行する場合には、 reverse()
を呼び出して逆の順序でアクションを実行することができます。
Otherwise known as, backwards.
ですが、単に逆の順序で Action
の実行されるだけではありません。
reverse()
を呼び出すと、 Sequence
やSpawn
のオリジナルのプロパティも逆の値で操作されるのです。
上記Spawn
の例を使用します。 Reverse処理をするのは簡単です。
// reverse a sequence, spawn or action
mySprite->runAction(mySpawn->reverse());
ほとんどの Action
オブジェクトと Sequence
オブジェクトが逆になりました!
使用するのは簡単ですが、何が起こっているのかを理解するようにしましょう。:
C++// create a Sprite
auto mySprite = Sprite::create("mysprite.png");
mySprite->setPosition(50, 56);
// create a few Actions
auto moveBy = MoveBy::create(2.0f, Vec2(500,0));
auto scaleBy = ScaleBy::create(2.0f, 2.0f);
auto delay = DelayTime::create(2.0f);
// create a sequence
auto delaySequence = Sequence::create(delay, delay->clone(), delay->clone(),
delay->clone(), nullptr);
auto sequence = Sequence::create(moveBy, delay, scaleBy, delaySequence, nullptr);
// run it
newSprite2->runAction(sequence);
// reverse it
newSprite2->runAction(sequence->reverse());
一体何が起こったのでしょうか? 手順を一覧にして書き出すと理解しやすいです:
reverse()
を使うのは簡単ですが、その内部で動いているロジックは簡単ではないことが分かります。 Cocos2d-x は多くの処理を全て行います!