標籤:cocos2d-x
問題的由來
怎麼樣使用 Cocos2d-x 快速開發遊戲,方法很簡單,你可以看看其內建的常式,或者從網上搜尋教程,運行起第一個HelloWorld,然後在 HelloWorld 裡面寫相關邏輯代碼,添加我們的層、精靈等 ~ 我們並不一定需要知道 Cocos2d-x 是如何運行或者在各種平台之上運行,也不用知道 Cocos2d-x 的遊戲是如何運行起來的,它又是如何渲染介面的 ~~~
兩個入口
程式入口的概念是相對的,AppDelegate 作為跨平台程式入口,在這之上做了另一層的封裝,封裝了不同平台的不同實現,比如我們通常認為一個程式是由 main 函數開始運行,那我們就去找尋,我們看到了在proj.win32目錄下存在 main.cpp 檔案,這就是我們要看的內容,如下:
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){ UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine);#ifdef USE_WIN32_CONSOLE AllocConsole(); freopen("CONIN$", "r", stdin); freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr);#endif // create the application instance AppDelegate app; CCEGLView* eglView = CCEGLView::sharedOpenGLView(); eglView->setViewName("HelloLua"); eglView->setFrameSize(480, 320); int ret = CCApplication::sharedApplication()->run();#ifdef USE_WIN32_CONSOLE FreeConsole();#endif return ret;}
在這裡我們看見了程式的真正入口,包含一個 main 函數,從此進入,執行 cocos2d-x 程式。
Android:
我們找到 Android 平台與上面 等價 的進入點,proj.android\jni\hellolua\main.cpp
void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject thiz, jint w, jint h){ if (!CCDirector::sharedDirector()->getOpenGLView()) { CCEGLView *view = CCEGLView::sharedOpenGLView(); view->setFrameSize(w, h); AppDelegate *pAppDelegate = new AppDelegate(); CCApplication::sharedApplication()->run(); } else { ccGLInvalidateStateCache(); CCShaderCache::sharedShaderCache()->reloadDefaultShaders(); ccDrawInit(); CCTextureCache::reloadAllTextures(); CCNotificationCenter::sharedNotificationCenter()->postNotification(EVENT_COME_TO_FOREGROUND, NULL); CCDirector::sharedDirector()->setGLDefaultValues(); }}
我們並沒有看到所謂的 main 函數,這是由於不同的平台封裝所以有著不同的實現,在 Android 平台,預設是使用 Java 開發,可以使用 Java 通過 Jni 調用 C++ 程式,而這裡也正式如此。我們暫且只需知道,由 Android 啟動一個應用,通過各種峰迴路轉,最終執行到了Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit 函數,由此,變開始了我們 cocos2d-x Android 平台的程式入口處。對於跨平台的 cocos2d-x 來說,除非必要,否則可不必深究其理,比如想要使用 Android 平台固有的特性等,那就需要更多的瞭解 Jni 使用方法,以及 Android 作業系統的更多細節。
程式的流程 (這裡以 Win32的實現為主,其它平台觸類旁通即可)
注釋:
我們從 main.cpp 中 CCApplication::sharedApplication()–>run(); 這一句看起,這一句標誌著, cocos2d-x 程式正式開始運行,一點點開始分析,我們定位到 sharedApplication() 方法的實現。
CCApplication.cpp:
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;}void CCApplication::setAnimationInterval(double interval){ LARGE_INTEGER nFreq; QueryPerformanceFrequency(&nFreq); m_nAnimationInterval.QuadPart = (LONGLONG)(interval * nFreq.QuadPart);}//////////////////////////////////////////////////////////////////////////// static member function//////////////////////////////////////////////////////////////////////////CCApplication* CCApplication::sharedApplication(){ CC_ASSERT(sm_pSharedApplication); return sm_pSharedApplication;}
從 sharedApplication() 方法,到 run() 方法,在這之前,我們需要調用到它的建構函式,否則不能運行,這就是為什麼在 CCApplication::sharedApplication()–>run();
之前,我們首先使用了 AppDelegate app; 建立 AppDelegate 變數的原因!
AppDelegate 和 CCAppliation 是什麼關係!
由 AppDelegate 的定義我們可以知道,它是 CCApplication 的子類,在建立子類對象的時候,調用其建構函式的同時,父類建構函式也會執行,然後就將 AppDelegate 的對象賦給了 CCApplication 的靜態變數,而在 AppDelegate 之中我們實現了applicationDidFinishLaunching 方法,所以在 CCApplication 中 run 方法的開始處調用的就是 AppDelegate 之中的實現。而我們在此方法中我們初始化了一些變數,建立了第一個 CCScene 情境等,之後的控制權,便全權交給了 CCDirector::sharedDirector()–>mainLoop(); 方法了。
從 CCApplication 到 CCDirector cocos2d-x 程式已經運行起來了,我們繼續下一步,mainLoop 函數:
CCDirector* CCDirector::sharedDirector(void){ if (!s_SharedDirector) { s_SharedDirector = new CCDisplayLinkDirector(); s_SharedDirector->init(); } return s_SharedDirector;}void CCDisplayLinkDirector::mainLoop(void){ if (m_bPurgeDirecotorInNextLoop) { m_bPurgeDirecotorInNextLoop = false; purgeDirector(); } else if (! m_bInvalid) { drawScene(); // release the objects CCPoolManager::sharedPoolManager()->pop(); }} 遊戲的運行以情境為基礎,每時每刻都有一個情境正在運行,其內部有一個情境棧,遵循後進後出的原則,當我們顯示的調用 end() 方法,或者彈出當前情境之時,其自動判斷,如果沒有情境存在,也會觸發 end() 方法,以說明情境啟動並執行結束,而遊戲如果沒有情境,就像演出沒有了舞台,程式進入最後收尾的工作,通過修改變數 m_bPurgeDirecotorInNextLoop 促使在程式 mainLoop 方法之內調用 purgeDirector方法。
走到最後一步,看 CCEGLView 是如果負責收尾工作的:
void CCEGLView::end(){ if (m_hWnd) {#if(_MSC_VER >= 1600) if(m_bSupportTouch){ s_pfUnregisterTouchWindowFunction(m_hWnd);}#endif /* #if(_MSC_VER >= 1600) */ DestroyWindow(m_hWnd); m_hWnd = NULL; } s_pMainWindow = NULL; UnregisterClass(kWindowClassName, GetModuleHandle(NULL)); delete this;}
end() 方法很簡單,只需要看到最後一句 delete this; 就明白了。
參考博文:http://blog.leafsoar.com/archives/2013/05-05.html