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], 主要就是把帧缓存中的内容真正的交换或者说是显示在屏幕上。 
     
     
    总结:
     
    可以看到, 主循环里做了很多工作, 更新,事件处理, 场景切换,渲染, 是不是全了。 我看差不多。 后面继续。。 
  • 相关阅读:
    单例模式——C++实现
    单例模式——java实现
    组合模式——java实现
    桥接模式——Java实现
    桥接模式——C++实现
    适配器模式——java实现
    编写一个显示当前时间的jsp页面
    适配器模式——C++实现
    安装配置hadoop1
    Nginx使用教程
  • 原文地址:https://www.cnblogs.com/liangzhimy/p/4425442.html
Copyright © 2011-2022 走看看