cocos2d-x3.3 源码分析之-动作Action和ActionManager

写这篇文章,没有花我多少时间。但是由于我的笔记写在云笔记中,图片也多。csdn也不支持直接复制粘贴图片,可以说把笔记移植过来比我当初写还要费事,希望改进吧



从入口开始分析,我们来看看addaction函数,他接受一个动作,并绑定自己,是否暂停取决于节点的running属性。

我们来具体看看ActionManager这个类

class CC_DLLActionManager : public Ref

{

public:

ActionManager(void);

~ActionManager(void);

void addAction(Action *action,Node*target,bool paused);

void removeAllActions();

void removeAllActionsFromTarget(Node*target);

void removeAction(Action *action);

void removeActionByTag(int tag,Node*target);

void removeAllActionsByTag(int tag,Node*target);

Action* getActionByTag(int tag,const Node*target) const;

ssize_tgetNumberOfRunningActionsInTarget(const Node *target) const;

void pauseTarget(Node *target);

void resumeTarget(Node *target);

Vector<Node*>pauseAllRunningActions();

void resumeTargets(constVector<Node*>& targetsToResume);

void update(float dt);

protected:

void removeActionAtIndex(ssize_t index,struct _hashElement *element);

void deleteHashElement(struct _hashElement*element);

void actionAllocWithHashElement(struct_hashElement *element);

protected:

struct _hashElement *_targets;

struct_hashElement *_currentTarget;

bool _currentTargetSalvaged;

};

struct _hashElement*_targets;

struct_hashElement *_currentTarget;

如果是对定时器数据结构很熟悉的人没看到这个结构体应该不会陌生。的确,内部结构是相同的,只不过把之前的定时器对象改为现在的action.


类似的,每个hash节点一般由一个target标志,根据target找到节点。每个节点弱引用一个动作的array,也就是说,每个节点可以绑定多个动作,paused记录了当前动作的暂停状态。

下面来看看addAction函数:

voidActionManager::addAction(Action *action,Node *target,bool paused)

{

CCASSERT(action != nullptr,"");

CCASSERT(target != nullptr,"");

//查找hash表,看看是否有相同的target,如果没有新建一个插入表中。

tHashElement *element = nullptr;

// we should convert it to Ref*,because wesave it as Ref*

Ref *tmp = target;

HASH_FIND_PTR(_targets,&tmp,element);

if (! element)

{

element =(tHashElement*)calloc(sizeof(*element),1);

element->paused = paused;

target->retain();

element->target = target;

HASH_ADD_PTR(_targets,target,element);

}

//为弱引用的数组分配内存

actionAllocWithHashElement(element);

//同一个动作结束前,如果被同一个节点包含两次,触发断言

CCASSERT(!ccArrayContainsObject(element->actions,action),"");

//将新的动作加入数组

ccArrayAppendObject(element->actions,action);

action->startWithTarget(target);

}

现在我们不介绍最后一句,因为我们需要回过头去看看Action及其子类

Action

class CC_DLL Action: public Ref,public Clonable

{

public:

//动作的默认tag,一般不可以用默认tag索引动作

static const int INVALID_TAG = -1;

/**

* @js NA

* @lua NA

*/

virtual std::string description() const;

/** returns a clone of action */

virtual Action* clone() const

{

CC_ASSERT(0);

return nullptr;

}

/** returns a new action that performs theexactly the reverse action */

virtual Action* reverse() const

{

CC_ASSERT(0);

return nullptr;

}

//! return true if the action has finished

//判断动作是否结束

virtual bool isDone() const;

//! called before the action start. It willalso set the target.

//_taggetoriginTarget都赋值为新的绑定对象

virtual void startWithTarget(Node *target);

/**

called after the action has finished. Itwill set the 'target' to nil.

IMPORTANT: You should never call"[action stop]" manually. Instead,use:"target->stopAction(action);"

*/

//动作结束后,调用该函数,把_target设置为null

virtual void stop();

//! called every frame with it's deltatime. DON'T override unless you know what you are doing.

//类似定时器中的update函数

virtual void step(float dt);

/**

called once per frame. time a value between0 and 1

For example:

- 0 means that the action just started

- 0.5 means that the action is in themiddle

- 1 means that the action is over

*/

//类似定时器中的trigger函数

virtual void update(float time);

inline Node* getTarget() const { return_target; }

/** The action will modify the targetproperties. */

inline void setTarget(Node *target) {_target = target; }

inline Node* getOriginalTarget() const {return _originalTarget; }

/** Set the original target,since targetcan be nil.

Is the target that were used to run theaction. Unless you are doing something complex,like ActionManager,you shouldNOT call this method.

The target is 'assigned',it is not'retained'.

@since v0.8.2

*/

inline void setOriginalTarget(Node*originalTarget) { _originalTarget = originalTarget; }

inline int getTag() const { return _tag; }

inline void setTag(int tag) { _tag = tag; }

CC_CONSTRUCTOR_ACCESS:

Action();

virtual ~Action();

protected:

Node*_originalTarget;

/** The "target".

The target will be set with the'startWithTarget' method.

When the 'stop' method is called,targetwill be set to nil.

The target is 'assigned',it is not'retained'.

*/

Node *_target;

/** The action tag. An identifier of theaction */

int_tag;

private:

CC_DISALLOW_COPY_AND_ASSIGN(Action);

};

isDownupdatestep这三个函数是虚函数,action并没有做什么实际的事情,所以我们现在还不知道他干了些什么,我们之后去看看具体的动作类就会清楚了。

_originalTarget_target这两者有什么区分现在也不清楚

我们来看看最重要的一个子类FiniteTimeAction

class CC_DLLFiniteTimeAction : public Action

{

public:

//! get duration in seconds of the action

inline float getDuration() const { return_duration; }

//! set duration in seconds of the action

inline void setDuration(float duration) {_duration = duration; }

//

// Overrides

//

virtual FiniteTimeAction* reverse() constoverride

{

CC_ASSERT(0);

return nullptr;

}

virtual FiniteTimeAction* clone() constoverride

{

CC_ASSERT(0);

return nullptr;

}

CC_CONSTRUCTOR_ACCESS:

FiniteTimeAction()

: _duration(0)

{}

virtual ~FiniteTimeAction(){}

protected:

//! duration in seconds

float _duration;

private:

CC_DISALLOW_COPY_AND_ASSIGN(FiniteTimeAction);

};

只是增加了一个_duration属性,也就是动作的持续时间,应该是为了他的延时动作子类准备的。

我么接着看ActionInstant这个瞬时动作:

class CC_DLLActionInstant : public FiniteTimeAction //<NSCopying>

{

public:

//

// Overrides

//

virtual ActionInstant* clone() constoverride

{

CC_ASSERT(0);

return nullptr;

}

virtual ActionInstant * reverse() constoverride

{

CC_ASSERT(0);

return nullptr;

}

virtual bool isDone() const override;

virtual void step(float dt) override;

virtual void update(float time) override;

};

瞬时动作类重写了step函数和isDown函数,其他的isDoneupdate函数并没有做出变化

step:


调用了update函数,并传入参数1。我们可以看看update函数的注释

called once perframe. time a value between 0 and 1

For example:

0 means that the action just started

0.5 means that the action is in the middle

1 means that the action is over

*/

1表示动作结束了

isDown:


isDown也返回true。我们或许会疑惑,不过接下来看了ActionManager中的update函数之后你就会明白了:

该函数在导演类的init函数中被注册到定时器:


该函数有最高的定时触发优先级,每一帧动作的运行都会触发该函数。

voidActionManager::update(float dt)

{

for (tHashElement *elt = _targets; elt !=nullptr; )

{

_currentTarget = elt;

_currentTargetSalvaged = false;

if (! _currentTarget->paused)

{

// The 'actions' MutableArray maychange while inside this loop.

for (_currentTarget->actionIndex= 0; _currentTarget->actionIndex < _currentTarget->actions->num;

_currentTarget->actionIndex++)

{

_currentTarget->currentAction =(Action*)_currentTarget->actions->arr[_currentTarget->actionIndex];

if(_currentTarget->currentAction == nullptr)

{

continue;

}

_currentTarget->currentActionSalvaged = false;

_currentTarget->currentAction->step(dt);

if(_currentTarget->currentActionSalvaged)

{

// The currentAction toldthe node to remove it. To prevent the action from

// accidentallydeallocating itself before finishing its step,we retained

// it. Now that step isdone,it's safe to release it.

_currentTarget->currentAction->release();

} else

if (_currentTarget->currentAction->isDone())

{

_currentTarget->currentAction->stop();

Action *action =_currentTarget->currentAction;

// Make currentAction nilto prevent removeAction from salvaging it.

_currentTarget->currentAction = nullptr;

removeAction(action);

}

_currentTarget->currentAction = nullptr;

}

}

// elt,at this moment,is still valid

// so it is safe to ask this here(issue #490)

elt = (tHashElement*)(elt->hh.next);

// only delete currentTarget if noactions were scheduled during the cycle (issue #481)

if (_currentTargetSalvaged &&_currentTarget->actions->num == 0)

{

deleteHashElement(_currentTarget);

}

}

// issue #635

_currentTarget = nullptr;

}

我们可以看到,step就相当于一个触发函数,而动作中的update函数就是触发函数中调用的函数,出发完成后,判断动作是否结束,如果结束调用stop函数,并移除动作。

我们现在可以回到之前的瞬时动作,update函数被传值为1isDown返回true,就很好理解了。

瞬时动作的基类没有实现update函数,我们以一个具体的瞬时动作来做例子:

class CC_DLL Show : public ActionInstant

{

public:

/** Allocates and initializes the action */

static Show * create();

//

// Overrides

//

virtual void update(float time) override;

virtual ActionInstant* reverse() constoverride;

virtual Show* clone() const override;

CC_CONSTRUCTOR_ACCESS:

Show(){}

virtual ~Show(){}

private:

CC_DISALLOW_COPY_AND_ASSIGN(Show);

};

重写了update函数:


接着看

void ActionManager::update(float dt)中的那个removeAction函数:

好了一目了然,之前的_originTarget_targrt的区别也就知道了,_target是为了实现节点动作用的,二_target置为空之后,_originTarget便可以用来删除这个动作,动作删除后被release

好了,我们可以接着看延时动作了:

class CC_DLLActionInterval : public FiniteTimeAction

{

public:

/** how many seconds had elapsed since theactions started to run. */

inline float getElapsed(void) { return_elapsed; }

//extension in GridAction

void setAmplitudeRate(float amp);

float getAmplitudeRate(void);

//

// Overrides

//

virtual bool isDone(void) const override;

virtual voidstep(float dt) override;

virtual void startWithTarget(Node *target)override;

virtual ActionInterval* reverse() constoverride

{

CC_ASSERT(0);

return nullptr;

}

virtual ActionInterval *clone() constoverride

{

CC_ASSERT(0);

return nullptr;

}

CC_CONSTRUCTOR_ACCESS:

/** initializes the action */

bool initWithDuration(float d);

protected:

float_elapsed;

bool_firstTick;

};

_elapsed表示延时动作度过的时间

isDone:



很简单,只要度过的时间大于等于动作的生命周期就表示动作结束。

step:


很简单的定时逻辑,ActionManager中的update函数每一次出发后调用step函数,dt一般是每一帧的时间(大多数是1 /60)。每一次调用后,把dt加到_elapsed就可以做到计时了。update的参数被限制在0-1之间,具体的实现得看一个具体的例子:


这里的initWithDuration就是我们创建动作时候传递的动作生命周期。

update



很简单吧,每次变化后的属性重新设置给Node就达到了动画的目的。

我们接下来看看移除动作:

void removeAction(Action *action)


移除很简单,找到绑定对象,从对象的动作数组中移除即可。

接下来看看Node中的stopAction函数:


函数原理都是一样的,拿其中一个举例:


间接调用了移除动作的函数。

注意事项:不要用action中的stop函数来停止动作,因为判断动作是否结束的标志是isDown函数,并且判断过程还是在动作执行之后。stop函数会将_target置为null,这样运行动作就会试图去修改_target的属性,导致程序奔溃。还有一点就是动作运行结束后会被删除,如果想多次运行动作,请retain

总结:

动作最重要的几个函数

isDown

step

stop

update

以及ActionManager::update

还有动作的重要属性:

_target

_origintarget

_elapsed

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


    本文实践自 RayWenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@163.com微信公众号:HopToad 欢迎转载,转载标注出处:http://blog.csdn.netotbaron/article/details/424343991.  软件准备 下载地址:http://cn.cocos2d-x.org/download 2.  简介2.1         引用C
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从Cocos2D-x官网上下载,进入网页http://www.cocos2d-x.org/download,点击Cocos2d-x以下的Download  v3.0,保存到自定义的文件夹2:从python官网上下载。进入网页https://www.python.org/downloads/,我当前下载的是3.4.0(当前最新
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发引擎,易学易用,支持多种智能移动平台。官网地址:http://cocos2d-x.org/当前版本:2.0    有很多的学习资料,在这里我只做为自己的笔记记录下来,错误之处还请指出。在VisualStudio2008平台的编译:1.下载当前稳
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《最强大脑》娱乐节目。将2048改造成一款挑战玩家对数字记忆的小游戏。邮箱:appdevzw@163.com微信公众号:HopToadAPK下载地址:http://download.csdn.net/detailotbaron/8446223源码下载地址:http://download.csdn.net/
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试以QtCreatorIDE来进行CMake构建。Cocos2d-x3.X地址:https://github.com/cocos2d/cocos2d-x1.打开QtCreator,菜单栏→"打开文件或项目...",打开cocos2d-x目录下的CMakeLists.txt文件;2.弹出CMake向导,如下图所示:设置
 下载地址:链接:https://pan.baidu.com/s/1IkQsMU6NoERAAQLcCUMcXQ提取码:p1pb下载完成后,解压进入build目录使用vs2013打开工程设置平台工具集,打开设置界面设置: 点击开始编译等待编译结束编译成功在build文件下会出现一个新文件夹Debug.win32,里面就是编译
分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!http://www.captainbed.net前言上次用象棋演示了cocos2dx的基本用法,但是对cocos2dx并没有作深入的讨论,这次以超级马里奥的源代码为线索,我们一起来学习超级马里奥的实
1. 圆形音量button事实上作者的本意应该是叫做“电位计button”。可是我觉得它和我们的圆形音量button非常像,所以就这么叫它吧~先看效果:好了,不多解释,本篇到此为止。(旁白: 噗。就这样结束了?)啊才怪~我们来看看代码:[cpp] viewplaincopyprint?CCContro
原文链接:http://www.cnblogs.com/physwf/archive/2013/04/26/3043912.html为了进一步深入学习贯彻Cocos2d,我们将自己写一个场景类,但我们不会走的太远,凡是都要循序渐进,哪怕只前进一点点,那也至少是前进了,总比贪多嚼不烂一头雾水的好。在上一节中我们建
2019独角兽企业重金招聘Python工程师标准>>>cocos2d2.0之后加入了一种九宫格的实现,主要作用是用来拉伸图片,这样的好处在于保留图片四个角不变形的同时,对图片中间部分进行拉伸,来满足一些控件的自适应(PS: 比如包括按钮,对话框,最直观的形象就是ios里的短信气泡了),这就要求图
原文链接:http://www.cnblogs.com/linji/p/3599478.html1.环境和工具准备Win7VS2010/2012,至于2008v2版本之后似乎就不支持了。 2.安装pythonv.2.0版本之前是用vs模板创建工程的,到vs2.2之后就改用python创建了。到python官网下载版本2.7.5的,然后
环境:ubuntu14.04adt-bundle-linux-x86_64android-ndk-r9d-linux-x86_64cocos2d-x-3.0正式版apache-ant1.9.3python2.7(ubuntu自带)加入环境变量exportANDROID_SDK_ROOT=/home/yangming/adt-bundle-linux/sdkexportPATH=${PATH}:/$ANDROID_SDK_ROOTools/export
1开发背景游戏程序设计涉及了学科中的各个方面,鉴于目的在于学习与进步,本游戏《FlappyBird》采用了两个不同的开发方式来开发本款游戏,一类直接采用win32底层API来实现,另一类采用当前火热的cocos2d-x游戏引擎来开发本游戏。2需求分析2.1数据分析本项目要开发的是一款游
原文链接:http://www.cnblogs.com/linji/p/3599912.html//纯色色块控件(锚点默认左下角)CCLayerColor*ccc=CCLayerColor::create(ccc4(255,0,0,128),200,100);//渐变色块控件CCLayerGradient*ccc=CCLayerGradient::create(ccc4(255,0,0,
原文链接:http://www.cnblogs.com/linji/p/3599488.html//载入一张图片CCSprite*leftDoor=CCSprite::create("loading/door.png");leftDoor->setAnchorPoint(ccp(1,0.5));//设置锚点为右边中心点leftDoor->setPosition(ccp(240,160));/
为了答谢广大学员对智捷课堂以及关老师的支持,现购买51CTO学院关老师的Cocos2d-x课程之一可以送智捷课堂编写图书一本(专题可以送3本)。一、Cocos2d-x课程列表:1、Cocos2d-x入门与提高视频教程__Part22、Cocos2d-x数据持久化与网络通信__Part33、Cocos2d-x架构设计与性能优化内存优
Spawn让多个action同时执行。Spawn有多种不同的create方法,最终都调用了createWithTwoActions(FiniteTimeAction*action1,FiniteTimeAction*action2)方法。createWithTwoActions调用initWithTwoActions方法:对两个action变量初始化:_one=action1;_two=action2;如果两个a
需要环境:php,luajit.昨天在cygwin上安装php和luajit环境,这真特么是一个坑。建议不要用虚拟环境安装打包环境,否则可能会出现各种莫名问题。折腾了一下午,最终将环境转向linux。其中,luajit的安装脚本已经在quick-cocos2d-x-develop/bin/中,直接luajit_install.sh即可。我的lin
v3.0相对v2.2来说,最引人注意的。应该是对触摸层级的优化。和lambda回调函数的引入(嗯嗯,不枉我改了那么多类名。话说,每次cocos2dx大更新。总要改掉一堆类名函数名)。这些特性应该有不少人研究了,所以今天说点跟图片有关的东西。v3.0在载入图片方面也有了非常大改变,仅仅只是