在上一篇博文中我們對(duì)cocos2d-x的動(dòng)作使用有了初步了解。今天,我們將通過閱讀部分cocos2d-x源碼來(lái)了解這個(gè)動(dòng)作系統(tǒng)是如何運(yùn)作的,,以及在使用時(shí)還有什么細(xì)節(jié)需要特別注意。
小弟初學(xué)cocos2d-x,如果文章中存在什么錯(cuò)誤或者不足,,望各位不吝賜教,及時(shí)指出,。
http://www.cnblogs.com/cocos2d-x/
動(dòng)作的祖先 - CCAction
CCAction是所有動(dòng)作類的基類,,我們來(lái)看一下CCAction的聲明:
1 /** 2 @brief Base class for CCAction objects. 3 */ 4 class CC_DLL CCAction : public CCObject 5 { 6 public: 7 CCAction(void); 8 virtual ~CCAction(void); 9 10 char * description(); 11 12 virtual CCObject* copyWithZone(CCZone *pZone); 13 14 //! return true if the action has finished 15 virtual bool isDone(void); 16 17 //! called before the action start. It will also set the target. 18 virtual void startWithTarget(CCNode *pTarget); 19 20 /** 21 called after the action has finished. It will set the 'target' to nil. 22 IMPORTANT: You should never call "[action stop]" manually. Instead, use: "target->stopAction(action);" 23 */ 24 virtual void stop(void); 25 26 //! called every frame with it's delta time. DON'T override unless you know what you are doing. 27 virtual void step(ccTime dt); 28 29 /** 30 called once per frame. time a value between 0 and 1 31 32 For example: 33 - 0 means that the action just started 34 - 0.5 means that the action is in the middle 35 - 1 means that the action is over 36 */ 37 virtual void update(ccTime time); 38 39 inline CCNode* getTarget(void) { return m_pTarget; } 40 /** The action will modify the target properties. */ 41 inline void setTarget(CCNode *pTarget) { m_pTarget = pTarget; } 42 43 inline CCNode* getOriginalTarget(void) { return m_pOriginalTarget; } 44 /** Set the original target, since target can be nil. 45 Is the target that were used to run the action. 46 Unless you are doing something complex, like CCActionManager, you should NOT call this method. 47 The target is 'assigned', it is not 'retained'. 48 @since v0.8.2 49 */ 50 inline void setOriginalTarget(CCNode *pOriginalTarget) { m_pOriginalTarget = pOriginalTarget; } 51 52 inline int getTag(void) { return m_nTag; } 53 inline void setTag(int nTag) { m_nTag = nTag; } 54 55 public: 56 /** Allocates and initializes the action */ 57 static CCAction* action(); 58 59 protected: 60 CCNode *m_pOriginalTarget; 61 /** The "target". 62 The target will be set with the 'startWithTarget' method. 63 When the 'stop' method is called, target will be set to nil. 64 The target is 'assigned', it is not 'retained'. 65 */ 66 CCNode *m_pTarget; 67 /** The action tag. An identifier of the action */ 68 int m_nTag; 69 };
1)description
跳過構(gòu)造和析構(gòu)函數(shù),第一個(gè)映入眼簾的是description成員函數(shù),,沒有注釋,。從字面上來(lái)看,這個(gè)函數(shù)應(yīng)該是返回類的描述,,那么它是如何定義的呢,?
1 char * CCAction::description() 2 { 3 char *ret = new char[100] ; 4 sprintf(ret,"<CCAction | Tag = %d>", m_nTag); 5 return ret; 6 }
可以看到description像strdup之類的函數(shù)那樣返回了在堆上分配的內(nèi)存。所以,,如果你調(diào)用了description,,那么你就要時(shí)刻注意,不要忘記將其釋放,。
2)isDone
再往下看可以看到copyWithZone成員函數(shù),,這是一個(gè)繼承自父類的函數(shù),跟動(dòng)作的關(guān)系不大,。接下來(lái)的函數(shù)叫做isDone,,通過它可以查詢動(dòng)作是否執(zhí)行完畢。大體上來(lái)說(shuō),,這個(gè)函數(shù)是通過對(duì)比流逝時(shí)間與目標(biāo)時(shí)間來(lái)得出結(jié)果,。但不同子類可能有著不同的實(shí)現(xiàn),例如:瞬時(shí)動(dòng)作總是返回true,,而CCRepeat使用執(zhí)行次數(shù)來(lái)取代流逝時(shí)間等等,。
3)startWithTarget/stop/step
在cocos2d-x中,一個(gè)動(dòng)作從startWithTarget開始,,到stop結(jié)束,。在動(dòng)作的執(zhí)行過程中,每幀調(diào)用一次step函數(shù),。這些構(gòu)成了一個(gè)動(dòng)作的完整流程,。
4)update
此函數(shù)接受一個(gè)百分比參數(shù),它表示動(dòng)作的完成進(jìn)度,。update根據(jù)這個(gè)百分比將目標(biāo)對(duì)象(可能是一個(gè)CCSprite對(duì)象,,也可能是別的什么)做出相應(yīng)的調(diào)整,。 筆者經(jīng)過統(tǒng)計(jì)發(fā)現(xiàn),只有2種函數(shù)調(diào)用過update,,一個(gè)是step,,另一個(gè)就是update本身。在第一種情況中,,step通過update來(lái)更新動(dòng)作的表現(xiàn),,在第二種情況中,這多半是一個(gè)包含了其它動(dòng)作的復(fù)雜動(dòng)作(比如CCActionEase類系),。
內(nèi)務(wù)府總管 - CCActionManager
前面我們提到startWithTarget、stop,、step構(gòu)成了一個(gè)動(dòng)作的完整流程,,那么這一整套流程是由誰(shuí)來(lái)驅(qū)動(dòng)的呢?下面我們就著重分析這個(gè)問題,。
1)startWithTarget
我們知道,,在runAction一個(gè)動(dòng)作之前,必須先創(chuàng)建這個(gè)動(dòng)作,。 以CCMoveTo為例,,我們是調(diào)用它的actionWithDuration成員函數(shù)來(lái)創(chuàng)建動(dòng)作的,那么是不是它調(diào)用了startWithTarget開始這個(gè)動(dòng)作呢,? 稍微思考一下,,不難發(fā)現(xiàn),絕對(duì)不是actionWithDuration調(diào)用了startWithTarget,。因?yàn)閟tartWithTarget需要一個(gè)Target,,也就是動(dòng)作的執(zhí)行者,而這里僅僅是創(chuàng)建動(dòng)作,,這個(gè)執(zhí)行者的參數(shù)無(wú)從獲取,。 按照這個(gè)思路,我們想到了runAction函數(shù),。此時(shí)動(dòng)作對(duì)象已創(chuàng)建,,執(zhí)行者也是明確的。
1 CCAction * CCNode::runAction(CCAction* action) 2 { 3 CCAssert( action != NULL, "Argument must be non-nil"); 4 CCActionManager::sharedManager()->addAction(action, this, !m_bIsRunning); 5 return action; 6 } 7 8 void CCActionManager::addAction(CCAction *pAction, CCNode *pTarget, bool paused) 9 { 10 CCAssert(pAction != NULL, ""); 11 CCAssert(pTarget != NULL, ""); 12 13 tHashElement *pElement = NULL; 14 // we should convert it to CCObject*, because we save it as CCObject* 15 CCObject *tmp = pTarget; 16 HASH_FIND_INT(m_pTargets, &tmp, pElement); 17 if (! pElement) 18 { 19 pElement = (tHashElement*)calloc(sizeof(*pElement), 1); 20 pElement->paused = paused; 21 pTarget->retain(); 22 pElement->target = pTarget; 23 HASH_ADD_INT(m_pTargets, target, pElement); 24 } 25 26 actionAllocWithHashElement(pElement); 27 28 CCAssert(! ccArrayContainsObject(pElement->actions, pAction), ""); 29 ccArrayAppendObject(pElement->actions, pAction); 30 31 pAction->startWithTarget(pTarget); 32 }
可以看到,,在CCActionManager的addAction里,,startWithTarget被調(diào)用了,這個(gè)動(dòng)作開始了執(zhí)行,。
2)step
還記得我們?cè)诮榻BCCAction時(shí)說(shuō)過,,step函數(shù)會(huì)按照每幀一次的速度被調(diào)用,因此必然存在一套驅(qū)動(dòng)機(jī)制,。通過對(duì)CCMoveTo的update下斷點(diǎn),,可以得到這樣的函數(shù)調(diào)用關(guān)系圖,。
可以看出CCActionManager的update調(diào)用了動(dòng)作的step函數(shù)(第23行),驅(qū)動(dòng)著動(dòng)作的執(zhí)行,。
1 // main loop 2 void CCActionManager::update(ccTime dt) 3 { 4 for (tHashElement *elt = m_pTargets; elt != NULL; ) 5 { 6 m_pCurrentTarget = elt; 7 m_bCurrentTargetSalvaged = false; 8 9 if (! m_pCurrentTarget->paused) 10 { 11 // The 'actions' CCMutableArray may change while inside this loop. 12 for (m_pCurrentTarget->actionIndex = 0; m_pCurrentTarget->actionIndex < m_pCurrentTarget->actions->num; 13 m_pCurrentTarget->actionIndex++) 14 { 15 m_pCurrentTarget->currentAction = (CCAction*)m_pCurrentTarget->actions->arr[m_pCurrentTarget->actionIndex]; 16 if (m_pCurrentTarget->currentAction == NULL) 17 { 18 continue; 19 } 20 21 m_pCurrentTarget->currentActionSalvaged = false; 22 23 m_pCurrentTarget->currentAction->step(dt); 24 25 if (m_pCurrentTarget->currentActionSalvaged) 26 { 27 // The currentAction told the node to remove it. To prevent the action from 28 // accidentally deallocating itself before finishing its step, we retained 29 // it. Now that step is done, it's safe to release it. 30 m_pCurrentTarget->currentAction->release(); 31 } else 32 if (m_pCurrentTarget->currentAction->isDone()) 33 { 34 m_pCurrentTarget->currentAction->stop(); 35 36 CCAction *pAction = m_pCurrentTarget->currentAction; 37 // Make currentAction nil to prevent removeAction from salvaging it. 38 m_pCurrentTarget->currentAction = NULL; 39 removeAction(pAction); 40 } 41 42 m_pCurrentTarget->currentAction = NULL; 43 } 44 } 45 46 // elt, at this moment, is still valid 47 // so it is safe to ask this here (issue #490) 48 elt = (tHashElement*)(elt->hh.next); 49 50 // only delete currentTarget if no actions were scheduled during the cycle (issue #481) 51 if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0) 52 { 53 deleteHashElement(m_pCurrentTarget); 54 } 55 } 56 57 // issue #635 58 m_pCurrentTarget = NULL; 59 }
而在CCActionManager初始化的時(shí)候就已經(jīng)向任務(wù)調(diào)度系統(tǒng)提交了注冊(cè),,保證自己每一幀都得到更新。
1 bool CCActionManager::init(void) 2 { 3 CCScheduler::sharedScheduler()->scheduleUpdateForTarget(this, 0, false); 4 m_pTargets = NULL; 5 return true; 6 }
3)stop
同理,,從上面的代碼中(第32行~第40行)可以看到,,每次執(zhí)行step后,會(huì)判斷一下動(dòng)作是否執(zhí)行完畢,,如果完畢則調(diào)用stop善后處理,,并移除該動(dòng)作。
小結(jié)
CCAction與CCActionManager相互配合,,一個(gè)井然有序的世界就這樣建立起來(lái)了,。 需要特別指出的是,大部分時(shí)候我們無(wú)需直接與CCActionManager打交道,,系統(tǒng)會(huì)自動(dòng)為我們打點(diǎn)好一切,。
|