3 cocos2dx 3.0 源碼分析-mainLoop詳細


 

簡述:
 
我靠上面圖是不是太大了, 有點看不清了。 
總結一下過程:
之前說過的appController 之后經過了若干初始化, 最后調用了displayLinker 的定時調用, 這里調用了函數 mainLoop, 我們在這里進行詳細分解這個mainloop 到底做些些啥, 看完這篇,應該能初步了解到cocos2dx是如何把Scene或界面元素顯示到屏幕上的。 
 
我們主要分析的是 void DisplayLinkDirector::mainLoop() 
這個函數, 它最后調用了Director::drawScene 這里對drawScene進行展開,不明白這個mainloop如何調用的, 可以參考一下我寫的第一篇分析 “1 cocos2dx 3.0 源碼分析-程序啟動與主循環”。 
 
目錄:
 
Director::drawScene()  主要做了哪些事呢? 其中省去了一些我覺得無關緊要的小東西,最好你看看源碼, 看哪里我沒說到。
 
1 計算時間差, 2幀之前的時間差,calculateDeltaTime()
2 定時任務調用 _scheduler->update(_deltaTime);
3 事件處理 EventAfterUpdate
4 設置當前的下一個場景, 主要就是把當前場景釋放掉, 之后把_nextScene 設置為當前的場景
5 調用當前場景的渲染方法        _runningScene->render(_renderer);
6 事件處理意思是Visit調用完了, _eventAfterVisit
7 調用渲染引擎進行渲染  _renderer->render();
8 事件處理 _eventAfterDraw
9 調用 openGLView 平台提供的屏幕顯示方法, _openGLView->swapBuffers();
 
下面我一個一個的展開, 簡單的介紹一下, 比較復雜的咱們留在后面進行分析說明:
 
1 計算時間差, 2幀之前的時間差 calculateDeltaTime()
 
我們在update(float dt) 中得到的float dt.  就是這里計算的,計算了, 上次調用和這次調用之前的差。 
 
2 定時任務調用 _scheduler->update(_deltaTime);
 
Scheduler 本身就是用來處理更新函數調用的。 我們在自己的場景中經常會使用schedulerUpdate(), 然后實現一個update(float dt) 方法,其實就是在內部把當前的調用請求注冊到了 scheudler 中, 之后在這個主循環每幀時進行統一的調用update() 函數. 
這里可以看到, 這個update調用的時機還是蠻早的呢, 啥都沒干呢, 就先調用它了。 稍等我要把這點記住。 
 
3 事件處理 EventAfterUpdate
 
 _eventDispatcher->dispatchEvent(_eventAfterUpdate);
 
觸發事件“更新調用完了”, 這里大家也不用糾結, 我們可以簡單的認為這是一個通知,發送了一個 “更新調用完了“的通知,之后由事件管理器EventDispatcher 來調用注冊了這個事件的函數。 
 
事件處理器Dispatcher 典型的觀察者模式, 細節這里不說, 以后單出一章說明。 
 
4 設置當前的下一個場景,其實就場景的處理, 場景的用處理就在這里體現了。 
setNextScene();
 
主要就是把當前場景釋放掉, 之后把_nextScene 設置為當前的場景, 還記得我們在調用場景時要調用一個 director->runWithScene(scene);
這里的scene 可以被認為是下一個場景了。 下面是它的幾個過程
 
1> 清理正在運行的Scene
_runningScene->release();
2> 設置當前Scene
 _runningScene = _nextScene;
_nextScene->retain();
_nextScene = nullptr;
3> 調用當前場景onEnter
_runningScene->onEnter();
 
5 調用當前場景的渲染方法        _runningScene->render(_renderer);
 
1> 加載當前的Camera
Camera::_visitingCamera = defaultCamera;
2> 裝載當前Camera的投影矩陣  director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION,Camera::_visitingCamera->getViewProjectionMatrix());
3> 調用visit; 這個函數可重要了,這個函數主要是分別調用當前節點下的子節點的visit(), 之后調用了自身的draw將相應的Command命令把渲染請求加入的當前readerer的渲染隊列中。   visit(renderer, Mat4::IDENTITY, 0);
3.1 對當前子節點排序 sortAllChildren
3.2 遞歸調用當前localZOrder 小於當前元素的子元素的visit() 
       
// draw children zOrder < 0
        for( ; i < _children.size(); i++ )
        {
            auto node = _children.at(i);

            if ( node && node->_localZOrder < 0 )
                node->visit(renderer, _modelViewTransform, flags);
            else
                break;
        }

 

3.3 調用自身的draw()把指定的渲染命令添加到當前的渲染隊列
   if (visibleByCamera)
    this->draw(renderer, _modelViewTransform, flags);
 
 下面是一個標准的Sprite的Draw() 
void Sprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    // Don't do calculate the culling if the transform was not updated
    _insideBounds = (flags & FLAGS_TRANSFORM_DIRTY) ? renderer->checkVisibility(transform,_contentSize) : _insideBounds;
    if(_insideBounds)
    {
        _quadCommand.init(_globalZOrder, _texture->getName(), getGLProgramState(),_blendFunc, &_quad, 1, transform);
        renderer->addCommand(&_quadCommand);
    }
}
 
3.4 遞歸調用其它子節點的visit() 
 
 for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
            (*it)->visit(renderer, _modelViewTransform, flags);
 

 

4> 調用渲染引擎進行渲染 renderer->render();
這塊渲染相關的, 我不展開了,回頭會詳細說, 大家知道引擎就是使用它來把元素顯示的屏幕上的,它封裝了大部分的OpenGL的指令。 
 
6 事件處理意思是Visit調用完了, _eventAfterVisit
 
通知Visit調用完了, 這個visit就是把渲染請求封裝成命令,壓入Render的渲染隊列。 
 
7 調用渲染引擎進行渲染  _renderer->render();
 
其它渲染操作。 就是畫東西
 
8 事件處理 _eventAfterDraw
 
通知我畫完了。 
 
9 調用 openGLView 平台提供的屏幕顯示方法, _openGLView->swapBuffers();
 
所有操作做完了, 這里我們的操作都還只在 OpenGL 里幀緩存中, 我們要把它顯示在屏幕上 就要調用 不同平台提供的對OpenGLES 的支持函數, iOS上調用的是 [context_presentRenderbuffer:GL_RENDERBUFFER], 主要就是把幀緩存中的內容真正的交換或者說是顯示在屏幕上。 
 
 
總結:
 
可以看到, 主循環里做了很多工作, 更新,事件處理, 場景切換,渲染, 是不是全了。 我看差不多。 后面繼續。。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM