首先CCAction是所有动作的基类,如下图继承关系:
那么来看看CCAction的定义:
@H_404_14@class CC_DLL CCAction : @H_404_14@public CCObject { @H_404_14@public: CCAction(@H_404_14@void); @H_404_14@virtual ~CCAction(@H_404_14@void); @H_404_14@const @H_404_14@char* description(); @H_404_14@virtual CCObject* copyWithZone(CCZone *pZone); //! return true if the action has finished @H_404_14@virtual @H_404_14@bool isDone(@H_404_14@void); @H_404_14@virtual @H_404_14@void startWithTarget(CCNode *pTarget); /** called after the action has finished. It will set the 'target' to nil. IMPORTANT: You should never call "[action stop]" manually. Instead,use: "target->stopAction(action);" */ @H_404_14@virtual @H_404_14@void stop(@H_404_14@void); //! called every frame with it's delta time. DON'T override unless you know what you are doing. @H_404_14@virtual @H_404_14@void step(@H_404_14@float dt); @H_404_14@virtual @H_404_14@void update(@H_404_14@float time); inline CCNode* getTarget(@H_404_14@void) { @H_404_14@return m_pTarget; } inline @H_404_14@void setTarget(CCNode *pTarget) { m_pTarget = pTarget; } inline CCNode* getOriginalTarget(@H_404_14@void) { @H_404_14@return m_pOriginalTarget; } inline @H_404_14@void setOriginalTarget(CCNode *pOriginalTarget) { m_pOriginalTarget = pOriginalTarget; } inline @H_404_14@int getTag(@H_404_14@void) { @H_404_14@return m_nTag; } inline @H_404_14@void setTag(@H_404_14@int nTag) { m_nTag = nTag; } @H_404_14@public: @H_404_14@static CCAction* create(); @H_404_14@protected: CCNode *m_pOriginalTarget; CCNode *m_pTarget; @H_404_14@int m_nTag; };
在类定义最后有三个成员变量,而继承自CCAction的CCFiniteTimeAction主要新增加了一个用于保存该动作总完成时间的成员变量float m_fDuration;
对于其两个子类CCActionInstant和CCActionInterval,前者没有新增任何函数和变量,而后者增加了两个成员变量:float m_elapsed(记录从动作开始起逝去的时间);和bool m_bFirstTick(一个控制变量);
那么动作是如何执行的呢?
当一个节点调用runAction方法时,动作管理类CCActionManager(单例类)会将新的动作和节点添加到其管理的动作表中。
CCAction * CCNode::runAction(CCAction* action) { CCAssert( action != NULL,"Argument must be non-nil"); m_pActionManager->addAction(action,@H_404_14@this,!m_bRunning); @H_404_14@return action; }
在addAction中,将动作添加到动作队列后,就会对该动作调用其成员函数startWithTarget(CCNode* pTarget)来绑定该动作的执行节点,和初始化动作类的成员变量。
这些工作都完成后,每一帧刷新屏幕时,系统就会在CCActionManager中遍历动作表中的每一个动作,并调用动作的step(float)方法。而step方法主要负责计算m_elapsed的值,并调用update(float)方法。
@H_404_14@void CCActionInterval::step(@H_404_14@float dt) { @H_404_14@if (m_bFirstTick) { m_bFirstTick = @H_404_14@false; m_elapsed = 0; } @H_404_14@else { m_elapsed += dt; } @H_404_14@this->update(MAX (0,// needed for rewind. elapsed could be negative MIN(1,m_elapsed / MAX(m_fDuration,FLT_EPSILON) // division by 0 ) ) ); }
传入update方法的float型参数表示逝去的时间与动作完成需要的时间的比值,介于0-1之间,即动作完成的百分比。然后在update方法中,通过完成比例对节点的属性进行操作来达到动作的效果。
例如:对MoveBy调用update时,通过传入的比例调用setPosition直接修改节点的属性。
最后在每一帧结束后,CCActionManager的update会检查动作队列中每个动作的isDone函数是否返回true,如果返回true,则动作结束,将其从队列中删除。
——————————————————————————————————————————————————————————————————————————
从上面知道:动作都是由CCActionManager来管理。那我们再来看看CCActionManager的工作原理。
在CCDirector初始化时,执行了如下代码:
// scheduler m_pScheduler = @H_404_14@new CCScheduler(); // action manager m_pActionManager = @H_404_14@new CCActionManager(); m_pScheduler->scheduleUpdateForTarget(m_pActionManager,kCCPrioritySystem,@H_404_14@false);
可见动作管理类在创建时就注册了Update定时器,那么CCScheduler在游戏的每一帧mainLoop更新中都会触发CCActionManager注册的update(float )方法。调度器原理请参照此链接:http://www.cnblogs.com/songcf/p/3162414.html
// main loop @H_404_14@void CCActionManager::update(@H_404_14@float dt) { //枚举动作表中的每一个节点 @H_404_14@for (tHashElement *elt = m_pTargets; elt != NULL; ) { m_pCurrentTarget = elt; m_bCurrentTargetSalvaged = @H_404_14@false; @H_404_14@if (! m_pCurrentTarget->paused) { //枚举节点的每一个动作 actions数组可能会在循环中被修改 @H_404_14@for (m_pCurrentTarget->actionIndex = 0; m_pCurrentTarget->actionIndex < m_pCurrentTarget->actions->num; m_pCurrentTarget->actionIndex++) { m_pCurrentTarget->currentAction = (CCAction*)m_pCurrentTarget->actions->arr[m_pCurrentTarget->actionIndex]; @H_404_14@if (m_pCurrentTarget->currentAction == NULL) { @H_404_14@continue; } m_pCurrentTarget->currentActionSalvaged = @H_404_14@false; m_pCurrentTarget->currentAction->step(dt); @H_404_14@if (m_pCurrentTarget->currentActionSalvaged) { // The currentAction told the node to remove it. To prevent the action from // accidentally deallocating itself before finishing its step,we retained // it. Now that step is done,it's safe to release it. m_pCurrentTarget->currentAction->release(); } @H_404_14@else @H_404_14@if (m_pCurrentTarget->currentAction->isDone()) { m_pCurrentTarget->currentAction->stop(); CCAction *pAction = m_pCurrentTarget->currentAction; // Make currentAction nil to prevent removeAction from salvaging it. m_pCurrentTarget->currentAction = NULL; removeAction(pAction); } m_pCurrentTarget->currentAction = NULL; } } // 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 no actions were scheduled during the cycle (issue #481) @H_404_14@if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0) { deleteHashElement(m_pCurrentTarget); } } // issue #635 m_pCurrentTarget = NULL; }