天天看點

從HelloApp看一個cocos2d-x程式是怎麼啟動的

初學cocos2d-x,照着别人的示例,稍微改了一些很基礎的東西玩玩,例如加些字,加些圖檔啊什麼的。大緻了解了,CCScene是個場景類,是被顯示的:CCDirector是到導演類,控制程式流程;CCLayer是遊戲畫面層類,層上可以放置menu、精靈(遊戲機關)、其他層等遊戲元素。但是我好奇這個程式啟動後的流程到底是怎麼樣的?于是稍微跟蹤了一下。

毫無疑問,先main入手。

AppDelegate app;
    CCEGLView* eglView = CCEGLView::sharedOpenGLView();
    eglView->setViewName("HelloCpp");
    eglView->setFrameSize(480, 320);
    return CCApplication::sharedApplication()->run();           

和QT差不多,基本main都很簡單,先是建一個app對象,然後啟動app的run()方法。從命名上看,AppDelegate應該是一個繼承于CCApplication的類,而

sharedApplication()           

這個靜态函數也定是傳回了app類的指針。轉到AppDelegate.h,一看果然是繼承于CCApplication。然後檢視sharedApplication() 這個函數的定義

CCApplication* CCApplication::sharedApplication()
{
    CC_ASSERT(sm_pSharedApplication);
    return sm_pSharedApplication;
}           

看了下,基本就是assert()一下,然後傳回CCApplication類指針。

到此,思路很簡單,就是建立一個App對象,擷取指針(至于為什麼不直接用,估計是用斷言判斷下指針非空什麼的,提高安全性吧),然後調用app的run()。那麼這個run()裡面到底發生了什麼?

檢視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 (!<span style="color: rgb(255, 0, 0);">applicationDidFinishLaunching()</span>)
    {
        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;
}           

發現了一個AppDelegate類中有定義的函數  applicationDidFinishLaunching()。然後下面的結構,一看就是消息循環了,接收msg,處理msg,windows程式設計也是這個結構,隻不過這個例子比較簡單,隻對一個WM_QUIT做了處理。回過頭來檢視 applicationDidFinishLaunching()這個函數,這函數父類CCApplication并沒有,查了下宗譜,發現原來是爺爺的

class CC_DLL CCApplication : public CCApplicationProtocol           

再看這函數的内容

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    CCDirector* pDirector = CCDirector::sharedDirector();
    CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();

    pDirector->setOpenGLView(pEGLView);
	
    // turn on display FPS
    pDirector->setDisplayStats(true);

    // set FPS. the default value is 1.0/60 if you don't call this
    pDirector->setAnimationInterval(1.0 / 60);

    // create a scene. it's an autorelease object
    CCScene *pScene = HelloWorld::scene();

    // run
    pDirector->runWithScene(pScene);

    return true;
}           

終于出現了CCDirector、CCScene這些”名“類,也出現了Helloword這個關鍵字,項目裡的另一個類終于出現了。重點在于

CCScene *pScene = HelloWorld::scene();

    // run
    pDirector->runWithScene(pScene);           

這兩句,調用了HelloWorld的一個靜态方法獲得了一個scene,然後director把運作起來了,然後我們就看到了,事情就是這麼簡單。繼續“轉到定義”看scene()這個函數,

CCScene* HelloWorld::scene()
{
    // 'scene' is an autorelease object
    CCScene *scene = CCScene::create();
    
    // 'layer' is an autorelease object
    HelloWorld *layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}           

建立一個CCScene對象,然後用HelloWorld類的create()函數建立一個“層”,把“層”布置到場景上,傳回給導演用。看了下HelloWorld的定義,發現它的基類也是CCLayer

,粗略一看,沒有create()函數。仔細看,發現一個

CREATE_FUNC(HelloWorld);           

點到這個宏上發現正是用這個宏定義這個create()函數,而且還發現這個函數用到了init()函數,正是這個Init()對HelloWorld這個Layer作了修飾,添加了各種圖檔按鈕。

<pre name="code" class="cpp">CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
    pMenu->setPosition(CCPointZero);
    this->addChild(pMenu, 1);           

添加了按鈕

CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24);
    
    // position the label on the center of the screen
    pLabel->setPosition(ccp(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - pLabel->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(pLabel, 1);           

添加圖示

CCSprite* pSprite = CCSprite::create("HelloWorld.png");

    // position the sprite on the center of the screen
    pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

    // add the sprite as a child to this layer
    this->addChild(pSprite, 0);           

添加精靈

到此為止,思路基本理清了,故事原來是這樣的:main函數建立了一個app對象并啟動了它的run接口;這個run接口呢,調用了一個AppDelegate從CCApplicationProtocol那裡繼承過來的applicationDidFinishLaunching()接口,在裡面建立一個了導演(CCDirector),并且用HelloWorld的scene()接口建立了一個HelloWorld的場景,用導演把場景運作起來,然後就進入按部就班模式(消息循環)。HelloWorld在建立場景的時候呢,就調用了自己的create函數,create裡生成了那個場景,至于它場景的布置,則是在init()函數裡完成的。

這樣整個流程就大緻清楚了。我可以在run()接口裡對多種消息做處理而不隻是退出;讓導演多做一些控制,而不隻是顯示,顯示也隻是顯示一個scene;在init裡多加一些遊戲元素;等等。

繼續閱讀