好久沒用動筆寫博客了,今天是1024節,還是要表示下。而且今天檢查郵箱發現了5個垃圾評論,覺得很高興,也要來篇文章慶祝下。哈哈。
時間隔得太久,我都不記得我講到那裏來了。原來是打算將幾個特殊點的Action爲大家結尾,最後一看源碼,發現有點雜亂,待我整理好之後再寫。今天來講一講CCScheduler。之前講過,在ActionManager創建出來之後立馬就加入到CCScheduler中去了,然後用ActionManager來控制不同Action的Update。也就是說CCScheduler是Action能工作的保障。我再貼這段代碼回顧下:
// scheduler
m_pScheduler = new CCScheduler();
// action manager
m_pActionManager = new CCActionManager();
m_pScheduler->scheduleUpdateForTarget(m_pActionManager, kCCPrioritySystem, false);
而在mainLoop中的drawScene中也只有一個除繪製以外的邏輯,那就是update。
// Draw the Scene
void CCDirector::drawScene(void)
{
// calculate "global" dt
calculateDeltaTime();
//tick before glClear: issue #533
if(!m_bPaused)
{
m_pScheduler->update(m_fDeltaTime);
}
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
......
}
所有的根源都是那個m_pScheduler的update。可見CCScheduler的重要性。CCScheduler的構造函數只是簡單的初始化了幾個指針變量,沒做任何的init邏輯處理。而且它不是單間,雖說cocos2d-x只支持一個scheduler update。需要了解CCScheduler還有一個伴生的概念叫做CCTimer,我們可以叫做定時器。CCScheduler內建了對update函數的支持,如果你要Scheduler自定義的函數就要藉助CCTimer了,它就相當於一個適配器。
以上面那個scheduleUpdateForTarget爲例,我們看起代碼:
void CCScheduler::scheduleUpdateForTarget(CCObject *pTarget, int nPriority, bool bPaused)
{
tHashUpdateEntry *pHashElement = NULL;
HASH_FIND_INT(m_pHashForUpdates, &pTarget, pHashElement);
if (pHashElement)
{
#if COCOS2D_DEBUG >= 1
CCAssert(pHashElement->entry->markedForDeletion,"");
#endif
// TODO: check if priority has changed!
pHashElement->entry->markedForDeletion = false;
return;
}
// most of the updates are going to be 0, that's way there
// is an special list for updates with priority 0
if (nPriority == 0)
{
appendIn(&m_pUpdates0List, pTarget, bPaused);
}
else if (nPriority < 0)
{
priorityIn(&m_pUpdatesNegList, pTarget, nPriority, bPaused);
}
else
{
// priority > 0
priorityIn(&m_pUpdatesPosList, pTarget, nPriority, bPaused);
}
}
前面的檢測保障其不能重複添加,但是允許在一幀之間先刪除再添加。我們可以發現這部分代碼和ActionManager是很像的, 這種標記刪除的手法在遊戲開發中很常見,如果你要統一管理自己的對象,記得也要採用這種方法,可以避免掉很多問題。與ActionManager不同的是,他內部採用了三個update容器來管理。我們看到
#define kCCPrioritySystem INT_MIN
可見,Action的update的優先級是最高的。從註釋上我們可以看出,默認的update的優先級是0,而且沒有特殊情況下優先級大於0的容器中是沒有元素的。這種設計是必要的,因爲有些邏輯比較分散,最後又要統一處理。添加到大於0的優先級中就可以了。
上面可以看到真正添加到update容器中的是priorityIn這個函數,添加到容器的步驟要注意幾點,新建一個容器元素,排好優先級,而且有個特殊的地方是它用了一個Hash表來快速的update這個列表中的函數。所以還要添加到Hash表中去。上面就是CCScheduler update的大致流程。我們知道CCScheduler除了update之外,還可以指定自定義的函數,也就是下面這個函數做的:
scheduleSelector(SEL_SCHEDULE pfnSelector,
CCObject *pTarget, float fInterval,
unsigned int repeat, float delay, bool bPaused)
這個函數的參數值簡直是喪心病狂,好在CCNode再次封裝了一次。這種手法與Action類似,我就不多做介紹了。觀其代碼:
void CCScheduler::scheduleSelector(SEL_SCHEDULE pfnSelector,CCObject*pTarget,float fInterval,unsigned int repeat,float delay,bool bPaused)
{
CCAssert(pfnSelector,"Argument selector must be non-NULL");
CCAssert(pTarget,"Argument target must be non-NULL");
tHashTimerEntry* pElement=NULL;
HASH_FIND_INT(m_pHashForTimers,&pTarget,pElement);
if(!pElement)
{
pElement=(tHashTimerEntry*)calloc(sizeof(*pElement),1);
pElement->target=pTarget;
if(pTarget)
{
pTarget->retain();
}
HASH_ADD_INT(m_pHashForTimers,target,pElement);
// Is this the 1st element ? Then set the pause level to all the selectors of this target
pElement->paused=bPaused;
}
else
{
CCAssert(pElement->paused==bPaused,"");
}
if(pElement->timers==NULL)
{
pElement->timers=ccArrayNew(10);
}
else
{
for(unsignedinti=0;i<pElement->timers->num;++i)
{
CCTimer*timer=(CCTimer*)pElement->timers->arr[i];
if(pfnSelector==timer->getSelector())
{
CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f",timer->getInterval(),fInterval);
timer->setInterval(fInterval);
return;
}
}
ccArrayEnsureExtraCapacity(pElement->timers,1);
}
CCTimer* pTimer=newCCTimer();
pTimer->initWithTarget(pTarget,pfnSelector,fInterval,repeat,delay);
ccArrayAppendObject(pElement->timers,pTimer);
pTimer->release();
}
這些操作簡直與Action如出一轍,他爲每個新加的Target分配了10個定時器的空間,而且允許重複進入(其作用是更新參數)。再看其update函數,可以說這個函數是cocos2d-x的邏輯主體,他首先處理3個update容器中的元素,再處理自定義的timer,最後刪除標記爲刪除的管理對象。代碼我就不貼了。其他一些管理邏輯和要注意的問題與ActionManager是類似的,就不再多囉嗦了。大家可以對比來加深印象。