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则保存了当前对象(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)注册窗口类,并注册消息处理函数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中。
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 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函数)。
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|