一、OpenGL结构
cocos2dx程序本质上就是一个OpenGL程序,掌握OpenGL基础是掌握好cocos的前提。OpenGL相关知识见OpenGL部分。
下面列出一个简单的OpenGL程序来展示其基本结构:
语雀内容
二、主循环
当Application对象被初始化后,就可以以设定的帧率执行循环,Application::run是整个程序的入口,Director::mainLoop中是一个循环的所有工作。每一帧就是一次循环,每帧发生的事情如下:
- 处理用户输入:如触摸、鼠标、重力感应
- 动画计算:优先级最高的scheduler,Scheduler::PRIORITY_SYSTEM。
- 物理模拟
- 逻辑更新:scheduler,
- UI树遍历
- UI绘制
- 交换缓冲区
- 双缓冲机制,一个默认缓冲,一个离线缓冲。
- 屏幕展示的为默认缓冲内容,正在渲染的数据输出到离线帧缓冲。
- 渲染完成,交换两个缓冲。
下面是win32平台的Application中关于游戏主循环的代码。
int Application::run(){ // 游戏启动......;initGLContextAttrs(); // 初始化GL State......;applicationDidFinishLaunching(); // Initialize instance and cocos2d......;while(!glview->windowShouldClose()) // 主循环{......if (interval >= ...) // 上一帧距离当前时间间隔>=设置的帧间隔{ // animationInterval,则立即进行循环......;director->mainLoop(); // 游戏主循环glview->pollEvents(); // 轮询事件,使事件发生会触发回调。}else // 时间间隔没有超过,主线程强行sleep。{......}}// Director should still do a cleanup if the window was closed manually.if (glview->isOpenGLReady()) { // 主循环终止,进行收尾工作。director->end();director->mainLoop();director = nullptr;}}//**********************************************void Director::mainLoop(){......else if (! _invalid){drawScene(); // 渲染绘制一帧//每帧结束,清空栈顶(当前)AutoReleasePool,并release一次所有auto release对象PoolManager::getInstance()->getCurrentPool()->clear();}}//**********************************************void Director::drawScene() // 循环主逻辑{// calculate "global" dtcalculateDeltaTime(); // 计算帧间隔if (_openGLView){_openGLView->pollEvents(); // 处理队列中的事件(触摸、按键等,会触发注册绑定的回调)}//tick before glClear: issue #533if (! _paused){_eventDispatcher->dispatchEvent(_eventBeforeUpdate);_scheduler->update(_deltaTime); // 执行schedule_eventDispatcher->dispatchEvent(_eventAfterUpdate);}_renderer->clear(); // 清除颜色缓冲、深度缓冲experimental::FrameBuffer::clearAllFBOs(); // 清空当前所有帧缓冲的附件(颜色、深度、模板)_eventDispatcher->dispatchEvent(_eventBeforeDraw); // 绘制前事件/* 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); // 压栈初始MV矩阵。if (_runningScene){......// 清除当前的绘制状态信息_renderer->clearDrawStats();// 遍历runningScene为根节点的UI树生成渲染指令加入渲染队列,然后执行渲染。if(_openGLView)_openGLView->renderScene(_runningScene, _renderer);_eventDispatcher->dispatchEvent(_eventAfterVisit); // UI渲染树遍历完成并绘制完成事件}// 遍历生成渲染指令,入队列。if (_notificationNode){_notificationNode->visit(_renderer, Mat4::IDENTITY, 0); // 遍历notifications node生成绘制指令并入绘制队列}updateFrameRate(); // 更新帧率if (_displayStats){#if !CC_STRIP_FPSshowStats(); // show FPS#endif}// 绘制上面的notificationNode_renderer->render();_eventDispatcher->dispatchEvent(_eventAfterDraw); // 所有绘制工作完成事件popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); // MV矩阵弹栈_totalFrames++; // 记录帧数// swap buffersif (_openGLView){_openGLView->swapBuffers(); // 双缓冲机制:调用的glfwSwapBuffers}if (_displayStats){#if !CC_STRIP_FPScalculateMPF();#endif}}void Director::calculateDeltaTime() // 计算帧间隔{// new delta time. Re-fixed issue #1277if (_nextDeltaTimeZero){_deltaTime = 0;_nextDeltaTimeZero = false;_lastUpdate = std::chrono::steady_clock::now();}else{// delta time may passed by invoke mainLoop(dt)if (!_deltaTimePassedByCaller){auto now = std::chrono::steady_clock::now();_deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(now - _lastUpdate).count() / 1000000.0f;_lastUpdate = now;}_deltaTime = MAX(0, _deltaTime);}#if COCOS2D_DEBUG// If we are debugging our code, prevent big delta timeif (_deltaTime > 0.2f){_deltaTime = 1 / 60.0f;}#endif}
