cocos2d-x 源碼剖析(11)

好久沒用動筆寫博客了,今天是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是類似的,就不再多囉嗦了。大家可以對比來加深印象。

發佈了2 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章