cocos2d-x 3.1.1學習筆記[23]尋找主迴圈 mainloop

weixin_33912246發表於2017-06-04

文章出自於  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















相關文章