HelloCpp是Cocos2d-x自帶的一個工程,它演示了Cocos2d-x最基本的使用方法和流程。先看一下它的基本構成html
win32目錄中包含了對應平臺的代碼,而Classes目錄中包含了咱們本身的實現代碼。編譯運行的結果以下圖node
main函數變形記linux
看到main命名的文件就會想到著名的main函數必定在這個文件裏面,那麼就讓咱們先看看這個文件吧。main.h裏面主要是include了各類各樣的頭文件,main.cpp是咱們真正須要關注的android
1 int APIENTRY _tWinMain(HINSTANCE hInstance, 2 HINSTANCE hPrevInstance, 3 LPTSTR lpCmdLine, 4 int nCmdShow) 5 { 6 UNREFERENCED_PARAMETER(hPrevInstance); 7 UNREFERENCED_PARAMETER(lpCmdLine); 8 9 // create the application instance 10 AppDelegate app; 11 CCEGLView* eglView = CCEGLView::sharedOpenGLView(); 12 eglView->setFrameSize(960, 640 ); 13 return CCApplication::sharedApplication()->run(); 14 }
1.1 APIENTRYexpress
首先出現的不明物體是APIENTRY。咱們先看看它的定義windows
1 #define APIENTRY WINAPI
APIENTRY是WINAPI的一個替身,而WINAPI是微軟定義的宏,實際就是__stdcall設計模式
1 #define WINAPI __stdcall
衆所周知,__stdcall聲明瞭函數從右至左將參數壓棧並由被調用者清理堆棧的調用約定。app
1.2 _tWinMain框架
若是我說_tWinMain是程序的入口函數,你會不會發飆:我去,入口函數不該該是main嗎?是的!對於常規的C/C++程序而言main是入口函數的歸宿,但在Windows程序中WinMain纔是入口函數。iphone
1 #ifdef _UNICODE 2 3 #define _tWinMain wWinMain 4 5 #else 6 7 #define _tWinMain WinMain 8 9 #endif
1.3 UNREFERENCED_PARAMETER
進入_tWinMain函數後就是兩句很神奇的語句
1 UNREFERENCED_PARAMETER(hPrevInstance); 2 UNREFERENCED_PARAMETER(lpCmdLine);
要理解這句話首先是要搞清楚UNREFERENCED_PARAMETER是神馬玩意兒。若是我告訴你它神馬都不是,你信嗎?
1 #define UNREFERENCED_PARAMETER(P) (P)
認可吧騷年,它真的神馬都不是啊~(你逗我玩兒吶?)
AppDelegate的前世此生
_tWinMain函數的真正主體是從AppDelegate app開始的,因此咱們就首先從AppDelegate提及。咱們先看看AppDelegate的宗族關係
要把AppDelegate弄清楚搞明白,咱們還得從源頭開始。
2.1 CCApplicationProtocol
做爲Cocos2d-x Application的源頭,CCApplicationProtocal定義以下
1 class CC_DLL CCApplicationProtocol 2 { 3 public: 4 5 virtual ~CCApplicationProtocol() {} 6 7 /** 8 @brief Implement CCDirector and CCScene init code here. 9 @return true Initialize success, app continue. 10 @return false Initialize failed, app terminate. 11 */ 12 virtual bool applicationDidFinishLaunching() = 0; 13 14 /** 15 @brief The function be called when the application enter background 16 @param the pointer of the application 17 */ 18 virtual void applicationDidEnterBackground() = 0; 19 20 /** 21 @brief The function be called when the application enter foreground 22 @param the pointer of the application 23 */ 24 virtual void applicationWillEnterForeground() = 0; 25 26 /** 27 @brief Callback by CCDirector for limit FPS. 28 @interval The time, expressed in seconds, between current frame and next. 29 */ 30 virtual void setAnimationInterval(double interval) = 0; 31 32 /** 33 @brief Get current language config 34 @return Current language config 35 */ 36 virtual ccLanguageType getCurrentLanguage() = 0; 37 38 /** 39 @brief Get target platform 40 */ 41 virtual TargetPlatform getTargetPlatform() = 0; 42 };
能夠看到,CCApplicationProtocol是一個抽象類,它定義並導出做爲DLL的接口。這其中有一個陌生CC_DLL,它定義了在DLL中的符號是導出仍是導入
1 #if defined(_USRDLL) 2 #define CC_DLL __declspec(dllexport) 3 #else /* use a DLL library */ 4 #define CC_DLL __declspec(dllimport) 5 #endif
整個CCApplicationProtocol除了析構函數之外的其餘全部函數都是純虛函數,這也就是它爲何叫Protocol的緣由。每一個函數的含義和做用在註釋裏有簡要的說明,但具體的實現何其做用須要進一步才能理解。
2.2 CCApplication
做爲對Cocos2d-x Application的抽象,CCApplication所扮演的角色是很是重要的。它的定義以下
1 class CC_DLL CCApplication : public CCApplicationProtocol 2 { 3 public: 4 CCApplication(); 5 virtual ~CCApplication(); 6 7 /** 8 @brief Run the message loop. 9 */ 10 int run(); 11 12 /** 13 @brief Get current applicaiton instance. 14 @return Current application instance pointer. 15 */ 16 static CCApplication* sharedApplication(); 17 18 /* override functions */ 19 virtual void setAnimationInterval(double interval); 20 virtual ccLanguageType getCurrentLanguage(); 21 22 /** 23 @brief Get target platform 24 */ 25 virtual TargetPlatform getTargetPlatform(); 26 27 /* set the Resource root path */ 28 void setResourceRootPath(const std::string& rootResDir); 29 30 /* get the Resource root path */ 31 const std::string& getResourceRootPath(void) 32 { 33 return m_resourceRootPath; 34 } 35 36 void setStartupScriptFilename(const std::string& startupScriptFile); 37 38 const std::string& getStartupScriptFilename(void) 39 { 40 return m_startupScriptFilename; 41 } 42 43 protected: 44 HINSTANCE m_hInstance; 45 HACCEL m_hAccelTable; 46 LARGE_INTEGER m_nAnimationInterval; 47 std::string m_resourceRootPath; 48 std::string m_startupScriptFilename; 49 50 static CCApplication * sm_pSharedApplication; 51 };
2.2.1 構造函數
CCApplication的構造函數所完成的工做就是對成員變量的初始化
1 CCApplication::CCApplication() 2 : m_hInstance(NULL) 3 , m_hAccelTable(NULL) 4 { 5 m_hInstance = GetModuleHandle(NULL); 6 m_nAnimationInterval.QuadPart = 0; 7 CC_ASSERT(! sm_pSharedApplication); 8 sm_pSharedApplication = this; 9 }
m_hInstance保存了當前模塊的句柄,而sm_pSharedApplicaton則保存了當前對象的指針。值得注意的是,sm_pSharedApplication是一個static的CCApplication對象指針。
2.2.2 Application是惟一的
咱們再把_tWinMain函數請出來仔細看看(這裏去掉了幾句無用的代碼)
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // create the application instance AppDelegate app; CCEGLView* eglView = CCEGLView::sharedOpenGLView(); eglView->setFrameSize(960, 640 ); return CCApplication::sharedApplication()->run(); }
是否對AppDelegate app這句感到疑惑:在定義了app之後卻從未使用過它。那麼咱們是否是能夠理解爲這句是多餘的呢?好吧!咱們把它註釋掉,結果程序崩潰了!崩潰了!!潰了!!!
這是由sm_pSharedApplication引起的斷言異常。sm_pSharedApplication是CCApplication類的一個protected的static成員,雖然在構造函數中將它賦值爲this,但它的初始化倒是0(空指針)
1 // sharedApplication pointer 2 CCApplication * CCApplication::sm_pSharedApplication = 0;
同時CCApplication提供一個public的static函數來訪問它
1 CCApplication* CCApplication::sharedApplication() 2 { 3 CC_ASSERT(sm_pSharedApplication); 4 return sm_pSharedApplication; 5 }
若果你熟悉設計模式就能一眼看出來這個實現其實是單例模式,它保證了CCApplication只有一個實例。
看到這裏,你可能會驚呼:必定是_tWinMain函數的最後一句 return CCApplication::sharedApplication()->run() 引入了錯誤。哎,事實老是殘酷的!實際上異常早在這以前就發生了。咱們在CCApplication::sharedApplication函數中下斷點,F5運行程序,從CallStack能夠看到異常在CCEGLView::WindowProc函數中就發生了
進入CCEGLView::WindowProc函數,很快咱們就能發現引起異常的代碼段
1 case WM_SIZE: 2 switch (wParam) 3 { 4 case SIZE_RESTORED: 5 CCApplication::sharedApplication()->applicationWillEnterForeground(); 6 break; 7 case SIZE_MINIMIZED: 8 CCApplication::sharedApplication()->applicationDidEnterBackground(); 9 break; 10 } 11 break;
WM_SIZE是Windows消息機制中的一個重要消息,每當窗口的大小改變時它就會進入到程序的消息隊列中。這段代碼就是處理WM_SIZE消息的。當程序啓動時,窗口的大小就會發生變化,此時就會調用CCApplication::sharedApplication函數獲取CCApplication::sm_pSharedApplication指針。然而,由於咱們註釋掉了「AppDelegate app」這句代碼致使CCApplication的構造函數沒有被調用,使得CCApplication::sm_pSharedApplication始終爲初始值0,致使調用CCApplication::sharedApplication函數時引起了其內部實現的斷言。
是否是被繞暈了?我也有點暈!不要緊,咱們把CCEGLView插入到這裏來分析一下cocos2d-x是如何構成完整Windows程序框架的。熬過這話題就不會暈了。
2.2.3 構建Windows程序框架
從經典教科書《Windows程序設計》中能夠看到,典型的Windows程序的框架大致以下
1 int WINAPI WinMain(HINSTANCE hInstance, 2 HINSTANCE hPreInstance, 3 PSTR szCmdLine, 4 int iCmdShow) 5 { 6 1)註冊窗口類,並註冊消息處理函數WindowProc 7 2)建立並顯示窗口 8 3)循環獲取消息 9 }
消息處理函數的結構以下
1 LRESULT CALLBACK WindowProc(HWND hwnd, 2 UINT uMsg, 3 WPARAM wParam, 4 LPARAM lParam) 5 { 6 switch (uMsg) 7 { 8 處理各類消息 9 } 10 }
對比一下HelloWorld的_tWinMain函數
1 int APIENTRY _tWinMain(HINSTANCE hInstance, 2 HINSTANCE hPrevInstance, 3 LPTSTR lpCmdLine, 4 int nCmdShow) 5 { 6 UNREFERENCED_PARAMETER(hPrevInstance); 7 UNREFERENCED_PARAMETER(lpCmdLine); 8 9 // create the application instance 10 AppDelegate app; 11 12 // 如下代碼對應Windows程序設計的通常框架 13 CCEGLView* eglView = CCEGLView::sharedOpenGLView(); 14 eglView->setFrameSize(960, 640 ); 15 return CCApplication::sharedApplication()->run(); 16 }
首先,咱們看一下CCEGLView::sharedOpenGLView函數的實現細節
1 CCEGLView* CCEGLView::sharedOpenGLView() 2 { 3 static CCEGLView* s_pEglView = NULL; 4 if (s_pEglView == NULL) 5 { 6 s_pEglView = new CCEGLView(); 7 } 8 return s_pEglView; 9 }
這是單例模式的一種變形,經過CCEGLView::sharedOpenGLView函數始終獲取同一個CCEGLView對象的指針。同時它也經過new操做符調用CCEGLView的構造函數實例化了一個CCEGLView對象,而CCEGLView的構造函數只不過是完成了成員變量的初始化。可見,「註冊窗口類並同時註冊消息處理函數」並不是經過CCEGLView::sharedOpenGLView函數完成的。
接下來,咱們分析CCEGLView::setFrameSize函數。其實現以下
1 void CCEGLView::setFrameSize(float width, float height) 2 { 3 Create((LPCTSTR)m_szViewName, (int)width, (int)height); 4 CCEGLViewProtocol::setFrameSize(width, height); 5 6 resize(width, height); // adjust window size for menubar 7 centerWindow(); 8 }
哈哈,竟然有一個CCEGLView::Create函數
1 bool CCEGLView::Create(LPCTSTR pTitle, int w, int h) 2 { 3 bool bRet = false; 4 do 5 { 6 CC_BREAK_IF(m_hWnd); 7 // 建立窗口類 8 HINSTANCE hInstance = GetModuleHandle( NULL ); 9 WNDCLASS wc; // Windows Class Structure 10 11 // Redraw On Size, And Own DC For Window. 12 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 13 wc.lpfnWndProc = _WindowProc; // WndProc Handles Messages 14 wc.cbClsExtra = 0; // No Extra Window Data 15 wc.cbWndExtra = 0; // No Extra Window Data 16 wc.hInstance = hInstance; // Set The Instance 17 wc.hIcon = LoadIcon( NULL, IDI_WINLOGO ); // Load The Default Icon 18 wc.hCursor = LoadCursor( NULL, IDC_ARROW ); // Load The Arrow Pointer 19 wc.hbrBackground = NULL; // No Background Required For GL 20 wc.lpszMenuName = m_menu; // 21 wc.lpszClassName = kWindowClassName; // Set The Class Name 22 // 註冊窗口類 23 CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError()); 24 25 // center window position 26 RECT rcDesktop; 27 GetWindowRect(GetDesktopWindow(), &rcDesktop); 28 29 WCHAR wszBuf[50] = {0}; 30 MultiByteToWideChar(CP_UTF8, 0, m_szViewName, -1, wszBuf, sizeof(wszBuf)); 31 32 // 建立窗口(create window) 33 m_hWnd = CreateWindowEx( 34 WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, // Extended Style For The Window 35 kWindowClassName, // Class Name 36 wszBuf, // Window Title 37 WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX, // Defined Window Style 38 0, 0, // Window Position 39 0, // Window Width 40 0, // Window Height 41 NULL, // No Parent Window 42 NULL, // No Menu 43 hInstance, // Instance 44 NULL ); 45 46 CC_BREAK_IF(! m_hWnd); 47 48 resize(w, h); 49 50 bRet = initGL(); 51 CC_BREAK_IF(!bRet); 52 53 s_pMainWindow = this; 54 bRet = true; 55 } while (0); 56 57 return bRet; 58 }
在CCEGLView::Create函數中完成了註冊窗口類和註冊消息處理函數_WindowProc,而且完成了窗口的建立。做爲Windows程序的核心函數,咱們有必要看看_WindowProc看看
1 static CCEGLView* s_pMainWindow = NULL; 2 3 static LRESULT CALLBACK _WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 4 { 5 if (s_pMainWindow && s_pMainWindow->getHWnd() == hWnd) 6 { 7 return s_pMainWindow->WindowProc(uMsg, wParam, lParam); 8 } 9 else 10 { 11 return DefWindowProc(hWnd, uMsg, wParam, lParam); 12 } 13 }
_WindowProc函數是一個全局函數,當知足必定條件時就將消息轉給s_pMainWindow->WindowProc函數處理。s_pMainWindow是一個全局變量,在CCEGLView::Create中賦值爲this指針。那麼,咱們就得進入CCEGLView::WindowProc看看
1 LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 2 { 3 BOOL bProcessed = FALSE; 4 5 switch (message) 6 { 7 case WM_LBUTTONDOWN: 8 if (m_pDelegate && MK_LBUTTON == wParam) 9 { 10 POINT point = {(short)LOWORD(lParam), (short)HIWORD(lParam)}; 11 CCPoint pt(point.x/CC_CONTENT_SCALE_FACTOR(), point.y/CC_CONTENT_SCALE_FACTOR()); 12 CCPoint tmp = ccp(pt.x, m_obScreenSize.height - pt.y); 13 if (m_obViewPortRect.equals(CCRectZero) || m_obViewPortRect.containsPoint(tmp)) 14 { 15 m_bCaptured = true; 16 SetCapture(m_hWnd); 17 int id = 0; 18 pt.x *= m_windowTouchScaleX; 19 pt.y *= m_windowTouchScaleY; 20 handleTouchesBegin(1, &id, &pt.x, &pt.y); 21 } 22 } 23 break; 24 25 case WM_MOUSEMOVE: 26 if (MK_LBUTTON == wParam && m_bCaptured) 27 { 28 POINT point = {(short)LOWORD(lParam), (short)HIWORD(lParam)}; 29 CCPoint pt(point.x/CC_CONTENT_SCALE_FACTOR(), point.y/CC_CONTENT_SCALE_FACTOR()); 30 int id = 0; 31 pt.x *= m_windowTouchScaleX; 32 pt.y *= m_windowTouchScaleY; 33 handleTouchesMove(1, &id, &pt.x, &pt.y); 34 } 35 break; 36 37 case WM_LBUTTONUP: 38 if (m_bCaptured) 39 { 40 POINT point = {(short)LOWORD(lParam), (short)HIWORD(lParam)}; 41 CCPoint pt(point.x/CC_CONTENT_SCALE_FACTOR(), point.y/CC_CONTENT_SCALE_FACTOR()); 42 int id = 0; 43 pt.x *= m_windowTouchScaleX; 44 pt.y *= m_windowTouchScaleY; 45 handleTouchesEnd(1, &id, &pt.x, &pt.y); 46 47 ReleaseCapture(); 48 m_bCaptured = false; 49 } 50 break; 51 case WM_SIZE: 52 switch (wParam) 53 { 54 case SIZE_RESTORED: 55 CCApplication::sharedApplication()->applicationWillEnterForeground(); 56 break; 57 case SIZE_MINIMIZED: 58 CCApplication::sharedApplication()->applicationDidEnterBackground(); 59 break; 60 } 61 break; 62 case WM_KEYDOWN: 63 if (wParam == VK_F1 || wParam == VK_F2) 64 { 65 CCDirector* pDirector = CCDirector::sharedDirector(); 66 if (GetKeyState(VK_LSHIFT) < 0 || GetKeyState(VK_RSHIFT) < 0 || GetKeyState(VK_SHIFT) < 0) 67 pDirector->getKeypadDispatcher()->dispatchKeypadMSG(wParam == VK_F1 ? kTypeBackClicked : kTypeMenuClicked); 68 } 69 if ( m_lpfnAccelerometerKeyHook!=NULL ) 70 { 71 (*m_lpfnAccelerometerKeyHook)( message,wParam,lParam ); 72 } 73 break; 74 case WM_KEYUP: 75 if ( m_lpfnAccelerometerKeyHook!=NULL ) 76 { 77 (*m_lpfnAccelerometerKeyHook)( message,wParam,lParam ); 78 } 79 break; 80 case WM_CHAR: 81 { 82 if (wParam < 0x20) 83 { 84 if (VK_BACK == wParam) 85 { 86 CCIMEDispatcher::sharedDispatcher()->dispatchDeleteBackward(); 87 } 88 else if (VK_RETURN == wParam) 89 { 90 CCIMEDispatcher::sharedDispatcher()->dispatchInsertText("\n", 1); 91 } 92 else if (VK_TAB == wParam) 93 { 94 // tab input 95 } 96 else if (VK_ESCAPE == wParam) 97 { 98 // ESC input 99 //CCDirector::sharedDirector()->end(); 100 } 101 } 102 else if (wParam < 128) 103 { 104 // ascii char 105 CCIMEDispatcher::sharedDispatcher()->dispatchInsertText((const char *)&wParam, 1); 106 } 107 else 108 { 109 char szUtf8[8] = {0}; 110 int nLen = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)&wParam, 1, szUtf8, sizeof(szUtf8), NULL, NULL); 111 CCIMEDispatcher::sharedDispatcher()->dispatchInsertText(szUtf8, nLen); 112 } 113 if ( m_lpfnAccelerometerKeyHook!=NULL ) 114 { 115 (*m_lpfnAccelerometerKeyHook)( message,wParam,lParam ); 116 } 117 } 118 break; 119 case WM_PAINT: 120 PAINTSTRUCT ps; 121 BeginPaint(m_hWnd, &ps); 122 EndPaint(m_hWnd, &ps); 123 break; 124 125 case WM_CLOSE: 126 CCDirector::sharedDirector()->end(); 127 break; 128 129 case WM_DESTROY: 130 destroyGL(); 131 PostQuitMessage(0); 132 break; 133 134 default: 135 if (m_wndproc) 136 { 137 138 m_wndproc(message, wParam, lParam, &bProcessed); 139 if (bProcessed) break; 140 } 141 return DefWindowProc(m_hWnd, message, wParam, lParam); 142 } 143 144 if (m_wndproc && !bProcessed) 145 { 146 m_wndproc(message, wParam, lParam, &bProcessed); 147 } 148 return 0; 149 }
若是咱們拋開具體的消息及其處理過程,CCEGLView::WindowProc函數能夠簡化爲
1 LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 2 { 3 switch (message) 4 { 5 // 處理各類消息 6 } 7 // 調用自定義的處理函數 8 if (m_wndproc && !bProcessed) 9 { 10 m_wndproc(message, wParam, lParam, &bProcessed); 11 } 12 13 return 0; 14 }
固然,若是不知足指定的條件就會使用Windows默認的DefWindowProc方法處理消息。
最後,就剩下「循環獲取消息」這部分了。這個時候你不是想起了_tWinMain函數的最後一句代碼了嗎?讓咱們再看他一眼
1 return CCApplication::sharedApplication()->run();
鎖定目標,咱們進入CCApplication::run函數探究一番,畢竟這個函數也是咱們以前列出的重要函數之一。
2.2.4 run出來的消息
CCApplication::run函數的實現以下
1 int CCApplication::run() 2 { 3 PVRFrameEnableControlWindow(false); 4 5 // Main message loop: 6 MSG msg; 7 LARGE_INTEGER nFreq; 8 LARGE_INTEGER nLast; 9 LARGE_INTEGER nNow; 10 11 QueryPerformanceFrequency(&nFreq); 12 QueryPerformanceCounter(&nLast); 13 14 // Initialize instance and cocos2d. 15 if (!applicationDidFinishLaunching()) 16 { 17 return 0; 18 } 19 20 CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView(); 21 pMainWnd->centerWindow(); 22 ShowWindow(pMainWnd->getHWnd(), SW_SHOW); 23 24 while (1) 25 { 26 if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 27 { 28 // Get current time tick. 29 QueryPerformanceCounter(&nNow); 30 31 // If it's the time to draw next frame, draw it, else sleep a while. 32 if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart) 33 { 34 nLast.QuadPart = nNow.QuadPart; 35 CCDirector::sharedDirector()->mainLoop(); 36 } 37 else 38 { 39 Sleep(0); 40 } 41 continue; 42 } 43 44 if (WM_QUIT == msg.message) 45 { 46 // Quit message loop. 47 break; 48 } 49 50 // Deal with windows message. 51 if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg)) 52 { 53 TranslateMessage(&msg); 54 DispatchMessage(&msg); 55 } 56 } 57 58 return (int) msg.wParam; 59 }
注意了,這裏面竟然有一個循環!並且是條件爲「真」的循環!咱們先把函數簡化後再來分析
1 int CCApplication::run() 2 { 3 // 顯示窗口 4 CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView(); 5 pMainWnd->centerWindow(); 6 ShowWindow(pMainWnd->getHWnd(), SW_SHOW); 7 8 while (1) 9 { 10 if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 11 { 12 // 處理一些數據 13 continue; 14 } 15 16 if (WM_QUIT == msg.message) 17 { 18 // 退出消息循環(Quit message loop) 19 break; 20 } 21 22 // 處理快捷鍵 23 } 24 25 return (int) msg.wParam; 26 }
這下整個CCApplication::run函數就清晰了,整體來講主要完成了兩個功能:
1)顯示窗口
2)進入消息循環
到此,典型的Windows程序框架就完整了!若是你仍是暈的,請從頭再看一遍吧。
2.3 AppDelegate
做爲對HelloWorld應用程序的抽象,AppDelegate從CCApplication派生而來,重載了純虛函數
1 class AppDelegate : private cocos2d::CCApplication 2 { 3 public: 4 AppDelegate(); 5 virtual ~AppDelegate(); 6 7 /** 8 @brief Implement CCDirector and CCScene init code here. 9 @return true Initialize success, app continue. 10 @return false Initialize failed, app terminate. 11 */ 12 virtual bool applicationDidFinishLaunching(); 13 14 /** 15 @brief The function be called when the application enter background 16 @param the pointer of the application 17 */ 18 virtual void applicationDidEnterBackground(); 19 20 /** 21 @brief The function be called when the application enter foreground 22 @param the pointer of the application 23 */ 24 virtual void applicationWillEnterForeground(); 25 };
在AppDelegate類中最主要是實現了applicationDidFinishLaunching函數。在程序啓動後,執行CCApplication::run函數的過程當中就會調用這個函數
1 bool AppDelegate::applicationDidFinishLaunching() 2 { 3 // initialize director 4 CCDirector *pDirector = CCDirector::sharedDirector(); 5 6 pDirector->setOpenGLView(CCEGLView::sharedOpenGLView()); 7 8 TargetPlatform target = getTargetPlatform(); 9 10 if (target == kTargetIpad) 11 { 12 // ipad 13 CCFileUtils::sharedFileUtils()->setResourceDirectory("iphonehd"); 14 15 // don't enable retina because we don't have ipad hd resource 16 CCEGLView::sharedOpenGLView()->setDesignResolutionSize(960, 640, kResolutionNoBorder); 17 } 18 else if (target == kTargetIphone) 19 { 20 // iphone 21 22 // try to enable retina on device 23 if (true == CCDirector::sharedDirector()->enableRetinaDisplay(true)) 24 { 25 // iphone hd 26 CCFileUtils::sharedFileUtils()->setResourceDirectory("iphonehd"); 27 } 28 else 29 { 30 CCFileUtils::sharedFileUtils()->setResourceDirectory("iphone"); 31 } 32 } 33 else 34 { 35 // android, windows, blackberry, linux or mac 36 // use 960*640 resources as design resolution size 37 CCFileUtils::sharedFileUtils()->setResourceDirectory("iphonehd"); 38 CCEGLView::sharedOpenGLView()->setDesignResolutionSize(960, 640, kResolutionNoBorder); 39 } 40 41 // turn on display FPS 42 pDirector->setDisplayStats(true); 43 44 // set FPS. the default value is 1.0/60 if you don't call this 45 pDirector->setAnimationInterval(1.0 / 60); 46 47 // create a scene. it's an autorelease object 48 CCScene *pScene = HelloWorld::scene(); 49 50 // run 51 pDirector->runWithScene(pScene); 52 53 return true; 54 }
1 // This function will be called when the app is inactive. When comes a phone call,it's be invoked too 2 void AppDelegate::applicationDidEnterBackground() 3 { 4 CCDirector::sharedDirector()->stopAnimation(); 5 6 // if you use SimpleAudioEngine, it must be pause 7 // SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic(); 8 } 9 10 // this function will be called when the app is active again 11 void AppDelegate::applicationWillEnterForeground() 12 { 13 CCDirector::sharedDirector()->startAnimation(); 14 15 // if you use SimpleAudioEngine, it must resume here 16 // SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic(); 17 }
如註釋所陳述的,applicationDidEnterBackground函數會在程序進入「非活躍」狀態(即失去窗口焦點)時被調用,而applicationWillEnterForeground函數會在程序進入「活躍」狀態(即得到窗口焦點)時被調用(能夠本身在這個函數裏面下斷點看看具體執行的流程)。
舞臺須要場景
演員站在舞臺上,卻表演於場景中
—— by 我掛科了
賦詞一句略顯文藝範兒,求勿噴!言歸正傳,首先咱們來看看HelloWorld的繼承關係
HelloWorld從CCLayer繼承,而CCLayer又是一個很是複雜的(至少它的father太多了)。你必定以爲HelloWorld很複雜吧,其實它沒有傳說中那麼複雜
1 class HelloWorld : public cocos2d::CCLayer 2 { 3 public: 4 // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone 5 virtual bool init(); 6 7 // there's no 'id' in cpp, so we recommend returning the class instance pointer 8 static cocos2d::CCScene* scene(); 9 10 // a selector callback 11 void menuCloseCallback(CCObject* pSender); 12 13 // touch callback 14 void ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent); 15 16 // implement the "static node()" method manually 17 CREATE_FUNC(HelloWorld); 18 };
多麼單純的類啊!你也許會說:CREATE_FUNC是神馬東東?它一點都不單純啊~
3.一、CREATE_FUNC
其實HelloWorld仍是很單純的,由於CREATE_FUNC定義很簡單
1 #define CREATE_FUNC(__TYPE__) \ 2 static __TYPE__* create() \ 3 { \ 4 __TYPE__ *pRet = new __TYPE__(); \ 5 if (pRet && pRet->init()) \ 6 { \ 7 pRet->autorelease(); \ 8 return pRet; \ 9 } \ 10 else \ 11 { \ 12 delete pRet; \ 13 pRet = NULL; \ 14 return NULL; \ 15 } \ 16 }
CREATE_FUNC是一個宏,它定義了一個名爲create的static函數,該函數完成下面幾個事情:
1)建立__TYPE__類型的對象指針
2)若是建立成功,則調用該對象的init函數
a)若是init函數執行成功,則調用該對象的autorelease函數並返回該對象指針
b)若是init函數執行失敗,則釋放該對象並返回NULL
將這個宏在HelloWorld類中展開,HelloWorld就露出了它的真面目了
1 class HelloWorld : public cocos2d::CCLayer 2 { 3 public: 4 // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone 5 virtual bool init(); 6 7 // there's no 'id' in cpp, so we recommend returning the class instance pointer 8 static cocos2d::CCScene* scene(); 9 10 // a selector callback 11 void menuCloseCallback(CCObject* pSender); 12 13 // touch callback 14 void ccTouchesBegan(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent); 15 16 // implement the "static node()" method manually 17 static HelloWorld* create() 18 { 19 HelloWorld *pRet = new HelloWorld(); 20 if (pRet && pRet->init()) 21 { 22 pRet->autorelease(); 23 return pRet; 24 } 25 else 26 { 27 delete pRet; 28 pRet = NULL; 29 return NULL; 30 } 31 } 32 };
我比較奇怪的是,爲何註釋上寫的是「static node()」而不是「static create()」呢?隱約聽到有人在喊:這必定是筆誤!好吧,不要在乎這些細節。
3.2 夢迴AppDelegate
還記得AppDelegate::applicationDidFinishLauching函數嗎?它的實現中有這麼一句
1 bool AppDelegate::applicationDidFinishLaunching() 2 { 3 // 其餘操做 4 5 // create a scene. it's an autorelease object 6 CCScene *pScene = HelloWorld::scene(); 7 // run 8 pDirector->runWithScene(pScene); 9 10 // 其餘操做 11 }
這是咱們的HelloWorld第一次在程序中被使用,那咱們就從HelloWorld::scene函數入手吧
1 CCScene* HelloWorld::scene() 2 { 3 // 'scene' is an autorelease object 4 CCScene *scene = CCScene::create(); 5 6 // 'layer' is an autorelease object 7 HelloWorld *layer = HelloWorld::create(); 8 9 // add layer as a child to scene 10 scene->addChild(layer); 11 12 // return the scene 13 return scene; 14 }
這個函數的實現完成了3個事情:
1)建立了一個空的CCScene對象scene
2)經過上面分析過的HelloWorld::create函數建立了一個HelloWorld對象layer,並將layer做爲scene的一個子節點添加到scene中
3)將scene返回
3.3 初始化
在前面咱們已經展現了HelloWorld::create函數,它建立了HelloWorld對象後會調用該對象的init函數來初始化對象。HelloWorld::init函數實現以下
1 // on "init" you need to initialize your instance 2 bool HelloWorld::init() 3 { 4 ////////////////////////////// 5 // 1. super init first 6 if ( !CCLayer::init() ) 7 { 8 return false; 9 } 10 11 CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); 12 CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin(); 13 14 ///////////////////////////// 15 // 2. add a menu item with "X" image, which is clicked to quit the program 16 // you may modify it. 17 18 // add a "close" icon to exit the progress. it's an autorelease object 19 CCMenuItemImage *pCloseItem = CCMenuItemImage::create( 20 "CloseNormal.png", 21 "CloseSelected.png", 22 this, 23 menu_selector(HelloWorld::menuCloseCallback)); 24 pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 , 25 origin.y + pCloseItem->getContentSize().height/2)); 26 // create menu, it's an autorelease object 27 CCMenu* pMenu = CCMenu::create(pCloseItem, NULL); 28 pMenu->setPosition(CCPointZero); 29 this->addChild(pMenu, 1); 30 31 ///////////////////////////// 32 // 3. add your codes below... 33 34 // add a label shows "Hello World" 35 // create and initialize a label 36 CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24); 37 // position the label on the center of the screen 38 pLabel->setPosition(ccp(origin.x + visibleSize.width/2, 39 origin.y + visibleSize.height - pLabel->getContentSize().height)); 40 // add the label as a child to this layer 41 this->addChild(pLabel, 1); 42 43 // add "HelloWorld" splash screen" 44 CCSprite* pSprite = CCSprite::create("HelloWorld.png"); 45 // position the sprite on the center of the screen 46 pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y)); 47 // add the sprite as a child to this layer 48 this->addChild(pSprite, 0); 49 50 // enable standard touch 51 this->setTouchEnabled(true); 52 53 return true; 54 }
不要看這個函數很長,實際上主要完成了4個事情
1)調用父類的初始化函數(CCLayer::init())完成對繼承自父類成員的初始化
2)建立一個菜單(CCMenu)並在其中添加一個菜單項(CCMenuItem),將菜單顯示在HelloWorld中。點擊這個菜單項能夠關閉程序
3)建立一個標籤(CCLabel),讓它在HelloWorld中顯示「HelloWorld」字串
4)建立一個精靈(CCSprite),讓它在HelloWorld中顯示圖片「HelloWorld.png」
HelloWorld::init函數中所建立的都是咱們在運行起來的界面中所能看到的東西,這其中涉及到一些類將在後面學習,這裏不深究。我比較好奇的是菜單項所實現的功能:點擊後關閉程序(不信你親自點一下試試)。這是怎麼實現的呢?仔細分析一下菜單項的建立過程
1 // add a "close" icon to exit the progress. it's an autorelease object 2 CCMenuItemImage *pCloseItem = CCMenuItemImage::create( 3 "CloseNormal.png", 4 "CloseSelected.png", 5 this, 6 menu_selector(HelloWorld::menuCloseCallback));
最後一個參數形式很特別啊~ 咱們先看看menu_selector是神馬東西
1 #define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR)
又是一個宏!它所完成的事情是將_SELECTOR取址並強制轉換爲SEL_MenuHandler類型。那麼SEL_MenuHandler又是什麼類型呢?
1 typedef void (CCObject::*SEL_MenuHandler)(CCObject*);
它是一個函數指針,這類函數有一個CCObject*類型的參數。此時咱們能夠將建立菜單項的代碼展開,看看其真實的原貌
1 // add a "close" icon to exit the progress. it's an autorelease object 2 CCMenuItemImage *pCloseItem = CCMenuItemImage::create( 3 "CloseNormal.png", 4 "CloseSelected.png", 5 this, 6 (SEL_MenuHandler)&HelloWorld::menuCloseCallback);
HelloWorld::menuCloseCallback函數以回調函數的方式傳入菜單項,在點擊菜單項時被觸發。也就是說實現關閉程序功能的是HelloWorld::menuCloseCallback函數
1 void HelloWorld::menuCloseCallback(CCObject* pSender) 2 { 3 CCDirector::sharedDirector()->end(); 4 5 #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) 6 exit(0); 7 #endif 8 }
最終,由CCDirector::end函數完成程序的關閉(在IOS中還須要調用exit函數)。
參考文獻
歡迎轉載,但請保留原文出處:http://www.cnblogs.com/xieheng/p/3611588.html