Cocos2d-x基礎:TestCpp之項目結構分析

        Cocos2d-x雖然很火,但是相關的學習資料還是很少的。在看完《Cocos2d-x權威指南》的基礎內容之後,我跟着學習過“老G的博客”,也看過一些“地球人也阻止不了程序猿們學習Cocos2d-x了”之類的帖子。總感覺介紹和講解的內容是零零散散的。這非常不利於對於Cocos2d-x引擎的整體理解和掌握。(求大爺們不要滅我,只是個人觀點額)

        我相信,那些寫帖子的同學們、老師們...大神們,都有自己的學習方法,必定不是通過看帖子來學習新東西的。

        好了,不廢話了。希望和大家一起學習,一起進步。爲了給自己鼓勁,在此也爲自己附上一句話。

        有夢想,並堅持,人生就有希望。     

                                                                ------ 北大校長


-------------------------------------------------------------------美麗的分割線-------------------------------------------------------------------


任何一個程序都有一個開始執行的入口,這個入口通常叫做“main”。那麼Cocos2d-x在Win32平臺下的入口是int APIENTRY _tWinMain:

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();
}

AppDelegate:

        程序源碼中的註釋:The reason for implement as private inheritance is to hide some interface call by CCDirector。(這句話如何理解,看各位自己的功力了,我就不做坑爹的翻譯了^ ^)

包含三個方法:

1、virtual bool applicationDidFinishLaunching();

        應用程序相關資源加載完成後執行此方法。可以簡單的認爲,這個就是程序的初始化函數。

        功能:

                (1)初始化應用程序中唯一的CCDirector導演。

                (2)設置OpenGL的相關參數等。

                (3)創建場景,創建佈景層,並將佈景層放入場景中,作爲子節點。

                (4)pDirector->runWithScene(pScene);  設置運行的第一個場景。

2、void AppDelegate::applicationDidEnterBackground()

        原文中的註釋:This function will be called when the app is inactive。表示應用程序當前狀態是“不活躍”(來電話)時,這個方法將被執行。

        功能:

                (1)停止遊戲當中的動畫。

                (2)暫停遊戲的背景音樂。

                (3)暫停遊戲的所有音效。

3、void AppDelegate::applicationWillEnterForeground()

        應用程序從後臺再次被喚醒時執行此方法。

        功能:恢復動畫、背景音樂、所有音效。


CCEGLView:

        由於Cocos2d-x是基於OpenGL ES圖形引擎的,所有在圖形繪製方面所遵守的規則都是OpenGL的規則,比如座標系統。

CCEGLView* eglView = CCEGLView::sharedOpenGLView();
        上面的代碼用來獲得OpenGL的視圖。關於圖形學的視圖這個概念可以在網上搜搜。當然,暫時還沒太多必要。就把這理解爲“獲得應用程序的畫面”吧。

        那麼有了視圖這個對象指針後,我們就可以設置這個視圖的名稱和大小。

CCApplication::sharedApplication()->run();

        應用程序相關的參數設置完成後,就可以調用run()方法,進入應用程序的消息循環。  

   

-------------------------------------------------------------------美麗的分割線-------------------------------------------------------------------


        馬上進入正題了,這裏非常關鍵,因此我也會非常小心的分析,免得誤人子弟,當然,還有自己~奮鬥

        學習Cocos2d-x的各種特性,莫過於學習TestCpp項目了,這是公開的“祕密”。開始我學習這個TestCpp的時候頭昏腦脹的,一點頭緒沒有,因爲程序的結構對於我這樣的新人來說太複雜,更別說是用C++編寫的程序....可惡的宏啊、可惡的高級特性啊抓狂。完全沒心思找那些示例代碼了,因爲和這個程序結構是緊緊相連的啊。

        但如論怎麼說,這個是學習Cocos2d-x最好的資料了,目前還沒發現比這個更好的。

        既然這樣,那就下定決心搞定它吧~!


        OK,先來看看項目的整體文件。

        文件列表:main.cpp、main.h、AppDelegate.h/AppDelegate.cpp、tests.h、testResource.h、VisibleRect.h/VisbleRect.cpp

                            testBasic.h/testBasic.cpp、controller.h/controller.cpp

         除了上面的文件,Classes包(文件夾)中還有許多繼承於CCLayer、CCScene的類,那些都是示例子場景示例子佈景層

        一一擊破:

                main.h/main.cpp和AppDelegate.h/AppDelegate.cpp 在前面我們已經搞定了。

                tests.h:#include所有Classes包中的頭文件、一些字符串、枚舉的標記變量等。(C/C++頭文件,通過包含的方法,就可以使用頭文件內的資源)

                testResource.h:圖片路徑的字符串等。

                VisibleRect.h/VisbleRect.cpp:作者封裝的一個管理座標的類,可以直接讓顯示對象居中、靠左、靠右對齊等。(可以Copy下來,以後自己用)

                testBasic.h/testBasic.cpp:TestScene,繼承於CCScene場景類,是所有示例子場景的爹。

                controller.h/controller.cpp:TestController,繼承於CCLayer佈景層類,是被應用程序第一個創建的佈景層,裏面有一個CCMenu菜單(主菜單)。

         到此,一切文件我們都熟悉了。現在我們深入的調查一下,這個示例程序到底是如何運行這麼多東東的。


CCApplication::sharedApplication()->run()

        main.cpp中,上面的代碼執行完之後就進入了消息循環,程序就正常運行了。

        當程序相關資源加載和設置完成之後就開始執行AppDelegate類中的applicationDidFinishLaunching()方法。

bool AppDelegate::applicationDidFinishLaunching()
{
	// As an example, load config file
	// XXX: This should be loaded before the Director is initialized,
	// XXX: but at this point, the director is already initialized
	CCConfiguration::sharedConfiguration()->loadConfigFile("configs/config-example.plist");

    // 獲得導演實例
    CCDirector *pDirector = CCDirector::sharedDirector();
    // 設置OpenGL視圖
    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
	// 獲取屏幕的分辨率(在Win32下,屏幕的大小是自己設置的,在main.cpp中看到eglView->setFrameSize(480, 320);)
    CCSize screenSize = CCEGLView::sharedOpenGLView()->getFrameSize();
    // 理想的分辨率
    CCSize designSize = CCSizeMake(480, 320);

	// CCFileUtils文件操作類
    CCFileUtils* pFileUtils = CCFileUtils::sharedFileUtils();
    // 如果屏幕的高度大於理想的高度,執行if內的代碼
    if (screenSize.height > 320)
    {
        CCSize resourceSize = CCSizeMake(960, 640);
        std::vector<std::string> searchPaths;
        searchPaths.push_back("hd");					
        pFileUtils->setSearchPaths(searchPaths);			
									
        pDirector->setContentScaleFactor(resourceSize.height/designSize.height); 
    }
	// 將視圖設置爲理想的分辨率。
    CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionNoBorder);
	// 創建一個場景和一個佈景層
    CCScene * pScene = CCScene::create();
    CCLayer * pLayer = new TestController();
	// 設置後,自動管理對象的釋放,不用手動釋放。

    pLayer->autorelease();
	// 將佈景層添加到場景中
    pScene->addChild(pLayer);
	// 運行場景
    pDirector->runWithScene(pScene);

    return true;
}

        分辨率的概念:移動平臺設備的分辨率總是不統一的,什麼大小的分辨率都有,因此Coco有一個處理辦法,那就是使用縮放方法。

                第一種解決思路,當設備的分辨率改變了,那麼讀取不同分辨率的資源。(需要準備各種分辨率的資源)

                第二種解決思路,直接縮放圖片資源的大小,計算公式:X = 設備分辨率/設計分辨率,然後將圖片縮放X倍。(這個比較坑爹)

                第三種,思考ing。

        在上面代碼的if()結構中,是讀取大分辨率資源(圖片等)。TestCpp項目有兩個資源文件夾,一個是images,一個是hd。我們可以找到工程的所在的文件夾,看到只有一部分的圖片有兩個版本的,而且hd文件夾中的資源,很多都在images裏面沒有。這樣做,可能爲了一些示例表現得更好纔沒有小分辨率的吧。暫時不管它。

pFileUtils->setSearchPaths(searchPaths); 
        本來程序裏精靈加載的圖片都是搜索默認Resources文件夾下的images文件夾。當if()結構觸發之後,就會執行上面的這行代碼,改變搜索路徑,轉到hd文件夾下搜索資源。
        我們也可以看到一個技巧,它這裏設置的搜索資源的路徑可以是多個,searchPaths是一個vector容器。

CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designSize.width, designSize.height, kResolutionNoBorder);

        上面代碼將設置應用程序分辨率,第三個參數決定了不同尺寸時畫面顯示的效果。

                kResolutionExactFit:不同尺寸時,畫面被拉伸。
                kResolutionNoBorder:不同尺寸時,沒有黑邊,畫面被裁減。
                kResolutionShowAll:不同尺寸時,有黑邊,如果設計的分辨率和屏幕分辨率不同,將顯示黑邊。

                (三種效果的說明是從coco源碼定義中的註釋找到的,我只是把英文通俗的翻譯過來了,大致也能想象到這三種都是什麼效果,等實際開發的時候再確定)

    CCScene * pScene = CCScene::create();
    CCLayer * pLayer = new TestController();

        細心的童鞋會發現在TestController佈景層中有加入了主菜單,和右下角的關閉按鈕。

pLayer->autorelease();
        我們知道,C++中都是成套使用new/delete的,但是在coco中,有內存自動管理的機制,非常方便,不易出現內存泄露。

        在網上一些關於coco的內存管理分析,大神們好像都建議使用這個,除非有特殊情況。

        不用時,使用release或autorelease,將來要用時,使用retain。

pDirector->runWithScene(pScene);
       Coco 應用程序運行成功!大笑

       那麼童鞋們,最後我們震一下~看看大致的結構圖。

       

-------------------------------------------------------------------美麗的分割線-------------------------------------------------------------------




                                                                                           圖x.x        TestCpp中ActionTest示例結構圖

       如果童鞋比較細心的話,可以發現我這個圖其實是UML圖。羨慕

       爲什麼是UML圖?UML能很好的將程序結構表現出來。遊戲編程除了要學習語言基礎、遊戲引擎,最重要的還是編碼能力和遊戲邏輯的思維。

       如有哪裏繪製錯誤了,希望童鞋們能馬上幫我指出來哭。好了,廢話不講了,簡單分析下這個結構。

       

       TestController被創建時,構造函數中會創建一個CCMenu菜單,用來選擇示例場景。在菜單的回調函數(菜單可以指定回調函數,通俗點講,就是響應函數)中會確定調用哪一個示例場景並顯示出來。代碼如下:

TestController::TestController()
: m_tBeginPos(CCPointZero)
{
    // 右下角的關閉按鈕
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(s_pPathClose, s_pPathClose, this, menu_selector(TestController::closeCallback) );
	CCMenu* pMenu =CCMenu::create(pCloseItem, NULL);
	// 設置關閉按鈕的位置
    pMenu->setPosition( CCPointZero );
    pCloseItem->setPosition(ccp( VisibleRect::right().x-30, VisibleRect::center().y));

    // 創建主菜單,添加菜單項
    m_pItemMenu = CCMenu::create();
    for (int i = 0; i < TESTS_COUNT; ++i)
    {
        CCLabelTTF* label = CCLabelTTF::create(g_aTestNames[i].c_str(), "Arial", 24);     
        CCMenuItemLabel* pMenuItem = CCMenuItemLabel::create(label, this, menu_selector(TestController::menuCallback));
        m_pItemMenu->addChild(pMenuItem, i + 10000);
        pMenuItem->setPosition( ccp( VisibleRect::center().x, (VisibleRect::top().y - (i + 1) * LINE_SPACE) ));
    }
	// 設置菜單的固定大小,無論視圖如何縮放。
    m_pItemMenu->setContentSize(CCSizeMake(VisibleRect::getVisibleRect().size.width, (TESTS_COUNT + 1) * (LINE_SPACE)));
    m_pItemMenu->setPosition(s_tCurPos);
    addChild(m_pItemMenu);
	// 允許觸摸事件
    setTouchEnabled(true);

    addChild(pMenu, 1);

}

void TestController::menuCallback(CCObject * pSender)
{
    // get the userdata, it's the index of the menu item clicked
    CCMenuItem* pMenuItem = (CCMenuItem *)(pSender);
    int nIdx = pMenuItem->getZOrder() - 10000;

    // create the test scene and run it
    TestScene* pScene = CreateTestScene(nIdx);
    if (pScene)
    {
        pScene->runThisTest();
        pScene->release();
    }
}

       當在主場景(TestController)中選擇一個菜單項時,就用上面的調用回調函數。pSender指向的就是被選擇的菜單項。

       接着獲取菜單項的z值,通過z值確定創建哪一個示例場景。(z值就是對象在容器對象裏的前後的關係)可爲什麼是獲取z值後減去了10000?

       我們找到創建菜單的地方(TestController的構造函數),有一個 m_pItemMenu->addChild(pMenuItem, i + 10000);   原來並不是從0開始的,而是從10000開始的。

       接着調用TestScene類中的靜態方法 CreateTestScene(int nIdx),創建新場景。代碼如下:

static TestScene* CreateTestScene(int nIdx)
{
    CCDirector::sharedDirector()->purgeCachedData();

    TestScene* pScene = NULL;

    switch (nIdx)
    {
    case TEST_ACTIONS:
        pScene = new ActionsTestScene(); break;
    case TEST_TRANSITIONS:
        pScene = new TransitionsTestScene(); break;
     case TEST_PROGRESS_ACTIONS:
         pScene = new ProgressActionsTestScene(); break;
    case TEST_EFFECTS:
        pScene = new EffectTestScene(); break;
    case TEST_CLICK_AND_MOVE:
        pScene = new ClickAndMoveTestScene(); break;
    ......
    return pScene;
 }

       接着調用場景的runThisTest()方法替換原有的場景,並運行。在Action示例中的代碼如下:(其他示例場景也類似)

void ActionsTestScene::runThisTest()
{
    sceneIdx = -1;
    addChild(nextAction());
    // 通過導演的replaceScene方法改變場景
    CCDirector::sharedDirector()->replaceScene(this);
}

       回到UML圖。每個示例子場景都繼承於一個基類,都有共同的runThisTest()方法,但內部實現不同。(TestScene其實是一個抽象類,它包含了一個純虛函數:virtual void runThisTest() = 0;。

       從主菜單的場景到示例的場景,切換就完成了。

       最後總結一下程序的執行過程。



       執行過程已經被我們搞定了,終於可以好好的去學習TestCpp中的示例了~!得意





發佈了31 篇原創文章 · 獲贊 2 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章