Cocos2d-x 的“HelloWorld” 深入分析(windows上的CCApplication的运作)(下)

重点关注函数: initInstance ()。

[cpp]  view plain copy
  1. #ifndef  _APP_DELEGATE_H_  
  2. #define  _APP_DELEGATE_H_  
  3. #include "CCApplication.h"  
  4. class  AppDelegate : private cocos2d::CCApplication  
  5. {  
  6. public:  
  7. //构造函数  
  8. AppDelegate();  
  9. //析构函数  
  10. virtual ~AppDelegate();  
  11.     //重载初始化函数  
  12.     virtual bool initInstance();  
  13.     //重载应用程序启动后调用的处理函数  
  14.     virtual bool applicationDidFinishLaunching();  
  15.     //重载应用程序转入后台时调用的函数  
  16.     virtual void applicationDidEnterBackground();  
  17.     //重载应用程序恢复前台时调用的函数  
  18.     virtual void applicationWillEnterForeground();  
  19. };  
  20. #endif // _APP_DELEGATE_H_  
再来看cpp文件
[cpp]  view plain copy
  1. #include "AppDelegate.h"  
  2. //加入cocos2d头文件  
  3. #include "cocos2d.h"  
  4. #include "HelloWorldScene.h"  
  5. //加入OpenGL窗口类  
  6. #include "CCEGLView.h"  
  7. //使用cocos2d命名空间  
  8. USING_NS_CC;  
  9. //构造函数  
  10. AppDelegate::AppDelegate() {  
  11. }  
  12. //析构函数  
  13. AppDelegate::~AppDelegate() {  
  14. }  
  15. //初始化函数,解答伏笔1  
  16. bool AppDelegate::initInstance()   
  17. {  
  18. //定义一个bool返回值用于判断是否正常初始化  
  19. bool bRet = false;  
  20. do {  
  21. //通过对宏定义的匹配来判断当前编译的代码的目标平台是什么,在这里可以知道Cocos2d-x的跨平台都支持哪些平台。我们的教程只针对WIN32平台部分做讲解,其它平台大家可以自行参考WIN32平台部分进行学习。  
  22. //第一种平台类型,WIN32系统  
  23. #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)  
  24. // 创建并初始化OpenGL窗口管理实例对象,注意这里使用的是new来动态实例化的。当程序退出时会通过显示设备的release函数进行对窗口的释放。【伏笔3后面会有讲解】。  
  25. CCEGLView * pMainWnd = new CCEGLView();  
  26. //CC_BREAK_IF宏的意思是如果括号中的语句为否则中断循环。配合do_while流程使用。  
  27. //可以看到这里的意思是,如果pMainWnd实例化失败或者pMainWnd创建窗口失败则中断循环。在这里硬性的设定了窗口的标题和窗口的宽度高度。  
  28. CC_BREAK_IF(! pMainWnd  
  29. || ! pMainWnd->Create(TEXT("cocos2d: Hello World"), 480, 320));  
  30. #endif  // CC_PLATFORM_WIN32  
  31.    //第二种平台类型,IOS类型  
  32. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)  
  33. #endif  // CC_PLATFORM_IOS  
  34. //第三种平台类型,ANDORID类型  
  35. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)  
  36. #endif  // CC_PLATFORM_ANDROID  
  37.     //第四种平台,WOPHONE平台  
  38. #if (CC_TARGET_PLATFORM == CC_PLATFORM_WOPHONE)  
  39. #endif  // CC_PLATFORM_WOPHONE  
  40. //第五种平台,MARMALADE  
  41. #if (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE)  
  42. #endif  
  43. //第六种平台,LINUX  
  44. #if (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)  
  45. #endif  // CC_PLATFORM_LINUX  
  46. //第七种平台,BADA  
  47. #if (CC_TARGET_PLATFORM == CC_PLATFORM_BADA)  
  48. #endif  // CC_PLATFORM_BADA  
  49. //第八种平台,QNX  
  50. #if (CC_TARGET_PLATFORM == CC_PLATFORM_QNX)  
  51. #endif // CC_PLATFORM_QNX  
  52. //如果没有被中断,则成功完成初始化。  
  53. bRet = true;  
  54. //退出循环  
  55. while (0);  
  56. return bRet;  
  57. }  
  58.     //重载应用程序启动后调用的处理函数  
  59. bool AppDelegate::applicationDidFinishLaunching() {  
  60. //通过CCDirector的静态函数sharedDirector来获取单件显示设备管理器指针  
  61. CCDirector *pDirector = CCDirector::sharedDirector();  
  62. //通过CCEGLView的静态函数sharedOpenGLView来获取单件管理实例对象并将其地址通过CCDirector的成员函数setOpenGLView传递给显示设备管理器。  
  63. pDirector->setOpenGLView(&CCEGLView::sharedOpenGLView());  
  64. //打开使用高清模式,这里给屏蔽了,如未来有需要可以打开。但是在设计程序时需要考虑到打开后对于位置的影响并提前制定相应的设计方案。  
  65. //pDirector->enableRetinaDisplay(true);  
  66. // 打开显示FPS  
  67. pDirector->setDisplayFPS(true);  
  68. // 设置当前屏幕的摆放方向,这里给屏蔽了。  
  69. // pDirector->setDeviceOrientation(kCCDeviceOrientationLandscapeLeft);  
  70. // 设置FPS的帧间隔时间差为60分之一秒,从而期望FPS为60帧。  
  71. pDirector->setAnimationInterval(1.0 / 60);  
  72. //通过HelloWorld的静态函数scene()创建返回一个场景实例  
  73. CCScene *pScene = HelloWorld::scene();  
  74. //运行这个场景  
  75. pDirector->runWithScene(pScene);  
  76. return true;  
  77. }  
  78. //重载应用程序转入后台时调用的函数,如电话打进来  
  79.  void AppDelegate::applicationDidEnterBackground() {  
  80. //暂停显示设备的渲染处理  
  81. CCDirector::sharedDirector()->pause();  
  82. // 如果使用了声音引擎,这里进行暂停设置。否则会影响到通话,因为暂时没用到,所以屏蔽  
  83. // SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic();  
  84. }  
  85. //重载应用程序恢复前台时调用的函数  
  86. void AppDelegate::applicationWillEnterForeground() {  
  87. //恢复显示设备的渲染处理  
  88. CCDirector::sharedDirector()->resume();  
  89. //如果使用了声音引擎,这里进行恢复设置  
  90. // SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();  
  91. }  


InitInstance函数中,我们看到Cocos2d-xWindows窗口的创建与控制都封装到了CCEGLView类中。它已经暴露出一个Create函数,做为一个求知者,我们应该迫不及待的想要看到它在哪里创建窗口。继续追进去吧!GoGoGo!

我们进入了CCEGLView_win32.h,通过文件名可以知道CCEGLView类应该有多个平台的版本。我们暂不理它。看一下Create函数。

//

[cpp]  view plain copy
  1. bool CCEGLView::Create(LPCTSTR pTitle, int w, int h)  
  2. {  
  3. //定义一个bool型返回值  
  4. bool bRet = false;  
  5. do   
  6. {  
  7. //检测窗口句柄是否已经存在。确保只创建一个窗口  
  8. CC_BREAK_IF(m_hWnd);  
  9. //进入到这里,我们应该很高兴了。没错,终于找到了创建窗口的代码。  
  10. HINSTANCE hInstance = GetModuleHandle( NULL );  
  11. WNDCLASS  wc;// 窗口类  
  12. // 设置相关参数  
  13. wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;    
  14. wc.lpfnWndProc    = _WindowProc;// 本窗口使用的WINDOWS消息处理函数  
  15. wc.cbClsExtra     = 0;                                
  16. wc.cbWndExtra     = 0;  
  17. wc.hInstance      = hInstance;  
  18. wc.hIcon          = LoadIcon( NULL, IDI_WINLOGO );  
  19. wc.hCursor        = LoadCursor( NULL, IDC_ARROW );  
  20. wc.hbrBackground  = NULL;                             
  21. wc.lpszMenuName   = NULL;                             
  22. wc.lpszClassName  = kWindowClassName;                 
  23. //注册窗口类,如果失败则判断失败原因是否是此类别已存在(错误码1410),如果是因为此类存在而导致失败,则仍然可以继续。  
  24. CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError());  
  25. // 取得窗口的窗体矩形  
  26. RECT rcDesktop;  
  27. GetWindowRect(GetDesktopWindow(), &rcDesktop);  
  28. // 调用创建口函数  
  29. m_hWnd = CreateWindowEx(  
  30. WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,  
  31. kWindowClassName,// 之前注册的窗口类  
  32. pTitle,// 窗口标题  
  33. WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,// 窗体样式  
  34. 0, 0,              // 窗体位置  
  35. 0,                                                  // 窗体宽度  
  36. 0,                                                  // 窗体高度  
  37. NULL,// 无父窗口  
  38. NULL,// 无菜单  
  39. hInstance,// 程序句柄  
  40. NULL );  
  41. //判断窗口是否创建成功  
  42. CC_BREAK_IF(! m_hWnd);  
  43.    //取得显示设备的摆放方向  
  44.         m_eInitOrientation = CCDirector::sharedDirector()->getDeviceOrientation();  
  45.    //通过对摆放方向的判断得出水平还是垂直  
  46.         m_bOrientationInitVertical = (CCDeviceOrientationPortrait == m_eInitOrientation  
  47.             || kCCDeviceOrientationPortraitUpsideDown == m_eInitOrientation) ? true : false;  
  48.         m_tSizeInPoints.cx = w;  
  49.         m_tSizeInPoints.cy = h;  
  50.    //设置窗口大小  
  51.         resize(w, h);  
  52. // 使用此窗口进行OpenGL的设置  
  53. m_pEGL = CCEGL::create(this);  
  54. // 如果OpenGL创建失败,销毁窗体并中断  
  55. if (! m_pEGL)  
  56. {  
  57. DestroyWindow(m_hWnd);  
  58. m_hWnd = NULL;  
  59. break;  
  60. }  
  61. //将静态指针设置为当前实例对象。  
  62. s_pMainWindow = this;  
  63. bRet = true;  
  64. while (0);  
  65. return bRet;  
  66. }  


 

       在注册类时,设定了窗口的消息处理回调函数为_WinDowProc,我们继续追入看一下程序退出时在哪里释放了Opengl窗口实例对象。呵呵,还记得前面我们埋下的伏笔3,它是用new创建出来的。而new出来的内存必须释放。程序退出会销毁窗体,消息处理函数会先后收到WM_CLOSE和WM_DESTROY。我们找一下。

[cpp]  view plain copy
  1. static LRESULT CALLBACK _WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
  2. {  
  3. //如果OpenGL窗体初始化完成,则由OpenGL窗体进行消息处理,否则由WINDOWS系统进行默认处理  
  4. if (s_pMainWindow && s_pMainWindow->getHWnd() == hWnd)  
  5. {  
  6. return s_pMainWindow->WindowProc(uMsg, wParam, lParam);  
  7. }  
  8. else  
  9. {  
  10. return DefWindowProc(hWnd, uMsg, wParam, lParam);  
  11. }  
  12. }  


继续。

[cpp]  view plain copy
  1. LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)  
  2. {  
  3. PAINTSTRUCT ps;  
  4. switch (message)  
  5. {…//其它消息暂不理,可自行分析  
  6. case WM_PAINT:  
  7. //这里是刷新屏幕,因为使用了OpenGL来进行绘制,所以这里不作任何处理。  
  8. BeginPaint(m_hWnd, &ps);  
  9. EndPaint(m_hWnd, &ps);  
  10. break;  
  11. case WM_CLOSE:  
  12. //调用单件显示设备管理器的end函数关闭窗口  
  13. CCDirector::sharedDirector()->end();  
  14. break;  
  15. case WM_DESTROY:  
  16. //向消息循环发送WM_QUIT消息退出程序  
  17. PostQuitMessage(0);  
  18. break;  
  19. default:  
  20. return DefWindowProc(m_hWnd, message, wParam, lParam);  
  21. }  
  22. return 0;  
  23. }  


       进入显示设备管理器CCDirectorend函数。

[cpp]  view plain copy
  1. void CCDirector::end()  
  2. {  
  3. m_bPurgeDirecotorInNextLoop = true;  
  4. }  


      倒!这里只是设置成员变量m_bPurgeDirecotorInNextLoop为true,没有什么delete。怎么回事呢?

      好吧,继续分析,成员变量的名字意思是“是否在下一个循环时清除显示设备”。哦。也就是说这只是一个开关。在循环函数中判断它是否为true来清除显示设备。打开mainLoop函数。解答伏笔2

[cpp]  view plain copy
  1. void CCDisplayLinkDirector::mainLoop(void)  
  2. {//如果清除显示设备开关为true  
  3. if (m_bPurgeDirecotorInNextLoop)  
  4. {  
  5. //清楚设备  
  6. purgeDirector();  
  7.           m_bPurgeDirecotorInNextLoop = false;  
  8. }//否则判断显示设备是否有效  
  9. else if (! m_bInvalid)  
  10.  {  
  11. //如果有效,绘制场景  
  12.  drawScene();  
  13.    
  14.  //调用内存管理器释放其管理的内存  
  15.  CCPoolManager::getInstance()->pop();  
  16.  }  
  17. }  


       马上要接晓答案了

[cpp]  view plain copy
  1. void CCDirector::purgeDirector()  
  2. {  
  3. //其它暂不理。有兴趣的朋友请自行分析  
  4. // 调用Opengl窗口管理实例对象的release函数进行释放。  
  5. m_pobOpenGLView->release();  
  6. m_pobOpenGLView = NULL;  
  7. }  


       进入OpenGL窗口管理类的release函数

[cpp]  view plain copy
  1. void CCEGLView::release()  
  2. {//销毁窗体  
  3. if (m_hWnd)  
  4. {  
  5. DestroyWindow(m_hWnd);  
  6. m_hWnd = NULL;  
  7. }  
  8. s_pMainWindow = NULL;  
  9. //注销所使用的窗口类  
  10. UnregisterClass(kWindowClassName, GetModuleHandle(NULL));  
  11.     //释放使用到的指针  
  12.     CC_SAFE_DELETE(m_pSet);  
  13.     CC_SAFE_DELETE(m_pTouch);  
  14.     CC_SAFE_DELETE(m_pDelegate);  
  15. CC_SAFE_DELETE(m_pEGL);  
  16. //关键点:在最后释放了自已在AppDelegate::initInstance()中通过new创建对象而占用的内存。  
  17.     delete this;  
  18. }  


       现在我们了解了Cocos2d-x是如何将WINDOWS程序基本框架封装在自已的类之中的。运行一下程序,可们可以看到弹出的窗口显示出HelloWorld的画面。而当初复杂的WINDOWS程序基本框架。在这里只化为简单的一句run()。非常简洁!

      这个运行中的窗口,没有最大化系统按钮,窗体大小也被固定在480x320。我们希望能够更灵活的设置窗口的标题和大小。那我们必须要做一些改动。即然已经看懂了代码,下面就亲自实习一下吧。

      打开AppDelegateinitInstance函数,增加相应的参数。

[cpp]  view plain copy
  1. bool AppDelegate::initInstance(LPCTSTR szTitle,UINT wWidth,UINT wHeight)  


      并改动下面一句

[cpp]  view plain copy
  1. CC_BREAK_IF(! pMainWnd  
  2. //改成由函数参数来创建OpenGL窗体  
  3. || ! pMainWnd->Create(szTitle, wWidth, wHeight));  
  4. //|| ! pMainWnd->Create(TEXT("cocos2d: Hello World"), 480, 320));  


       然后我们打开CCApplication_win32.cpp,找到run函数。做同样的修改。

[cpp]  view plain copy
  1. int CCApplication::run(LPCTSTR szTitle,UINT wWidth,UINT wHeight)  


并改动下面一句

[cpp]  view plain copy
  1. if (! initInstance(szTitle,wWidth,wHeight) || ! applicationDidFinishLaunching())  
  2. // if (! initInstance() || ! applicationDidFinishLaunching())  


      之后我们将头文件所需要改动的函数声明也一一改动。保存,重新编译libcocos2d工程。再打开HelloWorld中的main.cpp修改这一句:

//我们可以在这里自已设定窗口标题和窗口大小

[cpp]  view plain copy
  1. return cocos2d::CCApplication::sharedApplication().run(TEXT("第一个Cocos2d-x程序"),800,600);  
  2. //return cocos2d::CCApplication::sharedApplication().run();  


      编译HelloWorld。运行一下。享受一下成果!

                                

                                

        对于这个框架我写的较多,因为整个Cocos2d-x的所有例子都是基于这个框架,我们必须理解清楚它是怎么一回事。