Cocos2d-x学习笔记(二) 永远的HelloWorld

前端之家收集整理的这篇文章主要介绍了Cocos2d-x学习笔记(二) 永远的HelloWorld前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

HelloCpp是Cocos2d-x自带的一个工程,它演示了Cocos2d-x最基本的使用方法和流程。先看一下它的基本构成

win32目录中包含了对应平台的代码,而Classes目录中包含了我们自己的实现代码。编译运行的结果如下图


main函数变形记


看到main命名的文件就会想到著名的main函数一定在这个文件里面,那么就让我们先看看这个文件吧。main.h里面主要是include了各种各样的头文件,main.cpp是我们真正需要关注的

 1 int APIENTRY _tWinMain(HINSTANCE hInstance, 2                        HINSTANCE hPrevInstance,128); line-height:1.5!important"> 3                        LPTSTR    lpCmdLine,128); line-height:1.5!important"> 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 APIENTRY

首先出现的不明物体是APIENTRY。我们先看看它的定义

1 #define APIENTRY    WINAPI

APIENTRY是WINAPI的一个替身,而WINAPI是微软定义的宏,实际就是__stdcall

#define WINAPI __stdcall

众所周知,__stdcall声明了函数从右至左将参数压栈并由被调用者清理堆栈的调用约定

1.2 _tWinMain

如果我说_tWinMain是程序的入口函数,你会不会发飙:我去,入口函数不应该是main吗?是的!对于常规的C/C++程序而言main是入口函数的归宿,但在Windows程序中WinMain才是入口函数

好吧!但为什么这里对的入口函数是_tWinMain而不是WinMain呢?我们先来看看_tWinMain的真正定义
1 #ifdef _UNICODE
2 
3     #define _tWinMain wWinMain
4 
5 #else
6 
7     #define _tWinMain WinMain
8 
9 #endif
为了支持UNICODE,C运行库对WinMain区分了UNICODE版和ANSI版。对UNICODE版的程序,C运行库将调用wWinMain;而对于ANSI版的应用,则调用WinMain。至于WinMain更深入的知识请阅读 《Windows程序设计》,这里只需要知道它是Windows程序的入口函数即可。

1.3 UNREFERENCED_PARAMETER

进入_tWinMain函数后就是两句很神奇的语句

1 UNREFERENCED_PARAMETER(hPrevInstance); 2 UNREFERENCED_PARAMETER(lpCmdLine);

要理解这句话首先是要搞清楚UNREFERENCED_PARAMETER是神马玩意儿。如果我告诉你它神马都不是,你信吗?

#define UNREFERENCED_PARAMETER(P) (P)

承认吧骚年,它真的神马都不是啊~(你逗我玩儿呐?)

AppDelegate的前世今生


_tWinMain函数的真正主体是从AppDelegate app开始的,所以我们就首先从AppDelegate说起。我们先看看AppDelegate的宗族关系

要把AppDelegate弄清楚搞明白,我们还得从源头开始。

2.1 CCApplicationProtocol

作为Cocos2d-x Application的源头,CCApplicationProtocal定义如下

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 void applicationDidEnterBackground() = 19 20 21 @brief The function be called when the application enter foreground 22 23 24 void applicationWillEnterForeground() = 25 26 27 @brief Callback by CCDirector for limit FPS. 28 @interval The time,expressed in seconds,between current frame and next. 29 30 void setAnimationInterval(double interval) = 31 32 33 @brief Get current language config 34 @return Current language config 35 36 virtual ccLanguageType getCurrentLanguage() = 37 38 39 @brief Get target platform 40 41 virtual TargetPlatform getTargetPlatform() = 42 };
可以看到,CCApplicationProtocol是一个抽象类,它定义并导出作为DLL的接口。这其中有一个陌生CC_DLL,它定义了在DLL中的符号是导出还是导入

#if defined(_USRDLL) 2 #define CC_DLL __declspec(dllexport) 3 #else /* use a DLL library */ 4 #define CC_DLL __declspec(dllimport) #endif

整个CCApplicationProtocol除了析构函数以外的其他所有函数都是纯虚函数,这也就是它为什么叫Protocol的原因。每个函数的含义和作用在注释里有简要的说明,但具体的实现何其作用需要进一步才能理解。

2.2 CCApplication

作为对Cocos2d-x Application的抽象,CCApplication所扮演的角色是非常重要的。它的定义如下

class CC_DLL CCApplication : public CCApplicationProtocol 4 CCApplication(); virtual ~CCApplication(); @brief Run the message loop. 10 int run(); 11 13 @brief Get current applicaiton instance. 14 @return Current application instance pointer. 15 16 static CCApplication* sharedApplication(); 17 override functions 19 double interval); virtual ccLanguageType getCurrentLanguage(); 21 22 23 24 25 virtual TargetPlatform getTargetPlatform(); 26 27 set the Resource root path 28 void setResourceRootPath(const std::string& rootResDir); 29 get the Resource root path 31 string& getResourceRootPath(void) 32 { 33 return m_resourceRootPath; 34 } 35 void setStartupScriptFilename(string& startupScriptFile); 37 string& getStartupScriptFilename(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 };
虽然CCApplication提供了public的构造函数,但我们却不能直接实例化CCApplication的,因为它没有实现CCApplicationProtocol定义的所有接口函数(它还是一个抽象类)。
就Hello World这个示例而言,我们需要关注的并不多:构造函数、sharedApplication函数和run函数,我们会进一步全面剖析这些函数

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 = 7 CC_ASSERT(! sm_pSharedApplication); 8 sm_pSharedApplication = this; 9 }
m_hInstance保存了当前模块的句柄,而sm_pSharedApplicaton则保存了当前对象的指针。值得注意的是,sm_pSharedApplication是一个static的CCApplication对象指针

2.2.2 Application是唯一的

我们再把_tWinMain函数请出来仔细看看(这里去掉了几句无用的代码)

int nCmdShow) { create the application instance AppDelegate app; CCEGLView* eglView = CCEGLView::sharedOpenGLView(); eglView->setFrameSize(640 ); return CCApplication::sharedApplication()->run(); }
是否对AppDelegate app这句感到疑惑:在定义了app以后却从未使用过它。那么我们是不是可以理解为这句是多余的呢?好吧!我们把它注释掉,结果程序崩溃了!崩溃了!!溃了!!!

这是由sm_pSharedApplication引发的断言异常。sm_pSharedApplication是CCApplication类的一个protected的static成员,虽然在构造函数中将它赋值为this,但它的初始化却是0(空指针)

sharedApplication pointer 2 CCApplication * CCApplication::sm_pSharedApplication = 0;

同时CCApplication提供一个public的static函数来访问它

1 CCApplication* CCApplication::sharedApplication() 2 { 3 CC_ASSERT(sm_pSharedApplication); return sm_pSharedApplication; 5 }

若果你熟悉设计模式就能一眼看出来这个实现实际上是单例模式,它保证了CCApplication只有一个实例。

看到这里,你可能会惊呼:一定是_tWinMain函数的最后一句return CCApplication::sharedApplication()->run() 引入了错误。哎,事实总是残酷的!实际上异常早在这之前就发生了。我们在CCApplication::sharedApplication函数中下断点,F5运行程序,从CallStack可以看到异常在CCEGLView::WindowProc函数中就发生了

进入CCEGLView::WindowProc函数,很快我们就能发现引发异常的代码

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 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程序的框架大体如下

int WINAPI WinMain(HINSTANCE hInstance,128); line-height:1.5!important">2 HINSTANCE hPreInstance,128); line-height:1.5!important">3 PSTR szCmdLine,128); line-height:1.5!important">4 int iCmdShow) 5 { 6 1注册窗口类,并注册消息处理函数WindowProc 7 2)创建并显示窗口 8 3)循环获取消息 消息处理函数的结构如下

1 LRESULT CALLBACK WindowProc(HWND hwnd,128); line-height:1.5!important"> 2 UINT uMsg,128); line-height:1.5!important"> 3 WPARAM wParam,128); line-height:1.5!important"> 4 LPARAM lParam) 6 switch (uMsg) 7 { 8 处理各种消息 9 } 10 }
对比一下HelloWorld的_tWinMain函数

以下代码对应Windows程序设计的一般框架 13 CCEGLView* eglView = CCEGLView::sharedOpenGLView(); 14 eglView->setFrameSize(16 }
首先,我们看一下CCEGLView::sharedOpenGLView函数的实现细节

1 CCEGLView* CCEGLView::sharedOpenGLView() static CCEGLView* s_pEglView = NULL; if (s_pEglView == NULL) 5 { 6 s_pEglView = new CCEGLView(); 7 } 8 return s_pEglView; 这是单例模式的一种变形,通过CCEGLView::sharedOpenGLView函数始终获取同一个CCEGLView对象的指针。同时它也通过new操作符调用CCEGLView的构造函数实例化了一个CCEGLView对象,而CCEGLView的构造函数只不过是完成了成员变量的初始化。可见,“注册窗口类并同时注册消息处理函数”并非通过CCEGLView::sharedOpenGLView函数完成的。

接下来,我们分析CCEGLView::setFrameSize函数。其实现如下

void CCEGLView::setFrameSize(float width,float height) 3 Create((LPCTSTR)m_szViewName,(int)width,255); line-height:1.5!important">int)height); 4 CCEGLViewProtocol::setFrameSize(width,height); 5 6 resize(width,height); adjust window size for menubar 7 centerWindow(); 8 }
哈哈,居然有一个CCEGLView::Create函数

bool CCEGLView::Create(LPCTSTR pTitle,255); line-height:1.5!important">int w,255); line-height:1.5!important">int h) 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 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 = 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,128); line-height:1.5!important">0,m_szViewName,-1,wszBuf,255); line-height:1.5!important">sizeof(wszBuf)); 32 创建窗口(create window) 33 m_hWnd = CreateWindowEx( 34 WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,0); line-height:1.5!important"> Extended Style For The Window 35 kWindowClassName,0); line-height:1.5!important"> Class Name 36 wszBuf,0); line-height:1.5!important"> Window Title 37 WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBox,0); line-height:1.5!important"> Defined Window Style 38 Window Position 39 Window Width 40 Window Height 41 NULL,0); line-height:1.5!important"> No Parent Window 42 NULL,0); line-height:1.5!important"> No Menu 43 hInstance,0); line-height:1.5!important"> Instance 44 NULL ); 45 46 CC_BREAK_IF(! m_hWnd); 47 48 resize(w,h); 50 bRet = initGL(); 51 CC_BREAK_IF(!bRet); 52 53 s_pMainWindow = 54 bRet = true; 55 } while (0); 56 57 return bRet; 58 }
在CCEGLView::Create函数中完成了注册窗口类和注册消息处理函数_WindowProc,并且完成了窗口的创建。作为Windows程序的核心函数,我们有必要看看_WindowProc看看

static CCEGLView* s_pMainWindow = NULL; 2 static LRESULT CALLBACK _WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) 4 { if (s_pMainWindow && s_pMainWindow->getHWnd() == hWnd) 6 { return s_pMainWindow->WindowProc(uMsg,wParam,lParam); 8 } else 10 { return DefWindowProc(hWnd,uMsg,128); line-height:1.5!important">12 } 13 }
_WindowProc函数是一个全局函数,当满足一定条件时就将消息转给s_pMainWindow->WindowProc函数处理。s_pMainWindow是一个全局变量,在CCEGLView::Create中赋值为this指针。那么,我们就得进入CCEGLView::WindowProc看看

1 LRESULT CCEGLView::WindowProc(UINT message,128); line-height:1.5!important"> 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),255); line-height:1.5!important">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 = 16 SetCapture(m_hWnd); 17 int id = 18 pt.x *= m_windowTouchScaleX; 19 pt.y *= m_windowTouchScaleY; 20 handleTouchesBegin(pt.y); 21 } 22 } 23 24 25 case WM_MOUSEMOVE: 26 if (MK_LBUTTON == wParam && m_bCaptured) 27 { 28 POINT point = {( 29 CCPoint pt(point.x/CC_CONTENT_SCALE_FACTOR(),128); line-height:1.5!important"> 30 31 pt.x *= m_windowTouchScaleX; 32 pt.y *= m_windowTouchScaleY; 33 handleTouchesMove( 34 } 35 36 37 case WM_LBUTTONUP: 38 if (m_bCaptured) 39 { 40 POINT point = {( 41 CCPoint pt(point.x/CC_CONTENT_SCALE_FACTOR(),128); line-height:1.5!important"> 42 43 pt.x *= m_windowTouchScaleX; 44 pt.y *= m_windowTouchScaleY; 45 handleTouchesEnd( 46 47 ReleaseCapture(); 48 m_bCaptured = 49 } 50 51 52 53 { 54 55 CCApplication::sharedApplication()->applicationWillEnterForeground(); 56 57 58 CCApplication::sharedApplication()->applicationDidEnterBackground(); 59 60 } 61 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,lParam ); 72 } 73 74 case WM_KEYUP: 75 76 { 77 (*m_lpfnAccelerometerKeyHook)( message,128); line-height:1.5!important"> 78 } 79 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",128); line-height:1.5!important">1); 91 } 92 if (VK_TAB == wParam) 93 { 94 tab input 95 } 96 if (VK_ESCAPE == wParam) 97 { 98 ESC input 99 CCDirector::sharedDirector()->end(); 100 } 101 } 102 128) 103 { 104 ascii char 105 CCIMEDispatcher::sharedDispatcher()->dispatchInsertText((const char *)&wParam,128); line-height:1.5!important">106 } 107 108 { 109 char szUtf8[8] = {110 int nLen = WideCharToMultiByte(CP_UTF8,(LPCWSTR)&wParam,szUtf8,255); line-height:1.5!important">sizeof(szUtf8),NULL,NULL); 111 CCIMEDispatcher::sharedDispatcher()->dispatchInsertText(szUtf8,nLen); 112 } 113 114 { 115 (*m_lpfnAccelerometerKeyHook)( message,128); line-height:1.5!important">116 } 117 } 118 119 case WM_PAINT: 120 PAINTSTRUCT ps; 121 BeginPaint(m_hWnd,&ps); 122 EndPaint(m_hWnd,128); line-height:1.5!important">123 124 125 case WM_CLOSE: 126 CCDirector::sharedDirector()->end(); 127 128 129 case WM_DESTROY: 130 destroyGL(); 131 PostQuitMessage(132 133 134 default: 135 if (m_wndproc) 136 { 137 138 m_wndproc(message,lParam,&bProcessed); 139 if (bProcessed) 140 } 141 return DefWindowProc(m_hWnd,message,128); line-height:1.5!important">142 } 143 144 if (m_wndproc && !bProcessed) 145 { 146 m_wndproc(message,128); line-height:1.5!important">147 } 148 return 149 }
如果我们抛开具体的消息及其处理过程,CCEGLView::WindowProc函数可以简化为

1 LRESULT CCEGLView::WindowProc(UINT message,128); line-height:1.5!important"> 4 { 5 处理各种消息 6 } 调用自定义的处理函数 8 9 { 10 m_wndproc(message,128); line-height:1.5!important">11 } 12 当然,如果不满足指定的条件就会使用Windows默认的DefWindowProc方法处理消息。

最后,就剩下“循环获取消息”这部分了。这个时候你不是想起了_tWinMain函数的最后一句代码了吗?让我们再看他一眼

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

锁定目标,我们进入CCApplication::run函数探究一番,毕竟这个函数也是我们之前列出的重要函数之一。

2.2.4 run出来的消息

CCApplication::run函数的实现如下

int CCApplication::run() 3 PVRFrameEnableControlWindow(false); Main message loop: 6 MSG msg; 7 LARGE_INTEGER nFreq; 8 LARGE_INTEGER nLast; 9 LARGE_INTEGER nNow; 11 QueryPerformanceFrequency(&nFreq); 12 QueryPerformanceCounter(&nLast); Initialize instance and cocos2d. if (!applicationDidFinishLaunching()) 16 { 17 18 } 20 CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView(); 21 pMainWnd->centerWindow(); 22 ShowWindow(pMainWnd->getHWnd(),SW_SHOW); 23 1) 25 { 26 if (! PeekMessage(&msg,128); line-height:1.5!important">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 38 { 39 Sleep(40 } 41 continue; 42 } 43 44 if (WM_QUIT == msg.message) 45 { 46 Quit message loop. 47 48 } 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 }
注意了,这里面居然有一个循环!而且是条件为“真”的循环!我们先把函数简化后再来分析

显示窗口 4 CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView(); 5 pMainWnd->centerWindow(); 6 ShowWindow(pMainWnd->getHWnd(),128); line-height:1.5!important"> 7 10 11 { 12 处理一些数据 13 14 } 15 16 17 { 18 退出消息循环(Quit message loop) 19 20 } 21 22 处理快捷键 23 } 26 }
这下整个CCApplication::run函数就清晰了,总体来说主要完成了两个功能

1)显示窗口

2)进入消息循环

到此,典型的Windows程序框架就完整了!如果你还是晕的,请从头再看一遍吧。

2.3 AppDelegate

作为对HelloWorld应用程序的抽象,AppDelegate从CCApplication派生而来,重载了纯虚函数

class AppDelegate : private cocos2d::CCApplication 4 AppDelegate(); virtual ~AppDelegate(); bool applicationDidFinishLaunching(); void applicationDidEnterBackground(); void applicationWillEnterForeground(); 25 };
在AppDelegate类中最主要是实现了applicationDidFinishLaunching函数。在程序启动后,执行CCApplication::run函数的过程中就会调用这个函数

bool AppDelegate::applicationDidFinishLaunching() initialize director 4 CCDirector *pDirector = CCDirector::sharedDirector(); 5 6 pDirector->setOpenGLView(CCEGLView::sharedOpenGLView()); 7 8 TargetPlatform target = getTargetPlatform(); 9 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(640,kResolutionNoBorder); 17 } if (target == kTargetIphone) 19 { 20 iphone 21 try to enable retina on device 23 if (true == CCDirector::sharedDirector()->enableRetinaDisplay(true)) 24 { 25 iphone hd 26 CCFileUtils::sharedFileUtils()->setResourceDirectory(27 } 28 else 29 { 30 CCFileUtils::sharedFileUtils()->setResourceDirectory(iphone31 } 32 } 33 34 { 35 android,windows,blackberry,linux or mac 36 use 960*640 resources as design resolution size 37 CCFileUtils::sharedFileUtils()->setResourceDirectory(38 CCEGLView::sharedOpenGLView()->setDesignResolutionSize(39 } 40 turn on display FPS 42 pDirector->setDisplayStats(true); 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(); run 51 pDirector->runWithScene(pScene); 52 53 return 54 }
这个函数主要是完成CCDirector类和CCScene类对象的初始化,设置资源路径、分辨率大小和帧率(FPS:Frames Per Second);最后通过CCDirector::runWithScene函数开始场景。
对于另外两个函数,他们的实现就相对简单的多(至少代码量就少很多):把所有事情都交给CCDirector去完成
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(); if you use SimpleAudioEngine,it must be pause SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic(); 8 } 9 this function will be called when the app is active again 11 void AppDelegate::applicationWillEnterForeground() 12 { 13 CCDirector::sharedDirector()->startAnimation(); 14 SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic(); 17 }

如注释所陈述的,applicationDidEnterBackground函数会在程序进入“非活跃”状态(即失去窗口焦点)时被调用,而applicationWillEnterForeground函数会在程序进入“活跃”状态(即获得窗口焦点)时被调用可以自己在这个函数里面下断点看看具体执行的流程)。


舞台需要场景


演员站在舞台上,却表演于场景中

—— by 我挂科了

赋词一句略显文艺范儿,求勿喷!言归正传,首先我们来看看HelloWorld的继承关系

HelloWorld从CCLayer继承,而CCLayer又是一个非常复杂的(至少它的father太多了)。你一定觉得HelloWorld很复杂吧,其实它没有传说中那么复杂

class HelloWorld : public cocos2d::CCLayer Here's a difference. Method 'init' in cocos2d-x returns bool,instead of returning 'id' in cocos2d-iphone bool init(); there's no 'id' in cpp,so we recommend returning the class instance pointer static cocos2d::CCScene* scene(); a selector callback void menuCloseCallback(CCObject* pSender); touch callback void ccTouchesBegan(cocos2d::CCSet *pTouches,cocos2d::CCEvent *pEvent); 15 implement the "static node()" method manually 17 CREATE_FUNC(HelloWorld); 18 };

多么单纯的类啊!你也许会说:CREATE_FUNC是神马东东?它一点都不单纯啊~

3.1、CREATE_FUNC

其实HelloWorld还是很单纯的,因为CREATE_FUNC定义很简单

#define CREATE_FUNC(__TYPE__) \ static __TYPE__* create() \ 3 { \ 4 __TYPE__ *pRet = new __TYPE__(); \ if (pRet && pRet->init()) \ 6 { \ 7 pRet->autorelease(); \ 8 return pRet; \ 9 } \ 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就露出了它的真面目了

static HelloWorld* create() 18 { 19 HelloWorld *pRet = new HelloWorld(); if (pRet && pRet->init()) 21 { 22 pRet->autorelease(); 23 return pRet; 24 } 26 { 27 delete pRet; 28 pRet = NULL; 29 return NULL; 30 } 31 } 32 };

我比较奇怪的是,为什么注释上写的是“static node()”而不是“static create()”呢?隐约听到有人在喊:这一定是笔误!好吧,不要在意这些细节。

3.2 梦回AppDelegate

还记得AppDelegate::applicationDidFinishLauching函数吗?它的实现中有这么一句

bool AppDelegate::applicationDidFinishLaunching() 其他操作 6 CCScene *pScene = HelloWorld::scene(); 8 pDirector->runWithScene(pScene); 其他操作 11 }

这是我们的HelloWorld第一次在程序中被使用,那我们就从HelloWorld::scene函数入手吧

1 CCScene* HelloWorld::scene() 'scene' is an autorelease object 4 CCScene *scene = CCScene::create(); 5 'layer' is an autorelease object 7 HelloWorld *layer = HelloWorld::create(); add layer as a child to scene 10 scene->addChild(layer); return the scene 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函数实现如下

on "init" you need to initialize your instance bool HelloWorld::init() 4 ////////////////////////////// 1. super init first if ( !CCLayer::init() ) 10 11 CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize(); 12 CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin(); 14 ///////////////////////////// 2. add a menu item with "X" image,which is clicked to quit the program you may modify it. add a "close" icon to exit the progress. it's an autorelease object 19 CCMenuItemImage *pCloseItem = CCMenuItemImage::create( 20 CloseNormal.png",128); line-height:1.5!important">21 CloseSelected.png22 this,128); line-height:1.5!important">23 menu_selector(HelloWorld::menuCloseCallback)); 24 pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2,128); line-height:1.5!important">25 origin.y + pCloseItem->getContentSize().height/2)); create menu,it's an autorelease object 27 CCMenu* pMenu = CCMenu::create(pCloseItem,128); line-height:1.5!important">28 pMenu->setPosition(CCPointZero); this->addChild(pMenu,128); line-height:1.5!important">31 3. add your codes below... 33 34 add a label shows "Hello World" create and initialize a label 36 CCLabelTTF* pLabel = CCLabelTTF::create(Hello WorldArial24); 37 position the label on the center of the screen 38 pLabel->setPosition(ccp(origin.x + visibleSize.width/39 origin.y + visibleSize.height - pLabel->getContentSize().height)); 40 add the label as a child to this layer this->addChild(pLabel,128); line-height:1.5!important">43 add "HelloWorld" splash screen" 44 CCSprite* pSprite = CCSprite::create(HelloWorld.png45 position the sprite on the center of the screen 46 pSprite->setPosition(ccp(visibleSize.width/2 + origin.x,visibleSize.height/2 + origin.y)); add the sprite as a child to this layer 48 this->addChild(pSprite,128); line-height:1.5!important">49 enable standard touch 51 this->setTouchEnabled(52 54 }

不要看这个函数很长,实际上主要完成了4个事情

1)调用父类的初始化函数(CCLayer::init())完成对继承自父类成员的初始化

2)创建一个菜单(CCMenu)并在其中添加一个菜单项(CCMenuItem),将菜单显示在HelloWorld中。点击这个菜单项可以关闭程序

3)创建一个标签(CCLabel),让它在HelloWorld中显示“HelloWorld”字串

4)创建一个精灵(CCSprite),让它在HelloWorld中显示图片“HelloWorld.png”

HelloWorld::init函数中所创建的都是我们在运行起来的界面中所能看到的东西,这其中涉及到一些类将在后面学习,这里不深究。我比较好奇的是菜单项所实现的功能:点击后关闭程序(不信你亲自点一下试试)。这是怎么实现的呢?仔细分析一下菜单项的创建过程

2 CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
3                                     4                                     5                                     6                                     menu_selector(HelloWorld::menuCloseCallback));

最后一个参数形式很特别啊~ 我们先看看menu_selector是神马东西

#define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR)

又是一个宏!它所完成的事情是将_SELECTOR取址并强制转换为SEL_MenuHandler类型。那么SEL_MenuHandler又是什么类型呢?

1 typedef void (CCObject::*SEL_MenuHandler)(CCObject*);

它是一个函数指针,这类函数有一个CCObject*类型的参数。此时我们可以将创建菜单项的代码展开,看看其真实的原貌

6 (SEL_MenuHandler)&HelloWorld::menuCloseCallback);

HelloWorld::menuCloseCallback函数以回调函数的方式传入菜单项,在点击菜单项时被触发。也就是说实现关闭程序功能的是HelloWorld::menuCloseCallback函数

void HelloWorld::menuCloseCallback(CCObject* pSender) 3 CCDirector::sharedDirector()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) 6 exit(7 #endif 8 }

最终,由CCDirector::end函数完成程序的关闭(在IOS中还需要调用exit函数)。


参考文献


Cocos2d-x 高级开发教程:制作自己的捕鱼达人


FROM: http://www.cnblogs.com/xieheng/p/3611588.html

猜你在找的Cocos2d-x相关文章