一、OpenGL结构

cocos2dx程序本质上就是一个OpenGL程序,掌握OpenGL基础是掌握好cocos的前提。OpenGL相关知识见OpenGL部分。
下面列出一个简单的OpenGL程序来展示其基本结构:
语雀内容

二、主循环

当Application对象被初始化后,就可以以设定的帧率执行循环,Application::run是整个程序的入口,Director::mainLoop中是一个循环的所有工作。每一帧就是一次循环,每帧发生的事情如下:

  • 处理用户输入:如触摸、鼠标、重力感应
  • 动画计算:优先级最高的scheduler,Scheduler::PRIORITY_SYSTEM。
  • 物理模拟
  • 逻辑更新:scheduler,
  • UI树遍历
  • UI绘制
  • 交换缓冲区
    • 双缓冲机制,一个默认缓冲,一个离线缓冲。
    • 屏幕展示的为默认缓冲内容,正在渲染的数据输出到离线帧缓冲。
    • 渲染完成,交换两个缓冲。

下面是win32平台的Application中关于游戏主循环的代码。

  1. int Application::run(){ // 游戏启动
  2. ......;
  3. initGLContextAttrs(); // 初始化GL State
  4. ......;
  5. applicationDidFinishLaunching(); // Initialize instance and cocos2d
  6. ......;
  7. while(!glview->windowShouldClose()) // 主循环
  8. {
  9. ......
  10. if (interval >= ...) // 上一帧距离当前时间间隔>=设置的帧间隔
  11. { // animationInterval,则立即进行循环
  12. ......;
  13. director->mainLoop(); // 游戏主循环
  14. glview->pollEvents(); // 轮询事件,使事件发生会触发回调。
  15. }
  16. else // 时间间隔没有超过,主线程强行sleep。
  17. {
  18. ......
  19. }
  20. }
  21. // Director should still do a cleanup if the window was closed manually.
  22. if (glview->isOpenGLReady()) { // 主循环终止,进行收尾工作。
  23. director->end();
  24. director->mainLoop();
  25. director = nullptr;
  26. }
  27. }
  28. //**********************************************
  29. void Director::mainLoop(){
  30. ......
  31. else if (! _invalid)
  32. {
  33. drawScene(); // 渲染绘制一帧
  34. //每帧结束,清空栈顶(当前)AutoReleasePool,并release一次所有auto release对象
  35. PoolManager::getInstance()->getCurrentPool()->clear();
  36. }
  37. }
  38. //**********************************************
  39. void Director::drawScene() // 循环主逻辑
  40. {
  41. // calculate "global" dt
  42. calculateDeltaTime(); // 计算帧间隔
  43. if (_openGLView)
  44. {
  45. _openGLView->pollEvents(); // 处理队列中的事件(触摸、按键等,会触发注册绑定的回调)
  46. }
  47. //tick before glClear: issue #533
  48. if (! _paused)
  49. {
  50. _eventDispatcher->dispatchEvent(_eventBeforeUpdate);
  51. _scheduler->update(_deltaTime); // 执行schedule
  52. _eventDispatcher->dispatchEvent(_eventAfterUpdate);
  53. }
  54. _renderer->clear(); // 清除颜色缓冲、深度缓冲
  55. experimental::FrameBuffer::clearAllFBOs(); // 清空当前所有帧缓冲的附件(颜色、深度、模板)
  56. _eventDispatcher->dispatchEvent(_eventBeforeDraw); // 绘制前事件
  57. /* to avoid flickr, nextScene MUST be here: after tick and before draw.
  58. * FIXME: Which bug is this one. It seems that it can't be reproduced with v0.9
  59. */
  60. if (_nextScene)
  61. {
  62. setNextScene(); // 切换到下一个场景
  63. }
  64. pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); // 压栈初始MV矩阵。
  65. if (_runningScene)
  66. {
  67. ......
  68. // 清除当前的绘制状态信息
  69. _renderer->clearDrawStats();
  70. // 遍历runningScene为根节点的UI树生成渲染指令加入渲染队列,然后执行渲染。
  71. if(_openGLView)
  72. _openGLView->renderScene(_runningScene, _renderer);
  73. _eventDispatcher->dispatchEvent(_eventAfterVisit); // UI渲染树遍历完成并绘制完成事件
  74. }
  75. // 遍历生成渲染指令,入队列。
  76. if (_notificationNode)
  77. {
  78. _notificationNode->visit(_renderer, Mat4::IDENTITY, 0); // 遍历notifications node生成绘制指令并入绘制队列
  79. }
  80. updateFrameRate(); // 更新帧率
  81. if (_displayStats)
  82. {
  83. #if !CC_STRIP_FPS
  84. showStats(); // show FPS
  85. #endif
  86. }
  87. // 绘制上面的notificationNode
  88. _renderer->render();
  89. _eventDispatcher->dispatchEvent(_eventAfterDraw); // 所有绘制工作完成事件
  90. popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); // MV矩阵弹栈
  91. _totalFrames++; // 记录帧数
  92. // swap buffers
  93. if (_openGLView)
  94. {
  95. _openGLView->swapBuffers(); // 双缓冲机制:调用的glfwSwapBuffers
  96. }
  97. if (_displayStats)
  98. {
  99. #if !CC_STRIP_FPS
  100. calculateMPF();
  101. #endif
  102. }
  103. }
  104. void Director::calculateDeltaTime() // 计算帧间隔
  105. {
  106. // new delta time. Re-fixed issue #1277
  107. if (_nextDeltaTimeZero)
  108. {
  109. _deltaTime = 0;
  110. _nextDeltaTimeZero = false;
  111. _lastUpdate = std::chrono::steady_clock::now();
  112. }
  113. else
  114. {
  115. // delta time may passed by invoke mainLoop(dt)
  116. if (!_deltaTimePassedByCaller)
  117. {
  118. auto now = std::chrono::steady_clock::now();
  119. _deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(now - _lastUpdate).count() / 1000000.0f;
  120. _lastUpdate = now;
  121. }
  122. _deltaTime = MAX(0, _deltaTime);
  123. }
  124. #if COCOS2D_DEBUG
  125. // If we are debugging our code, prevent big delta time
  126. if (_deltaTime > 0.2f)
  127. {
  128. _deltaTime = 1 / 60.0f;
  129. }
  130. #endif
  131. }