cocos2dx win32下的MainLoop分析(啓動流程)

大家都知道,遊戲其實就是在死循環中無限繪製,渲染的過程,裏面參雜了許多操作,接下來在win32中查看一下cocos2dx的循環過程

一般遊戲主循環的簡單邏輯如下

float dt; 
while (1) 

... 
update(dt); //通過時間差更新數據 
present(dt);//通過時間差呈現、繪製遊戲畫面 
... 


我是用VS在WIN32平臺下查看,要分析cocos2dx的啓動流程那麼就從main函數開始吧,打開main.cpp文件可以看到這裏有win32的入口函數,

下方代碼爲main函數中的核心代碼

// create the application instance 
AppDelegate app; //實例化應用代理對象,這個對象是CCApplication的子類, 
//實現了applicationDidFinishLaunching()等方法,目的 
//目的是爲了在底層框架中回調,做一些初始化或者結尾的工作 
CCEGLView* eglView = CCEGLView::sharedOpenGLView();//繼承和封裝了一些對Opengl接口的操作。不同平臺實現不一樣 
eglView->setViewName("TestCpp"); 
eglView->setFrameSize(480, 320); 
return CCApplication::sharedApplication()->run();//一個單實例類,通過run方法進入引擎主循環 

接着進入 CCApplication::run();\win32\CCApplication.cpp 

int CCApplication::run()
{
    PVRFrameEnableControlWindow(false);//這個好像是專門用於windows的,寫註冊表禁用pvr frame。
    // Main message loop:
    MSG msg;
     //獲取cpu滴答數相關數據結構,可以認爲是高精度的計時器。
    LARGE_INTEGER nFreq;
    LARGE_INTEGER nLast;
    LARGE_INTEGER nNow;
    QueryPerformanceFrequency(&nFreq);
    QueryPerformanceCounter(&nLast);
    // Initialize instance and cocos2d.
    //執行AppDeletegate重載的applicationDidFinishLaunching函數,如果進入這個函數裏邊可以看到這個函數裏代碼初始化了cocos2dx的導演類對象、文件模塊對象,設置幀數創建場景等等。
    if (!applicationDidFinishLaunching())
    {
        return 0;
    }
    //創建窗口,並顯示。
    CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();
    pMainWnd->centerWindow();
    ShowWindow(pMainWnd->getHWnd(), SW_SHOW);
    //消息循環。
    while (1)
    {
        if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // Get current time tick.
            QueryPerformanceCounter(&nNow);
            // If it's the time to draw next frame, draw it, else sleep a while.控制幀數的。<span style="font-family: Arial, Helvetica, sans-serif;">判斷時間差是不是達到了到下一幀的條件</span>
            if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)
            {
<span style="white-space:pre">		</span>//主要是看鎖定多少的FPS 
                nLast.QuadPart = nNow.QuadPart;
                CCDirector::sharedDirector()->mainLoop();//進入引擎的主循環
            }
            else
            {
                Sleep(0);//如果不需要繪製下一幀那麼釋放CPU控制權。
            }
            continue;
        }
        if (WM_QUIT == msg.message)
        {
            // Quit message loop.
            break;
        }
        // Deal with windows message.消息分發
        if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int) msg.wParam;
}


從run()函數代碼可以看出來,渲染主要發生在CCDirector::sharedDirector()->mainLoop();裏邊。mainLoop()函數循環檢查程序是否退出的標誌位(m_bPurgeDirecotorInNextLoop,導演類調用end()方法將會使這個標誌位記爲true。這個方法在窗口過程收到WM_CLOSE消息的時候調用的),如果爲true則清除資源。如果m_bPurgeDirecotorInNextLoop爲false並且 m_bInvalid 並沒有被置爲true(這個變量由startAnimation()和stopAnimation()控制) ,那麼繪製場景以及檢查是否需要釋放沒有引用計數的內存資源。

void CCDisplayLinkDirector::mainLoop(void)//實際上CCDirector::sharedDirector();獲取的是CCDisplayLinkDirector類型對象。
{
    if (m_bPurgeDirecotorInNextLoop)//是否停止渲染。進入下一個主循環,也就是結束這次的主循環,就淨化,也就是一些後期處理
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)//可以通過調用stopAnimation()的方式停止渲染,在win32下通常在對窗口進行縮小的時候會被調用。
     {
         drawScene();//繪製屏幕 
         // release the objects
         CCPoolManager::sharedPoolManager()->pop(); //釋放一些沒有用的對象,主要保件內存的合理管理   
     }
}
CCDisplayLinkDirector是CCDisplay的子類,從命名就應該可以很清晰的知道它的用處。通過CCDirector::sharedDirector()代碼,得到的都是CCDisplayLinkDirector對象。通過drawScene()代碼就可以實現場景的繪製了
// Draw the Scene
void CCDirector::drawScene(void)
{
    // calculate "global" dt
    calculateDeltaTime();//計算時間差 

    //tick before glClear: issue #533
    if (! m_bPaused)//如果不暫停,就更新數據
        m_pScheduler->update(m_fDeltaTime);//調度者對象,是整個框架中,非常重要的東東,他負責者引擎中精靈、動作等的調度,而裏面所用的數據結構的組織,一定程<span style="white-space:pre">	</span>度決定者引擎的效率。
    }

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    /* to avoid flickr, nextScene MUST be here: after tick and before draw.
     XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
    if (m_pNextScene)
    {
        setNextScene();//如果有m_pNextScene對象不爲空,就說明需要調用到新的場景中,在其中onEnter()、onEnterTransitionDidFinish()等函數被回調。
    }

    kmGLPushMatrix();//opengl:把當前矩陣放到棧中

    // draw the scene
    if (m_pRunningScene)
    {
        m_pRunningScene->visit();//通過訪問方法,去繪製場景中包含的每個層和每個層中的每個節點的draw, 這裏面是一個遞歸的過程,其中transform()方法實現,open<span style="white-space:pre">	</span>gl矩陣的變化 //,移動,旋轉等。
    }

    // draw the notifications node
    if (m_pNotificationNode)
    {
        m_pNotificationNode->visit()//繪製通知節點
    }
    
    if (m_bDisplayStats)
    {
        showStats();
    }
    
    kmGLPopMatrix();//opengl:把當前矩陣從棧中移除,回覆之前的矩陣

    m_uTotalFrames++;//記錄總幀數 </span>

    // swap buffers
    if (m_pobOpenGLView)
    {
        m_pobOpenGLView->swapBuffers();//opengl:交換幀緩衝區,把繪製的東東顯示在屏幕。
    }
    
    if (m_bDisplayStats)
    {
        calculateMPF();
    }
}

參考http://blog.csdn.net/crazy_number/article/details/37915145


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章