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();
將我們在後臺繪製完成的幀序列,進行與前臺顯示的幀序列進行交換,顯示我們繪製好的下一場景。對我而言,我理解它就是類似於雙緩存,我們在後臺繪製後之後,一次性顯示在屏幕上,避免了卡頓的出現。
本文難免有所錯誤,如有問題歡迎留言。