大家都知道,遊戲其實就是在死循環中無限繪製,渲染的過程,裏面參雜了許多操作,接下來在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