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();
将我们在后台绘制完成的帧序列,进行与前台显示的帧序列进行交换,显示我们绘制好的下一场景。对我而言,我理解它就是类似于双缓存,我们在后台绘制后之后,一次性显示在屏幕上,避免了卡顿的出现。
本文难免有所错误,如有问题欢迎留言。