Cocos2d-x 开发者指南04:动作
动作概述
动作即 Action,其主要用途是改变 Node 对象的属性。Action 对象会随着时间的推移改变 Node 的属性,任何以 Node 为基类的对象都能够执行动作。
例如,我们可以在一个时间段内将 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);
By 和 To 的区别
每个动作通常都有 By 和 To 两个状态,这是因为它们执行的结果不同。By 表示相对操作,即基于节点的当前状态进行改变;而 To 表示绝对操作,它不考虑节点的当前状态。下面通过一个具体的例子来说明:
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 + 500 move = x is now 700 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()));
auto seq = Sequence::create(moveBy, delay, moveTo, nullptr);
mySprite->runAction(seq);
基本动作以及如何执行动作
基本动作通常是单一的动作,仅完成一个目标。下面介绍几种常见的基本动作:
移动(Move)
在规定的时间内移动 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);
旋转(Rotate)
在 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);
缩放(Scale)
在两秒内对 Node 进行缩放操作。
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, 5.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, 5.0f, 3.0f);
mySprite->runAction(scaleTo);
淡入淡出
FadeIn 用于修改透明度的值(范围在 0 - 255 之间),实现淡入效果,与之相反的动作是 FadeOut(淡出)。
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);
色调(Tint)
Tint 可以使节点的 NodeRGB 从当前值改变到用户设置的值。
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);
动画(Animate)
通过 Animate 可以轻松实现精灵的帧动画效果,只需简单地每隔一段时间替换显示帧即可。以下是一个示例:
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));
由于很难用文字描述一个动画,建议运行《开发者指南示例》中动作部分的代码来查看动作效果。
减速(Easing)
Easing 可以使用给定的加速度使动画更加顺畅。需要注意的是,无论速度快慢,ease 动作总是同时开始和结束。在游戏中,如果想模拟一些物理特效,又不想过度且麻烦地使用多个非常基本的动作,那么 Ease 动作是一个很好的选择。此外,它也是给菜单和按钮添加动画的不错方法。
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, Director::getInstance()->getVisibleSize().height - mySprite->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_back, delay->clone(), nullptr);
// run the sequence and repeat forever.
mySprite->runAction(RepeatForever::create(seq1));
运行“开发者指南示例”中的这部分代码可以查看动作效果。
序列动作以及如何执行它
Sequence 可以使一系列的动作对象按顺序执行,这一系列对象可以是任意数量的动作对象、函数,甚至是另外一个序列动作。在 Cocos2d-x 中,有一个 CallFunc 对象,它允许我们创建一个 function() 函数并传递到 Sequence 中执行,这样就可以在序列动作对象中添加自定义的函数功能,而不局限于 Cocos2d-x 中提供的动作对象。
以下是一个序列动作的例子:
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);
Sequence 对象会按顺序执行以下动作:Jump -> callbackJump -> Rotate -> callbackRotate。运行“开发者指南示例”中的代码可以查看动作效果。
Spawn
Spawn 与 Sequence 类似,但不同的是,使用 Spawn 时所有动作会同时执行。我们可以有多个动作对象,甚至可以嵌套其他 Spawn 对象。
Spawn 的作用与运行多个连续的 runAction() 语句所产生的结果相同,但使用 Spawn 的优点在于可以将其放到一个序列中,从而实现特定的效果,而 runAction() 无法做到这一点。将 Spawn 和 Sequence 结合使用可以实现很多强大的功能。
以下是一个例子:
// 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);
// running the above Actions with Spawn.
auto mySpawn = Spawn::createWithTwoActions(moveBy, fadeTo);
mySprite->runAction(mySpawn);
// running the above Actions with consecutive runAction() statements.
mySprite->runAction(moveBy);
mySprite->runAction(fadeTo);
两个方式产生的效果相同,但 Spawn 可以在 Sequence 中使用。以下是一个结合 Spawn 和 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);
运行“开发者指南示例”中的代码可以查看动作效果。
Reverse(逆序)
Reverse 的作用如其名,当运行一系列动作时,可以调用 reverse() 函数使动作逆序执行。需要注意的是,这不仅仅是简单的逆序运行,实际上还会将原始的 Sequence 和 Spawn 的属性也转换为逆序。
以下是一个使用 Reverse 的例子:
// 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
mySprite->runAction(sequence);
// reverse it
mySprite->runAction(sequence->reverse());
大部分 Action 和 Sequence 对象都是可逆的。虽然 reverse() 使用起来简单,但逻辑上并不简单,不过 Cocos2d-x 已经为我们处理了这些复杂的逻辑。