cocos2dx-mainloop

cocos2dx版本3.10(各個版本有細微的差別)


簡略圖
簡略


run

int Application::run()
{
    ...

    while(!glview->windowShouldClose())// 是否退出遊戲
    {
        QueryPerformanceCounter(&nNow);// 獲取當前時間
        if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
        {
            nLast.QuadPart = nNow.QuadPart;// 記錄一幀開始的時間

            director->mainLoop();// 進行圖形渲染與遊戲邏輯處理
            glview->pollEvents();// 進行遊戲的交互處理
        }
        else
        {
            Sleep(0);
        }
    }
    if (glview->isOpenGLReady())
    {
        director->end();
        director->mainLoop();
        director = nullptr;
    }
    glview->release();
    return 0;
}

圖解
主循環
mainLoop:負責調用定時器,繪圖,發送全局通知,並處理內存回收池。每一幀進行一次調用。主要實現的是mainLoop中的drawScene方法,稍後會提到


接下來我們着重探討下mainLoop的實現過程
mainLoop

void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (_restartDirectorInNextLoop)
    {
        _restartDirectorInNextLoop = false;
        restartDirector();
    }
    else if (! _invalid)
    {
        drawScene();

        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();//進行內存清理
    }
}

_purgeDirectorInNextLoop:是否在下一循環前清除Director。在退出消息while循環時,director->end()將_purgeDirectorInNextLoop設置爲true,再次調用mainLoop時,就進行purgeDirector()銷燬Director的工作
_restartDirectorInNextLoop:是否重新啓動Director,通過調用Director方法restart將_restartDirectorInNextLoop設置爲true,進行調用restartDirector()重新啓動
_invalid:是否爲無效的場景,該值初始化爲false

drawScene():主要實現了圖形渲染以及消息事件的處理,接下來我們分析下drawScene的實現
PoolManager::getInstance()->getCurrentPool()->clear():渲染一幀後,進行對當前內存池的清理工作(每一幀都會創建一個內存池)


drawScene

void Director::drawScene()
{
    // calculate "global" dt
    calculateDeltaTime();

    if (_openGLView)
    {
        _openGLView->pollEvents();
    }

    //tick before glClear: issue #533
    if (! _paused)
    {
        _eventDispatcher->dispatchEvent(_eventBeforeUpdate);
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }

    _renderer->clear();
    experimental::FrameBuffer::clearAllFBOs();
    /* to avoid flickr, nextScene MUST be here: after tick and before draw.
     * FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9
     */
    if (_nextScene)
    {
        setNextScene();
    }

    pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    if (_runningScene)
    {
#if (CC_USE_PHYSICS || (CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION) || CC_USE_NAVMESH)
        _runningScene->stepPhysicsAndNavigation(_deltaTime);
#endif
        //clear draw stats
        _renderer->clearDrawStats();

        //render the scene
        _runningScene->render(_renderer);

        _eventDispatcher->dispatchEvent(_eventAfterVisit);
    }

    // draw the notifications node
    if (_notificationNode)
    {
        _notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
    }

    if (_displayStats)
    {
        showStats();
    }
    _renderer->render();

    _eventDispatcher->dispatchEvent(_eventAfterDraw);

    popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    _totalFrames++;

    // swap buffers
    if (_openGLView)
    {
        _openGLView->swapBuffers();
    }

    if (_displayStats)
    {
        calculateMPF();
    }
}

1、calculateDeltaTime()
計算一次幀循環的時間(上一幀與當前幀的時間差),保存在全局變量_deltaTime中
_deltaTime = (now.tv_sec - _lastUpdate->tv_sec) + (now.tv_usec - _lastUpdate->tv_usec) / 1000000.0f;
2、

if (! _paused)//Director 是否暫停
    {
        _eventDispatcher->dispatchEvent(_eventBeforeUpdate);
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }

_eventDispatcher->dispatchEvent(_eventBeforeUpdate):處理用戶定時事件之前的觸摸消息事件
_scheduler->update(_deltaTime):處理定時事件,根據我們註冊的定時事件的優先級進行處理定時事件。我們註冊的定時事件,底層就是使用_scheduler進行註冊的。
_eventDispatcher->dispatchEvent(_eventAfterUpdate):事件處理完的通知事件,在上一幀的事件處理完成是產生該事件。如果用戶註冊了相關的事件,這裏將會使用EventDispatcher 來進行調用。例如你可以註冊該事件進行檢測更新完一次所需要的時間。

_eventBeforeUpdate = new (std::nothrow) EventCustom(EVENT_BEFORE_UPDATE);
_eventAfterUpdate = new (std::nothrow) EventCustom(EVENT_AFTER_UPDATE);

3、_renderer->clear()
清理渲染器(包括清楚openGL的buffer和屏幕的渲染)
4、experimental::FrameBuffer::clearAllFBOs()
清理幀緩衝區
5、setNextScene()

void Director::setNextScene()
{
    bool runningIsTransition = dynamic_cast<TransitionScene*>(_runningScene) != nullptr;
    bool newIsTransition = dynamic_cast<TransitionScene*>(_nextScene) != nullptr;

    // If it is not a transition, call onExit/cleanup
     if (! newIsTransition)
     {
         if (_runningScene)
         {
             _runningScene->onExitTransitionDidStart();
             _runningScene->onExit();
         }

         // issue #709. the root node (scene) should receive the cleanup message too
         // otherwise it might be leaked.
         if (_sendCleanupToScene && _runningScene)
         {
             _runningScene->cleanup();
         }
     }

    if (_runningScene)
    {
        _runningScene->release();
    }
    _runningScene = _nextScene;
    _nextScene->retain();
    _nextScene = nullptr;

    if ((! runningIsTransition) && _runningScene)
    {
        _runningScene->onEnter();
        _runningScene->onEnterTransitionDidFinish();
    }
}

主要進行了對正在運行的場景的清理,設置當前運行的場景_runningScene爲下個將要運行的場景_nextScene,再進行onEnter調用當前的場景。
onEnter() 是在進入場景的一瞬間就開始執行了。
onEnterTransitionDidFinish() 是在完全進入場景後開始執行的。
6、_renderer->clearDrawStats();
統計渲染的數據
7、_runningScene->render(_renderer);
調用當前場景的渲染方法進行對當前場景(此時當前場景已經切換爲下一個場景)的渲染
8、_eventDispatcher->dispatchEvent(_eventAfterVisit);
場景渲染完成產生的用戶通知事件,如果用戶註冊了該事件,該場景渲染完成時將進行調用。
9、_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
訪問_notificationNode的子節點,並進行遞歸繪製。
_renderer:渲染器
Mat4::IDENTITY:變換矩陣
0:渲染器標誌
10、showStats();
更新幀率的狀態
11、_renderer->render();
進行圖像的渲染,將我們場景需要顯示的東西顯示到顯示器上面。
12、_eventDispatcher->dispatchEvent(_eventAfterDraw);
圖像渲染完成產生的用戶通知事件,如果用戶註冊了該事件,該圖像渲染完成時將進行調用。
13、popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
彈出指定類型矩陣堆棧的頂層矩陣。
14、_totalFrames++;
進行對幀計算的值自加一
15、 _openGLView->swapBuffers();
將我們在後臺繪製完成的幀序列,進行與前臺顯示的幀序列進行交換,顯示我們繪製好的下一場景。對我而言,我理解它就是類似於雙緩存,我們在後臺繪製後之後,一次性顯示在屏幕上,避免了卡頓的出現。

本文難免有所錯誤,如有問題歡迎留言。

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