cocos2dx-mainloop

cocos2dx版本3.10(各个版本有细微的差别)


简略图
简略


run

int Application::run()
{
    ...

    while(!glview->windowShouldClose())// 是否退出游戏
    {
        QueryPerformanceCounter(&nNow);// 获取当前时间
        if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
        {
            nLast.QuadPart = nNow.QuadPart;// 记录一帧开始的时间

            director->mainLoop();// 进行图形渲染与游戏逻辑处理
            glview->pollEvents();// 进行游戏的交互处理
        }
        else
        {
            Sleep(0);
        }
    }
    if (glview->isOpenGLReady())
    {
        director->end();
        director->mainLoop();
        director = nullptr;
    }
    glview->release();
    return 0;
}

图解
主循环
mainLoop:负责调用定时器,绘图,发送全局通知,并处理内存回收池。每一帧进行一次调用。主要实现的是mainLoop中的drawScene方法,稍后会提到


接下来我们着重探讨下mainLoop的实现过程
mainLoop

void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (_restartDirectorInNextLoop)
    {
        _restartDirectorInNextLoop = false;
        restartDirector();
    }
    else if (! _invalid)
    {
        drawScene();

        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();//进行内存清理
    }
}

_purgeDirectorInNextLoop:是否在下一循环前清除Director。在退出消息while循环时,director->end()将_purgeDirectorInNextLoop设置为true,再次调用mainLoop时,就进行purgeDirector()销毁Director的工作
_restartDirectorInNextLoop:是否重新启动Director,通过调用Director方法restart将_restartDirectorInNextLoop设置为true,进行调用restartDirector()重新启动
_invalid:是否为无效的场景,该值初始化为false

drawScene():主要实现了图形渲染以及消息事件的处理,接下来我们分析下drawScene的实现
PoolManager::getInstance()->getCurrentPool()->clear():渲染一帧后,进行对当前内存池的清理工作(每一帧都会创建一个内存池)


drawScene

void Director::drawScene()
{
    // calculate "global" dt
    calculateDeltaTime();

    if (_openGLView)
    {
        _openGLView->pollEvents();
    }

    //tick before glClear: issue #533
    if (! _paused)
    {
        _eventDispatcher->dispatchEvent(_eventBeforeUpdate);
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }

    _renderer->clear();
    experimental::FrameBuffer::clearAllFBOs();
    /* 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);

    if (_runningScene)
    {
#if (CC_USE_PHYSICS || (CC_USE_3D_PHYSICS && CC_ENABLE_BULLET_INTEGRATION) || CC_USE_NAVMESH)
        _runningScene->stepPhysicsAndNavigation(_deltaTime);
#endif
        //clear draw stats
        _renderer->clearDrawStats();

        //render the scene
        _runningScene->render(_renderer);

        _eventDispatcher->dispatchEvent(_eventAfterVisit);
    }

    // draw the notifications node
    if (_notificationNode)
    {
        _notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
    }

    if (_displayStats)
    {
        showStats();
    }
    _renderer->render();

    _eventDispatcher->dispatchEvent(_eventAfterDraw);

    popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

    _totalFrames++;

    // swap buffers
    if (_openGLView)
    {
        _openGLView->swapBuffers();
    }

    if (_displayStats)
    {
        calculateMPF();
    }
}

1、calculateDeltaTime()
计算一次帧循环的时间(上一帧与当前帧的时间差),保存在全局变量_deltaTime中
_deltaTime = (now.tv_sec - _lastUpdate->tv_sec) + (now.tv_usec - _lastUpdate->tv_usec) / 1000000.0f;
2、

if (! _paused)//Director 是否暂停
    {
        _eventDispatcher->dispatchEvent(_eventBeforeUpdate);
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    }

_eventDispatcher->dispatchEvent(_eventBeforeUpdate):处理用户定时事件之前的触摸消息事件
_scheduler->update(_deltaTime):处理定时事件,根据我们注册的定时事件的优先级进行处理定时事件。我们注册的定时事件,底层就是使用_scheduler进行注册的。
_eventDispatcher->dispatchEvent(_eventAfterUpdate):事件处理完的通知事件,在上一帧的事件处理完成是产生该事件。如果用户注册了相关的事件,这里将会使用EventDispatcher 来进行调用。例如你可以注册该事件进行检测更新完一次所需要的时间。

_eventBeforeUpdate = new (std::nothrow) EventCustom(EVENT_BEFORE_UPDATE);
_eventAfterUpdate = new (std::nothrow) EventCustom(EVENT_AFTER_UPDATE);

3、_renderer->clear()
清理渲染器(包括清楚openGL的buffer和屏幕的渲染)
4、experimental::FrameBuffer::clearAllFBOs()
清理帧缓冲区
5、setNextScene()

void Director::setNextScene()
{
    bool runningIsTransition = dynamic_cast<TransitionScene*>(_runningScene) != nullptr;
    bool newIsTransition = dynamic_cast<TransitionScene*>(_nextScene) != nullptr;

    // If it is not a transition, call onExit/cleanup
     if (! newIsTransition)
     {
         if (_runningScene)
         {
             _runningScene->onExitTransitionDidStart();
             _runningScene->onExit();
         }

         // issue #709. the root node (scene) should receive the cleanup message too
         // otherwise it might be leaked.
         if (_sendCleanupToScene && _runningScene)
         {
             _runningScene->cleanup();
         }
     }

    if (_runningScene)
    {
        _runningScene->release();
    }
    _runningScene = _nextScene;
    _nextScene->retain();
    _nextScene = nullptr;

    if ((! runningIsTransition) && _runningScene)
    {
        _runningScene->onEnter();
        _runningScene->onEnterTransitionDidFinish();
    }
}

主要进行了对正在运行的场景的清理,设置当前运行的场景_runningScene为下个将要运行的场景_nextScene,再进行onEnter调用当前的场景。
onEnter() 是在进入场景的一瞬间就开始执行了。
onEnterTransitionDidFinish() 是在完全进入场景后开始执行的。
6、_renderer->clearDrawStats();
统计渲染的数据
7、_runningScene->render(_renderer);
调用当前场景的渲染方法进行对当前场景(此时当前场景已经切换为下一个场景)的渲染
8、_eventDispatcher->dispatchEvent(_eventAfterVisit);
场景渲染完成产生的用户通知事件,如果用户注册了该事件,该场景渲染完成时将进行调用。
9、_notificationNode->visit(_renderer, Mat4::IDENTITY, 0);
访问_notificationNode的子节点,并进行递归绘制。
_renderer:渲染器
Mat4::IDENTITY:变换矩阵
0:渲染器标志
10、showStats();
更新帧率的状态
11、_renderer->render();
进行图像的渲染,将我们场景需要显示的东西显示到显示器上面。
12、_eventDispatcher->dispatchEvent(_eventAfterDraw);
图像渲染完成产生的用户通知事件,如果用户注册了该事件,该图像渲染完成时将进行调用。
13、popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
弹出指定类型矩阵堆栈的顶层矩阵。
14、_totalFrames++;
进行对帧计算的值自加一
15、 _openGLView->swapBuffers();
将我们在后台绘制完成的帧序列,进行与前台显示的帧序列进行交换,显示我们绘制好的下一场景。对我而言,我理解它就是类似于双缓存,我们在后台绘制后之后,一次性显示在屏幕上,避免了卡顿的出现。

本文难免有所错误,如有问题欢迎留言。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值