1. 程式人生 > >cocos2d-x原始碼剖析之整體框架

cocos2d-x原始碼剖析之整體框架


     剛閱讀完了cocos2d-x的大部分原始碼,感覺受益匪淺,cocos2d-x的程式碼並不複雜,可讀性很強,並且其中一部分精華的部分也可以運用到工作中去,相得益彰。現在看來,閱讀原始碼的最好方式是top-down的方式,先弄懂整個框架,再重點突破重要和感興趣的模組。廢話少說,先看看coscos2d-x的框架是怎樣的,如何執行起來。
     先讓我們看看測試用例TestCpp中的主函式,也是整個Win32程式的入口。

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);


// create the application instance
AppDelegate app;
CCEGLView* eglView = CCEGLView::sharedOpenGLView();
eglView->setViewName("TestCpp");
eglView->setFrameSize(480, 320);
return CCApplication::sharedApplication()->run();
}
     這個入口有2個非常重要的類CCEGLView和CCApplication,在整個程式中都是單例。
     先看看CCEGLView
     CCEGLView是用來管理視窗和繪製。在 CCEGLView::Create中做了如下大家非常熟悉的事情
    1. RegisterClass註冊視窗,其中非常重要的訊息處理函式CCEGLView::_WindowProc就是在整個地方定義的。整個遊戲中的視窗的鍵盤、滑鼠訊息響應就可以在這個函式中進行處理。
    2. initGL初始化OpenGL引擎.
    m_hDC = GetDC(m_hWnd);
    SetupPixelFormat(m_hDC);
    //SetupPalette();
   m_hRC = wglCreateContext(m_hDC);
   wglMakeCurrent(m_hDC, m_hRC);
   CCEGLView::initGL中設定了畫素的格式,建立了OpenGL的RenderContext,OpenGL最終渲染的結果會顯示到該視窗的DC上。
    再看看CCApplication
    CCApplication是用來管理程式的邏輯,最後一句CCApplication::sharedApplication()->run()整個程式就開始高速運轉起來了。剛開始看程式碼的時候有一個疑問:
CCApplication* CCApplication::sharedApplication()
{
    CC_ASSERT(sm_pSharedApplication);
    return sm_pSharedApplication;
}
  這個函式中的sm_pSharedApplication是在什麼地方初始化的呢?聰明的你很快就會發現其實前面有一個AppDelegate app,AppDelegate是Application的子類,在這個子類的建構函式中聲明瞭這個全域性唯一的靜態變數。
CCApplication::CCApplication()
: m_hInstance(NULL)
, m_hAccelTable(NULL)
{
m_hInstance    = GetModuleHandle(NULL);
m_nAnimationInterval.QuadPart = 0;
CC_ASSERT(! sm_pSharedApplication);
sm_pSharedApplication = this;
}
 由此Application的單例也有了。Application::Run()做了什麼呢,讓整個程式執行起來了呢?
int CCApplication::run()
{
    PVRFrameEnableControlWindow(false);


    // Main message loop:
    MSG msg;
    LARGE_INTEGER nFreq;
    LARGE_INTEGER nLast;
    LARGE_INTEGER nNow;

    QueryPerformanceFrequency(&nFreq);
    QueryPerformanceCounter(&nLast);

    // Initialize instance and cocos2d.
    if (!applicationDidFinishLaunching())
    {
        return 0;
    }

    CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();
    pMainWnd->centerWindow();
    ShowWindow(pMainWnd->getHWnd(), SW_SHOW);

    while (1)
    {
        if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // Get current time tick.
            QueryPerformanceCounter(&nNow);

            // If it's the time to draw next frame, draw it, else sleep a while.
            if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)
            {
                nLast.QuadPart = nNow.QuadPart;
                CCDirector::sharedDirector()->mainLoop();
            }
            else
            {
                Sleep(0);
            }
            continue;
        }

        if (WM_QUIT == msg.message)
        {
            // Quit message loop.
            break;
        }

        // Deal with windows message.
        if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}
先跳過applicationDidFinishLaunching()部分(這部分和測試用例有關係),可以看到在while主迴圈中做了兩件事:
  1. 轉發視窗訊息,交給之前定義的CCEGLView::_WindowProc進行處理
  2. 每隔m_nAnimationInterval.QuadPart時間,也就是遊戲的一幀,進行一次處理.
這裡就引出了最重要的一個類CCDirector即導演類,這個類也是一個單例,負責整個遊戲場景管理,邏輯更新以及繪製。那讓我們看看
CCDirector::sharedDirector()->mainLoop()的每幀的mainLoop都做了啥事情:
   CCDirector的幀常工作:
void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         drawScene();
     
         // release the objects
         CCPoolManager::sharedPoolManager()->pop();        
     }
}
 導演類的的每幀做的最重要的事情就是drawScene,
void CCDirector::drawScene(void)
{
    ......
    if (! m_bPaused)
    {
        m_pScheduler->update(m_fDeltaTime);
    }
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // draw the scene
    if (m_pRunningScene)
    {
        m_pRunningScene->visit();
    }
    // swap buffers
    if (m_pobOpenGLView)
    {
        m_pobOpenGLView->swapBuffers();
    }
}

讀者仔細看看就會發現這個函式做了兩個方面的工作,:

       1.  更新排程器m_pScheduler,比如場景中的動作等,後面的博文會詳細的分析

        2.  繪製場景中的物件結點,樹形的方式

        至此,cocos2d-x的整體框架就非常清晰的擺在我們面前了,後面就開始解剖麻雀了^_^