zoukankan      html  css  js  c++  java
  • cocos2d-x 3.1.1学习笔记[23]寻找主循环 mainloop

    文章出自于  http://blog.csdn.net/zhouyunxuan


    cocos2d到底是怎样把场景展示给我们的,我一直非常好奇。

    凭个人猜想,引擎内部的结构类似于这样

        while(true)
        {
            if(update_span < min_update_span)
            {
                update_game();
                if(done)
                {
                    break;
                }
            }
            else
            {
                cal_update_span();
            }
        }


    在app開始执行时会调用里面的方法。


        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 
    来看看这个函数最后return YES之前的一行代码
        cocos2d::Application::getInstance()->run();
    没错,就是这个,然后我们进入到run函数里面来看个到底

        int Application::run()
        {
            if (applicationDidFinishLaunching()) 
            {
                //这个函数在这里调用了startMainLoop
                [[CCDirectorCaller sharedDirectorCaller] startMainLoop];
            }
            return 0;
        }


    然后我们继续跟进看看startMainLoop

        -(void) startMainLoop
        {
                // Director::setAnimationInterval() is called, we should invalidate it first
                [displayLink invalidate];
                displayLink = nil;
                
                //给CADisplayLink传了一个doCaller函数,让CADisplayLink不断的调用
                displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
                //设置调用频率
                [displayLink setFrameInterval: self.interval];
                //開始循环吧!
                [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        }


    CADisplayLink,须要增加QuartzCore.framework
    这个函数类似于update函数,默认每秒被调用60次,如今我们再进入doCaller函数吧

        -(void) doCaller: (id) sender
        {
            cocos2d::Director* director = cocos2d::Director::getInstance();
            [EAGLContext setCurrentContext: [(CCEAGLView*)director->getOpenGLView()->getEAGLView() context]];
            //最终进入到这场表演的主角了,我期待了好久!!

    。 director->mainLoop(); }



    事实上Director::getInstance();返回的不是Director。被骗了好久-= -

    Director::getInstance() 返回的并非Director。而是Director的子类DisplayLinkDirector();
    
        Director* Director::getInstance()
        {
            if (!s_SharedDirector)
            {
                s_SharedDirector = new DisplayLinkDirector();
                s_SharedDirector->init();
            }
    
            return s_SharedDirector;
        }


    程序的主要逻辑都通过调用mainloop来完毕,这种方法负责调用计时器。画图,发送全局通知,并处理内存回收池,这种方法按帧调用,每帧调用一次。而帧之间取决于两个因素,一个是预设的帧频(默觉得每秒六十次),还有一个是每帧的计算量大小,当逻辑处理与画图计算量过大时,设备无法完毕每秒六十次的绘制,此时帧率就会减少。

        void DisplayLinkDirector::mainLoop()
        {
            //是否在下一循环中清除
            //bool _purgeDirectorInNextLoop; // this flag will be set to true in end()
            if (_purgeDirectorInNextLoop)
            {
                log("clear director");
                _purgeDirectorInNextLoop = false;
                //会做一些清理
                purgeDirector();
            }
            //假设不清除的话(且为合法的)ps:一般都是会进入到这里。然后进行绘制等等。

    else if (! _invalid) { //画场景 drawScene(); // release the objects PoolManager::getInstance()->getCurrentPool()->clear(); } }



    然后我们接着看看这伟大的 drawScene里面做了什么吧!


     void Director::drawScene()
        {
            //计算时间增量
            // calculate "global" dt
            calculateDeltaTime();
            
    
            // 假设两帧间隔时间太短(_deltaTime等于0)就直接忽略这次的绘制
            // FLT_EPSILON 是 __FLT_EPSILON__ 的宏。__FLT_EPSILON__ 是c99的特征,它是满足 x+1.0不等于1.0的最小的正数,直接输出为0。
            if(_deltaTime < FLT_EPSILON)
            {
                return;
            }
            
            //
            if (_openGLView)
            {
                _openGLView->pollInputEvents();
            }
    
            //tick before glClear: issue #533
            //仅仅要游戏没有暂停,调度器神马的就会在这里被运行。
            if (! _paused)
            {
                _scheduler->update(_deltaTime);
                _eventDispatcher->dispatchEvent(_eventAfterUpdate);
            }
    
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
            /* to avoid flickr, nextScene MUST be here: after tick and before draw.
             XXX: 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)
            {
                _runningScene->visit(_renderer, Mat4::IDENTITY, false);
                _eventDispatcher->dispatchEvent(_eventAfterVisit);
            }
    
            // 画通知节点
            if (_notificationNode)
            {
                _notificationNode->visit(_renderer, Mat4::IDENTITY, false);
            }
            
            //假设设置了显示debug信息,就会在这里进行每帧的更新。
            if (_displayStats)
            {
                showStats();
            }
    
            _renderer->render();
            _eventDispatcher->dispatchEvent(_eventAfterDraw);
    
            popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    
            _totalFrames++;
    
            // 交换缓冲区
            if (_openGLView)
            {
                _openGLView->swapBuffers();
            }
            
            //计算fps上的信息
            if (_displayStats)
            {
                calculateMPF();
            }
        }


    最初提出来的结构和发现的结构还是有点相似的。

    仅仅只是引擎更友好的抽象封装出来了。且做了非常多防止异常的处理。程序猿还都是非常小心的嘛。。。
    调用的CADisplayLink是ios平台的,假设换成其它平台就不一样啦。

    毕竟win是木有CADisplayLink的。


    不相信?
    好吧,我们来看看win是怎样调用的吧,首先找到Application::run()函数。

        //假设窗体不关闭
        while(!glview->windowShouldClose())
            {
                //计算时间
                QueryPerformanceCounter(&nNow);
                //两帧间距时间要大一点才给你画哦
                if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
                {
                    //上一帧的时间就等于如今这一帧,用于下次计算两帧的间隔时间。

    nLast.QuadPart = nNow.QuadPart; //进入到我们伟大的mainloop了。是不是有点小激动 - - director->mainLoop(); glview->pollEvents(); } else { //神马!睡0秒。

    好吧。光是看表面还是非常骗人的。 Sleep(0); } }


    Sleep(0)是指CPU交出当前线程的运行权,让CPU去运行其它线程。也就是放弃当前线程的时间片。转而运行其它线程。
    一般来说,假设当前线程比較耗时比較占CPU资源。能够在结尾处加上Sleep(0), 这样效率会得到大大的提高。



    看了win上面的调用,发现和我一開始的猜想更像有木有!!!




    有时候。做笔记记录下学习过程也挺不错的。

    肯定没人会转载的- - 

    可是为了防止蜘蛛爬走了我的文章,我还是凝视下- -

    文章出自于  http://blog.csdn.net/zhouyunxuan















  • 相关阅读:
    链表栈
    C# TCP应用编程二 同步TCP应用编程
    C# TCP应用编程一 概述
    C# 网络流
    远程连接 出现身份验证错误,要求的函数不受支持(这可能是由于CredSSP加密Oracle修正)
    C#线程Thread类
    C# 通过Internet搜索网络资源
    正则表达式
    C#文件的读写
    微服务实战
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/6940583.html
Copyright © 2011-2022 走看看