菜鸟学习Cocos2d-x 3.x——浅谈动作Action
来源:互联网 发布:育知同创学费 编辑:程序博客网 时间:2024/06/08 19:37
动作类概述
一款游戏,设计的再NB的游戏,如果都是一堆静态的图片,没有任何动作,那也只能“呵呵”了。动作体系对于一款游戏的成功与否,有着非常重要的影响。所以,这篇文章就对Cocos2d-x中的动作进行总结。先来看看Cocos2d-x中的与动作相关的类。
与动作相关的类图如下图所示: 现在就对这些类进行简单的介绍,在后续的小节中还会进行详细的分析的。
- Ref和Clonable:这里不说,在总结Cocos2d-x内存管理的时候再进行详细总结;
- Action:所有动作的父类,定义了公共的操作;
- FiniteTimeAction:瞬时动作和延时动作的父类,可以定义动作的时间变化;
- Follow:跟随节点的动作;
- Speed:改变一个动作的时间,比如实现慢动作回放或者快进;
- ActionInstant:瞬间完成动作,中间没有任何动画效果;
- ActionInterval:动作会在指定的时间内完成,中间会有动画效果;
- FlipX:X轴方向翻转;
- MoveTo:移动动作;
- ……
下面就对上面说的这些类进行通过实际的代码进行总结。
Action类的主要成员函数
以下是Action
类的主要成员函数:
/*** 返回一个新的Action对象,表示原动作的相反的动作*/virtual Action* reverse() const = 0;// 如果动作已经完成了,就返回truevirtual bool isDone() const;// 在动作开始之前被调用,设置动作作用的对象virtual void startWithTarget(Node *target);/*** 在动作完成以后会被调用,它会设置"target"对象为空* 注:请永远不要手动调用该函数,而是调用对应的"target->stopAction(action);"*/virtual void stop();/*** 每帧都会调用的方法,如果你需要在每帧控制动作,则需要重写,时间间隔为动作间隔时间* 最好不要重写该函数,除非你真的知道怎么做*/virtual void step(float dt);/*** 每一帧都会调用一次该函数,参数time取值为0和1之间的任意值,例如:* 0表示动作刚刚开始的时候调用;* 0.5表示动作执行到一半的时候调用;* 1表示动作完成以后调用;*/virtual void update(float time);// 获得执行动作的对象inline Node* getTarget() const { return _target; }// 设置执行动作的对象inline void setTarget(Node *target) { _target = target; }// 动作标签inline int getTag() const { return _tag; }inline void setTag(int tag) { _tag = tag; }
瞬时动作
ActionInstant
是瞬时动作类。瞬时动作表示瞬间完成动作,中间没有任何动画效果;由于ActionInstant
的子类那么多,这里就以FlipX
为例子,做一个简单的Demo。效果如下图所示: 可以看到小狗走到最左边时,会有一个转向的过程,这就是FlipX
的动作效果。示例代码如下:
Size visibleSize = Director::getInstance()->getVisibleSize();// 创建移动动作ActionInterval *moveto = MoveTo::create(5, Vec2(0, 200));// 创建X轴方向的翻转动作ActionInstant *flipx = FlipX::create(true);ActionInterval *moveback = MoveTo::create(5, Vec2(visibleSize.width, 200));auto scene = Director::getInstance()->getRunningScene();auto layer = scene->getChildByTag(1);auto dog = layer->getChildByTag(1);// flipx->reverse()获得对应的逆向动作auto action = Sequence::create(moveto, flipx, moveback, flipx->reverse(), NULL);dog->runAction(action);
瞬时动作是只能够立刻完成的动作,这类动作是在下一帧立刻完成的动作,如设定位置、设定缩放等。把它们包装成动作后,可以与其他动作类组合为复杂动作。
延时动作
动作会在指定的时间内完成,中间会有动画效果。延时动作通过属性值的逐渐变化来实现动画效果。需要注意的是XXTo和XXBy的区别在于XXTo是表示最终值,而XXBy则表示向量-改变值。比如MoveTo
和MoveBy
动作,如以下效果所示: 示例代码如下:
// 创建移动动作ActionInterval *moveto = MoveTo::create(5, Vec2(0, 200));ActionInterval *moveby = MoveBy::create(5, Vec2(-200, 0));auto scene = Director::getInstance()->getRunningScene();auto layer = scene->getChildByTag(1);auto dog1 = layer->getChildByTag(1);auto dog2 = layer->getChildByTag(2);dog1->runAction(moveto);dog2->runAction(moveby);
上面的那只狗是使用的MoveBy
动作,而下面这只狗使用的是MoveTo
动作。MoveTo
动作都在用,下面就来看看稍微复杂点的贝塞尔曲线动作。
使用贝塞尔曲线,可以使节点进行曲线运动。每条贝塞尔曲线都包含一个起点和一个终点。在一条曲线中,起点和终点各自包含一个控制点,而控制点到端点的连线称作控制线。控制点决定了曲线的形状,包含角度和长度两个参数。如下图: 实现效果如下: 示例代码如下:
ccBezierConfig bezier;bezier.controlPoint_1 = Point(200, 300);bezier.controlPoint_2 = Point(400, 400);bezier.endPosition = Point(50, 200);auto bezierAction = BezierTo::create(2.0f, bezier);dog1->runAction(bezierAction);
我们在实际开发中,主要的任务就是确定两个控制点,去协调精灵的移动弧度。
缓冲动作
在游戏中,我们经常要实现一些加速度或者减速度的效果。Cocos2d-x已经为我们做好了。
ActionEase
类可以实现动作的速度由快到慢、速度随时间改变的匀速运动。该类包含5类运动:
- 指数缓冲;
- Sine缓冲;
- 弹性缓冲;
- 跳跃缓冲;
- 回震缓冲。
每类运动都包含3个不同时期的变换:In、Out和InOut。
- In表示开始的时候加速;
- Out表示结束的时候加速;
- InOut表示开始和结束的时候加速。
上述5类运动分别对应以下的类:
- 指数缓冲:
EaseExponentialIn
、EaseExponentialOut
和EaseExponentialInOut
; - Sine缓冲:
EaseSineIn
、EaseSineOut
和EaseSineInOut
; - 弹性缓冲:
EaseElasticIn
、EaseElasticOut
和EaseElasticInOut
; - 跳跃缓冲:
EaseBounceIn
、EaseBounceOut
和EaseBounceInOut
; - 回震缓冲:
EaseBackIn
、EaseBackOut
和EaseBackInOut
。
通过图表和描述也不能形象的说明上述5中运动,下面就通过指数缓冲的实际运行效果来进行展示:
指数缓冲 示例代码如下:
ActionInterval *moveto1 = MoveTo::create(5, Vec2(50, 100));ActionInterval *moveto2 = MoveTo::create(5, Vec2(50, 300));ActionInterval *moveto3 = MoveTo::create(5, Vec2(50, 500));auto scene = Director::getInstance()->getRunningScene();auto layer = scene->getChildByTag(1);auto dog1 = layer->getChildByTag(1);auto dog2 = layer->getChildByTag(2);auto dog3 = layer->getChildByTag(3);auto action1 = EaseExponentialIn::create(moveto1);auto action2 = EaseExponentialOut::create(moveto2);auto action3 = EaseExponentialInOut::create(moveto3);dog1->runAction(action1);dog2->runAction(action2);dog3->runAction(action3);
从下到上的三次小狗,分别对应的是EaseExponentialIn
、EaseExponentialOut
和EaseExponentialInOut
。
- EaseExponentialIn表现的效果为速度越来越快;
- EaseExponentialOut表现的效果为速度越来越慢;
- EaseExponentialInOut表现的效果为中间速度非常快,两头速度较慢。
组合动作
在游戏中,很多时候,一个对象并不是单纯的执行一个动作,而是依次执行一系列动作或者同时执行一系列动作,那么这又该如何去完成呢?现在就来看看Cocos2d-x中是如何去完成这些的吧。
- Sequence 可以使用
Sequence
定义一个动作序列,应用实例可以参见瞬时动作这小节。 - Spawn
Spawn
也是定义一系列动作,但是定义的动作会同时执行,使用方法同Sequence
是一样的。下面就通过具体的效果和代码来看看。 可以看到,移动和淡入的效果在同时进行。示例代码如下:ActionInterval *moveto1 = MoveTo::create(5, Vec2(50, 100));ActionInterval *fadein = FadeIn::create(5);auto scene = Director::getInstance()->getRunningScene();auto layer = scene->getChildByTag(1);auto dog1 = layer->getChildByTag(1);auto spawnAction = Spawn::create(moveto1, fadein, NULL);dog1->runAction(spawnAction);
- Repeat和RepeatForever
Repeat
和RepeatForever
两个类可以使动作重复执行,Repeat
可以在参数中指定重复次数;RepeatForever
则是一直重复执行。 从左往右,第一只小狗只跳一次;第二只小狗使用的Repeat
类,定义的跳跃5次;第三只小狗使用的RepeatForever
,将会一直跳跃下去。实例代码如下:// 创建跳跃动作ActionInterval *jumpTo1 = JumpBy::create(3, Vec2(0, 0), 100, 1);ActionInterval *jumpTo2 = JumpBy::create(3, Vec2(0, 0), 100, 1);ActionInterval *jumpTo3 = JumpBy::create(3, Vec2(0, 0), 100, 1);auto scene = Director::getInstance()->getRunningScene();auto layer = scene->getChildByTag(1);auto dog1 = layer->getChildByTag(1);auto dog2 = layer->getChildByTag(2);auto dog3 = layer->getChildByTag(3);// 重复跳动5次auto repeatJump = Repeat::create((FiniteTimeAction *)jumpTo2, 5);// 一直重复跳动动作auto repeatForeverJump = RepeatForever::create((ActionInterval *)jumpTo3);dog1->runAction(jumpTo1);dog2->runAction(repeatJump);dog3->runAction(repeatForeverJump);
在实际试验中,发现,
Repeat
和RepeatForever
对于JumpTo
,并不能得到我们预期的结果。后来在源代码中,发现JumpTo
没有重写Action
类的Update
方法,导致了错误的结果,希望在下个版本中有更改吧。 通常在开发中我们需要将各种动作组合起来再让节点执行,复合动作的作用就是将各种动作组合在一起。而且,复合动作本身也是动作。因此可以作为一个普通动作嵌入到其他动作中。注意:Sequence动作不能嵌入其他复合动作内使用,DelayTime不属于复合动作,但是只能在复合动作内使用。
跟随动作
跟随动作Follow
是一个节点跟随另外一个节点的动作。我都不理解这个动作,此处不做过多的总结。以后明白了,再回来补上。
函数回调动作
有的时候,我们需要在动作完成以后,回调一个我们自定义的函数,完成某些功能,比如攻击一个敌人,攻击动作完成以后,敌人需要减少血量,这个时候,就需要使用函数回调动作。
CallFunc
系列动作包括CallFunc
、CallFuncN
、__CCCallFuncND
,以及__CCCallFuncO
四个动作,用来在动作中进行方法的调用(之所以不是函数调用,是因为它们只能调用某个类中的实例方法,而不能调用普通的C函数)。
当某个对象执行CallFunc
系列动作时,就会调用一个先前被设置好的方法,以完成某些特别的功能。
在CallFunc
系列动作的4个类中:
CallFunc
调用的方法不包含参数;CallFuncN
调用的方法包含一个Node*
类型的参数,表示执行动作的对象;__CCCallFuncND
调用的方法包含两个参数,不仅有一个节点参数,还有一个自定义参数(Node*
与void*
);__CCCallFuncO
调用的方法则只包含一个Ref*
类型的参数;
实际上,CallFunc
系列动作的后缀”N”表示Node参数,指的是执行动作的对象, “D”表示Data参数,指的是用户自定义的数据,”O”表示对象,指的是一个用户自定义的Ref参数。(__CCCallFuncND
和__CCCallFuncO
的命名好奇葩,可能在后续的版本中又要变了。)
在不同的情况下,我们可以根据不同的需求来选择不同的CallFunc
动作。看看代码示例。
// 使用Lambda表达式auto callFunc1 = CallFunc::create([&]{log("cocos2d-x 1"); });// 使用成员函数auto callFunc2 = CallFuncN::create(this, callfuncN_selector(HelloWorld::testFunc));// 顺序执行所有动作auto action = Sequence::create(moveTo, callFunc1, callFunc2, NULL);dog1->runAction(action);
动作管理
动作管理类ActionManager
是管理所有动作的一个单例类,当我们调用runAction
函数时,该函数会把动作通过动作管理类的addAction
函数将对象传递给ActionManager
的单例,该实例再把这个动作添加到自己的动作序列中。
动作管理单例通过定时刷新自己的update
方法,在这个方法中去调用行为序列中每个动作的step
,这些step
方法再根据自身的完成进度去update
或是结束行为。
实际上是由动作管理单例驱动每个动作去更新自己的逻辑,而runAction
方法只是将动作对象添加进ActionManager
的待执行动作队列中,由ActionManager
去管理所有动作。当节点被清除或是动作结束时,动作管理类会自动将动作从队列中删除,不需要人为的去管理。
后续分析具体的小游戏的时候,遇到了ActionManager
,再做详细的分析。
总结
终于总结完了,很显然,这部分的内容远不止这么点。我这里只是把比较重要的几个部分进行了详细的总结;后续的博文中,遇到了再做更详细的总结。希望这篇稍微有点长的博文对大家有帮助。
这篇文章稍微有点长,写的稍微有点累,写的真心不容易。各位,有钱的捧个钱场,有人的捧个人场。
2014年11月24日 于深圳。
- 菜鸟学习Cocos2d-x 3.x——浅谈动作Action
- 菜鸟学习Cocos2d-x 3.x——浅谈动作Action
- 菜鸟学习Cocos2d-x 3.x——浅谈动作Action
- 菜鸟学习Cocos2d-x 3.x——浅谈动作Action
- 【基础】Cocos2d-x 浅谈动作Action
- Cocos2d-x ——Action (动作)简介
- Cocos2d-x ——Action (动作)简介
- Cocos2d-x --Action (动作)
- cocos2d-x Action动作
- Cocos2d-x动作Action
- 菜鸟学习Cocos2d-x 3.x——浅谈事件处理机制
- cocos2d-x学习笔记(四)动作(Action)
- Cocos2d-3.x Action动作锦集
- Cocos2d-x学习:动作Action(延时类动作)
- cocos2d-x Action:持续动作
- cocos2d-x Action:其他动作
- cocos2d-x 基本概念 动作 Action
- Cocos2d-x之动作Action
- iOS 二维码的扫描使用,以及图片预览,类似于相册
- cocos2dx Lua 定时器
- 项目报错“JavaServer Faces 2.2 can not be installed : One or more constraints”等一系列问题
- 简洁风格的Tab
- php连接oracle类 分页查询和乱码问题
- 菜鸟学习Cocos2d-x 3.x——浅谈动作Action
- Eclipse: Java was started but returned exit code=13
- Google sshxcute优化版+二次封装类
- 1345: 国际象棋
- list不能修改不存在的项 IndexError: list assignment index out of range
- document.body.scrollTop 值总为0的解决方法
- #define PINT int * typedef int * SINT; 这两的区别及define与undefine用法
- Prim算法求最小生成树的c++代码实现
- 机器学习-->贝叶斯网络