cocos2d-x Helloworld 详解

cocos2d-x Helloworld 详解,第1张

概述HelloCpp是Cocos2d-x自带的一个工程,它演示了Cocos2d-x最基本的使用方法和流程。先看一下它的基本构成 win32目录中包含了对应平台的代码,而Classes目录中包含了我们自己的实现代码。编译运行的结果如下图    main函数变形记    看到main命名的文件就会想到著名的main函数一定在这个文件里面,那么就让我们先看看这个文件吧。main.h里面主要是include了

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 instance10     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 _UNICODE2 3     #define _tWinMain wWinMain4 5 #else6 7     #define _tWinMain WinMain8 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 background16 @param the pointer of the application17 18 voID applicationDIDEnterBackground() = 19 20 21 @brIEf The function be called when the application enter foreground22 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 config34 @return Current language config35 36 virtual cclanguageType getCurrentLanguage() = 37 38 39 @brIEf Get target platform40 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则保存了当前对象(app)的指针。值得注意的是,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)注册窗口类,并注册消息处理函数WindowProc7 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 menubar7 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 Structure10 Redraw On Size,And Own DC For Window.12 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 13 wc.lpfnWndProc = _WindowProc; WndProc Handles Messages14 wc.cbClsExtra = 0; No Extra Window Data15 wc.cbWndExtra = 16 wc.hInstance = hInstance; Set The Instance17 wc.hIcon = LoadIcon( NulL,IDI_WINlogo ); Load The Default Icon18 wc.hCursor = LoadCursor( NulL,IDC_ARROW ); Load The Arrow Pointer19 wc.hbrBackground = NulL; No Background required For GL20 wc.lpszMenuname = m_menu; // 21 wc.lpszClassname = kWindowClassname; Set The Class name22 // 注册窗口类23 CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError()); 24 25 center window position26 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 Window35 kWindowClassname,0); line-height:1.5!important"> Class name36 wszBuf,0); line-height:1.5!important"> Window Title37 WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBox,0); line-height:1.5!important"> defined Window Style38 Window position39 Window WIDth40 Window Height41 NulL,0); line-height:1.5!important"> No Parent Window42 NulL,0); line-height:1.5!important"> No Menu43 hInstance,0); line-height:1.5!important"> Instance44 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 }else10 {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 char105 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 ipad13 CCfileUtils::sharedfileUtils()->setResourceDirectory(iphonehd");14 15 don't enable retina because we don't have ipad hd resource16 CCEGLVIEw::sharedOpenGLVIEw()->setDesignResolutionSize(640,kResolutionNoborder);17 }if (target == kTargetIphone)19 {20 iphone21 try to enable retina on device23 if (true == CCDirector::sharedDirector()->enableRetinadisplay(true))24 {25 iphone hd26 CCfileUtils::sharedfileUtils()->setResourceDirectory(27 }28 else 29 {30 CCfileUtils::sharedfileUtils()->setResourceDirectory(iphone31 }32 }33 34 {35 androID,windows,blackBerry,linux or mac36 use 960*640 resources as design resolution size37 CCfileUtils::sharedfileUtils()->setResourceDirectory(38 CCEGLVIEw::sharedOpenGLVIEw()->setDesignResolutionSize(39 }40 turn on display FPS42 pDirector->setdisplayStats(true);44 set FPS. the default value is 1.0/60 if you don't call this45 pDirector->setAnimationInterval(1.0 / 60);46 47 create a scene. it's an autorelease object48 CCScene *pScene = HelloWorld::scene(); run51 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 again11 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-iphonebool init(); there's no 'ID' in cpp,so we recommend returning the class instance pointerstatic cocos2d::CCScene* scene(); a selector callbackvoID menuCloseCallback(CCObject* pSender); touch callbackvoID cctouchesBegan(cocos2d::CCSet *ptouches,cocos2d::CCEvent *pEvent);15 implement the "static node()" method manually17 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 scene10 scene->addChild(layer); return the scenereturn scene;14 } 这个函数的实现完成了3个事情:

1)创建了一个空的CCScene对象scene

2)通过上面分析过的HelloWorld::create函数创建了一个HelloWorld对象layer,并将layer作为scene的一个子节点添加到scene中。

HelloWorld::create函数实际上调用的是CREATE_FUNC(HelloWorld);

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 firstif ( !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 object19 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 object27 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 label36 cclabelTTF* pLabel = cclabelTTF::create(Hello WorldArial24);37 position the label on the center of the screen38 pLabel->setposition(ccp(origin.x + visibleSize.wIDth/39 origin.y + visibleSize.height - pLabel->getContentSize().height));40 add the label as a child to this layerthis->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 screen46 pSprite->setposition(ccp(visibleSize.wIDth/2 + origin.x,visibleSize.height/2 + origin.y)); add the sprite as a child to this layer48 this->addChild(pSprite,128); line-height:1.5!important">49 enable standard touch51 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 #endif8 } 最终,由CCDirector::end函数完成程序的关闭(在IOS中还需要调用exit函数)。

总结

以上是内存溢出为你收集整理的cocos2d-x Helloworld 详解全部内容,希望文章能够帮你解决cocos2d-x Helloworld 详解所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1014679.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-22
下一篇2022-05-22

发表评论

登录后才能评论

评论列表(0条)

    保存