zoukankan      html  css  js  c++  java
  • 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], 主要就是把帧缓存中的内容真正的交换或者说是显示在屏幕上。 
     
     
    总结:
     
    可以看到, 主循环里做了很多工作, 更新,事件处理, 场景切换,渲染, 是不是全了。 我看差不多。 后面继续。。 
  • 相关阅读:
    11111 Generalized Matrioshkas
    Uva 442 Matrix Chain Multiplication
    Uva 10815 Andy's First Dictionary
    Uva 537 Artificial Intelligence?
    Uva 340 MasterMind Hints
    SCAU 9508 诸葛给我牌(水泥题)
    Uva 10420 List of Conquests(排序水题)
    Uva 409 Excuses, Excuses!
    10/26
    11/2
  • 原文地址:https://www.cnblogs.com/liangzhimy/p/4425442.html
Copyright © 2011-2022 走看看