写这篇文章,没有花我多少时间。但是由于我的笔记写在云笔记中,图片也多。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

cocos2d-x3.3 源码分析之-动作Action和ActionManager的更多相关文章

  1. HTML实现代码雨源码及效果示例

    这篇文章主要介绍了HTML实现代码雨源码及效果示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  2. 利用Node实现HTML5离线存储的方法

    这篇文章主要介绍了利用Node实现HTML5离线存储的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. Html5 滚动穿透的方法

    这篇文章主要介绍了Html5 滚动穿透的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  4. Html5 Canvas实现图片标记、缩放、移动和保存历史状态功能 (附转换公式)

    这篇文章主要介绍了Html5 Canvas实现图片标记、缩放、移动和保存历史状态功能 (附转换公式),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. HTML5 拖放(Drag 和 Drop)详解与实例代码

    本篇文章主要介绍了HTML5 拖放(Drag 和 Drop)详解与实例代码,具有一定的参考价值,有兴趣的可以了解一下

  6. 跨域修改iframe页面内容详解

    这篇文章主要介绍了跨域修改iframe页面内容详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  7. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  8. ios – 使用带有NodeJs HTTPS的certificates.cer

    我为IOS推送通知生成了一个.cer文件,我希望将它与NodeJSHTTPS模块一起使用.我发现HTTPS模块的唯一例子是使用.pem和.sfx文件,而不是.cer:有解决方案吗解决方法.cer文件可以使用两种不同的格式进行编码:PEM和DER.如果您的文件使用PEM格式编码,您可以像使用任何其他.pem文件一样使用它(有关详细信息,请参见Node.jsdocumentation):如果您的文件使

  9. ios – 暂停调度队列是否会暂停其目标队列?

    我想创建两个串行队列A&B.队列B是队列A的目标.我想在B上排队一些块,并暂停它直到我准备执行它们,但是我想继续在队列A上执行块.如果我暂停B,这还会暂停它的目标队列(队列A)吗?我的想法是,我想安排这些特定的块在稍后日期执行但是我不希望它们同时运行而我不这样做想要处理信号量.但我希望队列A继续处理它的块,而B则被暂停如果不清楚这里是一些示例代码解决方法queueB被挂起,但queueA未被挂起.queueA和queueB被挂起.

  10. ios – 使用CocoaPods post install hook将自定义路径添加到HEADER_SEARCH_PATHS

    解决方法在Podfile中定义一个方法:然后在post_install中调用该方法:

随机推荐

  1. 【cocos2d-x 3.x 学习笔记】对象内存管理

    Cocos2d-x的内存管理cocos2d-x中使用的是上面的引用计数来管理内存,但是又增加了一些自己的特色。cocos2d-x中通过Ref类来实现引用计数,所有需要实现内存自动回收的类都应该继承自Ref类。下面是Ref类的定义:在cocos2d-x中创建对象通常有两种方式:这两中方式的差异可以参见我另一篇博文“对象创建方式讨论”。在cocos2d-x中提倡使用第二种方式,为了避免误用第一种方式,一般将构造函数设为protected或private。参考资料:[1]cocos2d-x高级开发教程2.3节[

  2. 利用cocos2dx 3.2开发消灭星星六如何在cocos2dx中显示中文

    由于编码的不同,在cocos2dx中的Label控件中如果放入中文字,往往会出现乱码。为了方便使用,我把这个从文档中获取中文字的方法放在一个头文件里面Chinese.h这里的tex_vec是cocos2dx提供的一个保存文档内容的一个容器。这里给出ChineseWords,xml的格式再看看ChineseWord的实现Chinese.cpp就这样,以后在需要用到中文字的地方,就先include这个头文件然后调用ChineseWord函数,获取一串中文字符串。

  3. 利用cocos2dx 3.2开发消灭星星七关于星星的算法

    在前面,我们已经在GameLayer中利用随机数初始化了一个StarMatrix,如果还不知道怎么创建星星矩阵请回去看看而且我们也讲了整个游戏的触摸事件的派发了。

  4. cocos2dx3.x 新手打包APK注意事项!

    这个在编译的时候就可以发现了比较好弄这只是我遇到的,其他的以后遇到再补充吧。。。以前被这两个问题坑了好久

  5. 利用cocos2dx 3.2开发消灭星星八游戏的结束判断与数据控制

    如果你看完之前的,那么你基本已经拥有一个消灭星星游戏的雏形。开始把剩下的两两互不相连的星星消去。那么如何判断是GameOver还是进入下一关呢。。其实游戏数据贯穿整个游戏,包括星星消除的时候要加到获得分数上,消去剩下两两不相连的星星的时候的加分政策等,因此如果前面没有做这一块的,最好回去搞一搞。

  6. 利用cocos2dx 3.2开发消灭星星九为游戏添加一些特效

    needClear是一个flag,当游戏判断不能再继续后,这个flag变为true,开始消除剩下的星星clearSumTime是一个累加器ONE_CLEAR_TIME就是每颗星星消除的时间2.连击加分信息一般消除一次星星都会有连击信息和加多少分的信息。其实这些combo标签就是一张图片,也是通过控制其属性或者runAction来实现。源码ComboEffect.hComboEffect.cpp4.消除星星粒子效果消除星星时,为了实现星星爆裂散落的效果,使用了cocos2d提供的粒子特效引擎对于粒子特效不了

  7. 02 Cocos2D-x引擎win7环境搭建及创建项目

    官网有搭建的文章,直接转载记录。环境搭建:本文介绍如何搭建Cocos2d-x3.2版本的开发环境。项目创建:一、通过命令创建项目前面搭建好环境后,怎样创建自己的Cocos2d-x项目呢?先来看看Cocos2d-x3.2的目录吧这就是Cocos2d-x3.2的目录。输入cocosnew项目名–p包名–lcpp–d路径回车就创建成功了例如:成功后,找到这个项目打开proj.win32目录下的Hello.slnF5成功了。

  8. 利用cocos2dx 3.2开发消灭星星十为游戏添加音效项目源码分享

    一个游戏,声音也是非常的重要,其实cocos2dx里面的简单音效引擎的使用是非常简单的。我这里只不过是用一个类对所有的音效进行管理罢了。Audio.hAudio.cpp好了,本系列教程到此结束,第一次写教程如有不对请见谅或指教,谢谢大家。最后附上整个项目的源代码点击打开链接

  9. 03 Helloworld

    程序都有一个入口点,在C++就是main函数了,打开main.cpp,代码如下:123456789101112131415161718#include"main.h"#include"AppDelegate.h"#include"cocos2d.h"USING_NS_CC;intAPIENTRY_tWinMain{UNREFERENCED_ParaMETER;UNREFERENCED_ParaMETER;//createtheapplicationinstanceAppDelegateapp;return

  10. MenuItemImage*图标菜单创建注意事项

    学习cocos2dx,看的是cocos2d-x3.x手游开发实例详解,这本书错误一大把,本着探索求知勇于发现错误改正错误的精神,我跟着书上的例子一起调试,当学习到场景切换这个小节的时候,出了个错误,卡了我好几个小时。

返回
顶部