当前位置:   article > 正文

Cocos2d-x动作(Action)详解

Cocos2d-x动作(Action)详解

Cocos2d-x动作(Action)详解

  动作(Action)的功能就和字面含义一样,它通过改变一个Node对象的属性,让它表现出某种动作。动作对象能实时的改变Node的属性,任何一个对象只要它是Node的子类都能被改变。比如,你能通过动作对象把一个精灵从一个位置移动到另外一个位置。

  通过MoveTo和MoveBy方法:

//在两秒内,移动一个精灵到(50,10)的位置
auto moveTo = MoveTo::create(2,Vec2(50,10));
mySprite1->runAction(moveTo);

//在两秒内,相对于当前对象的位置移动到右侧20的位置,横坐标不变
auto moveBy = moveBy::create(2,Vec2(20,0));
mySprite2->runAction(moveBy);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Action类

效果图

  • 延时类——ActionInterval
    • 基本动作
    • 重复执行——Repeat和RepeatForever
    • 序列——Sequence和Spawn
    • 帧动画——Animate
  • 瞬时类——ActionInstant
    • 基本动作
    • 回调函数包装器

Action类的使用

  • 创建动作
Action* moveAction = MoveBy::create(2.f,Vec2(visibleSize.width/2,0));
  • 1

里氏替换原则:任何基类可以出现的地方,子类一定可以出现。

  • 执行动作
sprite->runAction(moveAction);
  • 1

注意:同一个动作不能执行两次!!

  • 停止动作
sprite->stopActionByTag(1000);
sprite->stopAllAcitons();
  • 1
  • 2

By和To 的区别

  你能注意到,每一个动作都会两个方法ByTo。两种方法方便你在不同的情况使用,By算的上是相对于节点对象的当前位置,To算得上是绝对位置,不考虑当前节点对象在哪。如果你想动作的表现是相对于Node当前位置的,就用By,相对的想让动作的表现是按照坐标的绝对位置就用To,看一个例子:

auto MySprite = Sprite::create("mysprite.png");
mySprite->setPosition(Vec(200,256));

//MoveBy - 让精灵在两秒内,在原来x的位置移动500的距离,纵坐标不变
//MoveBy是相对的,原来的x为200,加上移动的距离500,即移动至x=700的位置
auto moveBy = MoveBy::create(2,Vec2(500,mySprite->getPositionY()));

//MoveTo - 让精灵在两秒内直接移动到x为300,y为256的位置
//MoveTo是绝对的,无论如何都会移动(300,256)的位置,不管当前精灵在任何位置
auto MoveTo = MoveTo::create(2,Vec2(300,mySprite->getPositionY()));


//Delay - 制造一小会儿的延迟
auto delay = DelayTime::create(1);

//执行以上三个动作
auto seq = Sequence::create(moveBy,delay,moveTo,nullptr);

mySprite->runAction(seq);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

效果图

游戏中常见的动作

  • Cocos2d中常见的Action

效果图

基本动作

效果图

移动

  使用MoveToMoveBy完成节点对象在一个设置的时间后移动。

auto mySprite = Sprite::create("mysprite.png");

//在2秒内,将精灵移动到特定的位置
auto moveTo = MoveTo::create(2,Vec2(50,0));

mySprite->runAction(moveTo);

//在2秒内,将精灵移动到当前位置的右侧50像素位置,纵坐标不变
auto moveBy = MoveBy::create(2,Vec2(50,0));

mySprite->runAction(moveBy);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

效果图

旋转

  使用RotateToRotateBy完成节点对象在设置的时间后顺时针旋转指定角度。

auto mySprite = Sprite::create("mysprite.png");

//在2秒内旋转特定的角度
auto rotateTo = RotateTo::create(2.0f,40.0f);
mySprite->runAction(rotateTo);

//在2秒内,基于当前的角度,顺时针旋转40度
auto rotateBy = RotateBy::create(2.0f,40.0f);
mySprite->runAction(rotateBy);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

效果图

缩放

  使用ScaleByScaleTo完成节点对象的比例缩放。

auto mySprite = Sprite::create("mysprite.png");

//在2秒内,基于当前的大小,均匀的放大3倍
auto scaleBy = ScaleBy::create(2.0f,3.0f);
mySprite->runAction(scaleBy);

//在2秒内,基于当前大小,x轴缩放到原来的3倍,y轴缩放到原来的3倍
auto scaleBy = ScaleBy::create(2.0f,3.0f,3.0f);
mySprite->runAction(scaleBy);

//在2秒内,均匀缩放绝对大小的3倍
auto scaleTo = ScaleTo::create(2.0f,3.0f);
mySprite->runAction(scaleTo);

//在2秒内,x轴缩放绝对大小的3倍,y轴缩放绝对大小的3倍
auto scaleTo = ScaleTo::create(2.0f,3.0f,3.0f);
mySprite->runAction(scaleTo);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

效果图

淡入淡出

  使用FadeInFadeOut完成节点对象的淡入,淡出。FadeIn修改节点对象的透明度属性,从完全透明到完成不透明,FadeOut相反。

auto mySprite = Sprite::create("mysprite.png");

//在1秒内让精灵消失
auto fadeIn = FadeIn::create(1.0f);
mySprite->runAction(fadeIn);

//在2秒内让精灵显示
auto fadeOut = FadeOut::create(2.0f);
mySprite->runAction(fadeOut);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

效果图

色彩混合

  使用TintToTintBy,将一个实现了NodeRGB协议的节点对象进行色彩混合。

auto mySprite = Sprite::create("mysprite.png");

//用指定的RGH值表示一个节点
auto tintTo = TintTo::create(2.0f,120.0f,232.0f,254.0f);
mySprite->runAction(tintTo);

//对原有RGB值进行增量来表示该节点
auto tintBy = TintBy::create(2.0f,120.0f,232.0f,254.0f);
mySprite->runAction(tintBy);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

效果图

帧动画

  使用Animate对象可以很容易的通过每隔一个短暂时间进行图像替代的方式,实现一个翻页效果。

示例1:

auto mySprite = Sprite::create("mysprite.png");

//创建动画框架
Vector<SpriteFrame*> animFrames;
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_Front4.png"),Rect(0,0,65,81));
animFrames.pushBack(SpriteFrame::create("Blue_Front5.png"),Rect(0,0,65,81));
animFrames.pushBack(SpriteFrame::create("Blue_Front6.png"),Rect(0,0,65,81));
animFrames.pushBack(SpriteFrame::create("Blue_Front7.png"),Rect(0,0,65,81));
animFrames.pushBack(SpriteFrame::create("Blue_Front8.png"),Rect(0,0,65,81));
animFrames.pushBack(SpriteFrame::create("Blue_Front9.png"),Rect(0,0,65,81));
animFrames.pushBack(SpriteFrame::create("Blue_Front10.png"),Rect(0,0,65,81));
animFrames.pushBack(SpriteFrame::create("Blue_Front11.png"),Rect(0,0,65,81));
animFrames.pushBack(SpriteFrame::create("Blue_Front12.png"),Rect(0,0,65,81));

//利用动画框架创建动画
Animation* animation = Animation::createWithSpriteFrames(animFrams,0.1f);
Animate* animate = Animate::create(animation);

//循环反复执行动画
mySprite->runAction(RepeatForever::create(animate));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("hlb1.plist");
	Sprite* sprite1 = Sprite::createWithSpriteFrameName("hlb1_1.png");
	this->addChild(sprite1);
	sprite1->setPosition(Vec2(visibleSize.width / 2, visibleSize.height / 2));

	//动画实例
	Vector<SpriteFrame*> images;
	for (int i = 1; i <= 15; i++)
		images.pushBack(SpriteFrameCache::getInstance()->getSpriteFrameByName("hlb1_" + Value(i).asString() + ".png"));
	Animation* _animation = Animation::createWithSpriteFrames(images, 2.0f / 23);
	sprite1->runAction(RepeatForever::create(Animate::create(_animation)));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

效果图

示例2:

  • Animate

    • 加载图集资源

      auto cache = SpriteFrameCache::getInstance();
      cache->addSpriteFrameWithFile("vampire/vampire.plist");
      
      • 1
      • 2
    • 创建帧动画精灵

      Sprite* vampireSprite = Sprite::createWithSpriteFrameName("1");
      
      • 1
    • 创建帧动画

      Vector<SpriteFrame*> images;
      for(int i=1;i<=8;i++)
      {
          iamges.pushBack(cache->getSpriteFrameByName(Value(i).asString()));
      }
      Animation* _animation = Animation::createWithSpriteFrames(images,1.f/8);
      vampireSprite->runAction(RepeatForever::create(Animate::create(_animation)));
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

变速运动

  变速动作可以让节点对象具有加速度,产生平滑同时相对复杂的动作,所以可以用变速动作来模仿一些物理运动,这样比实际使用物理引擎的i性能消耗更低,使用起来也简单。当然也可以将变速动作应用到动画菜单和按钮上,实现想要的效果。

效果图

  Cocos2d-x支持上图中的大部分变速动作,实现起来也很简单。例子:一个精灵从屏幕顶部落下然后不断跳动:

//创建一个精灵
auto mySprite = Sprite::create("mysprite.png");

//创建一个移动让精灵消失
auto move = MoveBy::create(2,Vec2(200,dirs->getVisibleSize()->height - newSprite2->getContetSize().height));

//创建一个弹跳
auto move_ease_in = EaseBounceIn::create(move->clone());
auto move_ease_in_back = move_ease_in->reverse();

//创建一个延迟
auto delay = DelayTime::create(0.25f);

//创建一个执行顺序
auto seq1 = Sequence::create(move_ease_in,delay,move_ease_in_back,delay->clone(),nullptr);

//循环反复执行
mySprite->runAction(RepeatForever::create(seq1));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

动作序列

  动作序列(Sequence)是一种封装多个动作的对象,当这个对象执行时被封装的动作会顺序执行。

  一个Sequence可以包含任何数量的动作对象,回调方法和其他序列。可以包含回调方法?没错!Cocos2d-x允许把一个方法添加进去CallFunc对象,然后将callFunc添加到Sequence,这样,在执行序列的时候就能触发方法调用。因此,你能在一个序列中添加一些个性化的功能,而不仅仅是添加Cocos2d-x提供的有限动作。下面是一个序列的动作执行示意图:

效果图

回调函数包装器

  • 什么是回调函数包装器?

    • 简单的说,就是使用动作执行的方式调用函数。

    TIPS:回调函数一般是在特定的时间点或事件之后执行。

  • 四种回调函数包装器

    • CallFunc
    • CallFuncN
    • CallFuncND
    • CallFuncO
CallFunc::create(CC_CALLBACK_0(Node::removeFromParent,sprite));//函数指针
  • 1
CallFuncN::create([sprite](){//lamda表达式,至少要传入一个节点
   sprite->removeFromParent(); 
});
  • 1
  • 2
  • 3

Sequence示例

auto mySprite = Sprite::create("mysprite.png");

auto jump = JumpBy::create(0.5,Vec2(0,0),100,1);

auto rotate = RotateTo::create(2.0f,10);

//创建回调方法
auto callbackJump = CallFunc::create([](){
    log("Jumped!");
});

auto callbackRotate = CallFunc::create([](){
    log("Rotated!");
});

auto seq = Sequence::create(jump,callbackJump,rotate,callbackRotate,nullptr);

mySprite->runAction(seq);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

Spawn

  SpawnSequence是非常相似的,区别是Spawn同时执行所有的动作。Spawn对象可以添加任意数量的动作和其它Spawn对象。

效果图

  Spawn的效果和同时运行多个动作的runAction()方法是一致的,但是它的独特之处是Spawn能被放到Sequence中,结合SpawnSequence能实现非常强大的动作效果。

  例如,创建两个动作:

//创建两个动作
auto mySprite = Sprite::create("mysprite.png");

auto moveBy = MoveBy::create(10,Vec2(400,100));
auto fadeTo = FadeTo::create(2.0f,120.0f);
  • 1
  • 2
  • 3
  • 4
  • 5

  使用Spawn:

//使用Spawn运行
auto mySpawn = Spawn::createWithTwoActions(moveBy,fadeTo);
mySprite->runAction(mySpawn);
  • 1
  • 2
  • 3

  同时调用方法runAction():

//使用runAction()运行上面程序
mySprite->runAction(moveBy);
mySprite->runAction(fadeTo);
  • 1
  • 2
  • 3

  上面两种方式的效果是一样的,现在看把一个Spawn添加到一个Sequence中是怎样的一种情景,动作的执行流程会看起来像这样:

效果图

auto mySprite = Sprite::create("mysprite.png");

auto moveBy = MoveBy::create(10,Vec2(400,100));
auto fadeTo = FadeTo::create(2.0f,120.0f);
auto scaleBy = ScaleBy::create(2.0f,3.0f);

auto mySpawn = Spawn::createWithTwoActions(scaleBy,fadeTo);

auto seq = Sequence::create(moveBy,mySpawn,moveBy,nullptr);

mySprite->runAction(seq);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

动作的克隆和倒转

动作的克隆

  克隆(clone)的功能和字面含义一样,如果你对一个节点对象使用了clone()方法,你就获得了这个节点对象的拷贝。

  为什么要使用clone()方法?因为当Action对象运行时会产生一个内部状态,记录着节点属性的改变。当你想将一个创建的动作,重复使用到不同的节点对象时,如果不用**clone()**方法,就无法确定这个动作的属性到底是怎么样的(因为被使用过,产生了内部状态),这会造成难以预料的结果。

  我们来看示例,假如你有一个坐标位置是(0,0)的heroSprite,执行这一个动作:

MoveBy::create(10,Vec2(400,100));
  • 1

  你的heroSprite就在10s的时间中,从(0,0)移动到了(400,100),heroSprite有了一个新位置(400,100),更重要的是动作对象也有了节点位置相关的内部状态了。现在假如你有一个坐标位置是(200,200)的emenySprite。你还是用这个相同的动作,emenySprite就会移动到(800,200)的坐标位置(会使用当前的内部状态作为起点,再次基于当前坐标x轴移动400,y轴移动100),并不是你期待的结果。因为第二次将这个动作应用的时候,它已经又内部状态了。使用**clone()**能避免这种情况,克隆获得一个新的动作对象,新的对象没有之前的内部状态。

  以下是错误示例:

auto heroSprite = Sprite::create("herosprite.png");
auto enemySprite = Sprite::create("enemySprite.png");

auto MoveBy = MoveBy::create(10,Vec2(400,100));

heroSprite->runAction(moveBy);

//moveBy内部状态记录的坐标作为起始坐标
enemySprite->runAction(moveBy);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

  使用clone()的正确情况:

auto heroSprite = Sprite::create("herosprite.png");
auto enemySprite = Sprite::create("enemysprite.png");

auto moveBy = MoveBy::create(10,Vec2(400,100));

heroSprite->runAction(moveBy);

enemySprite->runAction(moveBy->clone());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

动作的倒转

  倒转(Reverse)的功能也和字面意思一样,调用reverse()可以让一系列动作按相反的方向执行。reserve()不是只能简单的让一个Action对象反向执行,还能让SequenceSpawn倒转。

  倒转使用起来很简单:

mySprite->runAction(mySpawn->reverse());
  • 1

  以下示例:

auto mySprite->Sprite::create("mysprite.png");
mySprite->setPosition(50,56);

auto moveBy = MoveBy::create(2.0f,Vec2(500,0));
auto scaleBy = ScaleBy::create(2.0f,2.0f);
auto delay = DelayTime::create(2.0f);

auto delaySequence = Sequence::create(delay,delay->clone(),delay->clone(),delay->clone(),nullptr);

auto sequence = Sequence::create(moveBy,delay,scaleBy,delaySequence,nullptr);

mySprite->runAction(sequence);

mySprite->runAction(sequence->reverse());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  执行步骤如下:

  1. mySprite创建
  2. mySprite的坐标位置设置成(50,56)
  3. sequence开始执行
  4. sequence执行第一个动作moveBy,2s中mySprite移动到了坐标位置(550,56)
  5. sequence执行第二个动作,暂停2s
  6. sequence执行第三个动作,scaleBy,2s中mySprite放大了2倍
  7. sequence执行第四个动作,delaySequence,暂停6s
  8. reverse()被调用,序列倒转,开始反向执行
  9. sequence执行第四个动作,delaySequence,暂停6s
  10. sequence执行第三个动作,scaleBy,2s中mySprite缩小了2倍(注意:序列内的动作被倒转
  11. sequence执行第二个动作,暂停2s
  12. sequence执行第一个动作moveBy,2s中mySprite从坐标位置(550,56),移动到了(50,56);
  13. mySprite回到了最初的位置
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/233961
推荐阅读
相关标签
  

闽ICP备14008679号