9秒分享:Cocos2d-X2.2版本框架源码分析第三讲--完结

OK 最后一课吧..如果说这是一本书的话,这章就相当于附录.. 看了前两章,相信大家已经对基本框架掌握了..所以,这章属于提高篇吧..

附录一

Cocos2d-x 的“HelloWorld” 细节分析

打开 HelloWorld 工程 , 里面有两个文件目录 Classes 和 win32 。 
Classes 下有 HelloWorldScene.h/cpp ,AppDelegate.h/cpp. 
win32 下有 main.h/cpp 
首先看一下 win32 目录下的 main.h, 其中定义了使用 win32 平台进行编译的宏和一些 Windows 编程头文件。
  1. #ifndef __MAIN_H__
  2. #define __MAIN_H__
  3. //定义使用WIN32平台进行编译的宏
  4. #define WIN32_LEAN_AND_MEAN            
  5. // 所用到的Windows编程所用头文件
  6. #include <windows.h>
  7. #include <tchar.h>
  8. // Cocos的C语言头文件
  9. #include "CCStdC.h"
  10. #endif                        // __MAIN_H__
复制代码
再打开 main.cpp.

  1. //加入main.h头文件
  2. #include "main.h"
  3. //加入使用的AppDelegate类头文件
  4. #include "../Classes/AppDelegate.h"
  5. //WinMain主函数
  6. int APIENTRY _tWinMain(HINSTANCE hInstance,
  7.    HINSTANCE hPrevInstance,
  8.    LPTSTR    lpCmdLine,
  9.    int       nCmdShow)
  10. {
  11.    //UNREFERENCED_PARAMETER用于在VC编译器下告知编译器,不必检测改变量是 否使用的警告。
  12.    UNREFERENCED_PARAMETER(hPrevInstance);
  13.    UNREFERENCED_PARAMETER(lpCmdLine);
  14.    // 创建一个Cocos2d-x程序实例
  15. AppDelegate app;
  16.    // 运行程序实例
  17.    return cocos2d::CCApplication::sharedApplication().run();
  18. }
复制代码
代码看着就这么几行,好像非常简单。那是因为 Cocos2d-x 把很多 WINDOWS 窗口程序的代码都封装到了内部。这也使得它更加简单易学。但我们也应该明白这里面是怎么一回事。 
             
咱们转到AppDelegate.h ,可以看到 AppDelegate 类是一个私有继承 Cocos2d 命名空间中的 CCApplication 类。它的成员函数均是重载了 CCApplication 类的成员函数。顾名思义, CCApplication 代表了程序类。我们打开它的声明文件(CCApplication_win32.h)看一下:
  1. #ifndef __CC_APPLICATION_WIN32_H__
  2. #define __CC_APPLICATION_WIN32_H__
  3. //Windows头文件
  4. #include <Windows.h> 
  5. //Cocos2d-x公共头文件,声明了一些公共函数以及语言类型枚举ccLanguageType
  6. #include "CCCommon.h"
  7. //使用cocos2d的命名空间来包括后面的代码
  8. NS_CC_BEGIN;
  9. //声明一下CCRect类,在CCApplication中会用到CCRect类指针
  10. class CCRect;
  11. class CC_DLL CCApplication
  12. {
  13. public:
  14. //构造
  15. CCApplication();
  16.     //析构
  17.     virtual ~CCApplication();
  18.     //初始化
  19.     virtual bool initInstance() = 0;
  20. //程序启动后调用的函数,在这里创建设备与场景
  21.     virtual bool applicationDidFinishLaunching() = 0;
  22. //当程序转入后台,如电话打入时调用
  23.     virtual void applicationDidEnterBackground() = 0;
  24. //当程序转入前台,再次恢复时调用
  25.     virtual void applicationWillEnterForeground() = 0;
  26. //在“设备”设置FPS时调用的函数,设置帧间隔时间
  27. void setAnimationInterval(double interval);
  28. //声明一个枚举,列出当前设备的摆放方向
  29.     typedef enum
  30.     {
  31.         /// 垂直方向, home 键在下面
  32.         kOrientationPortrait = 0,
  33.         /// 垂直方向, home 键在上面
  34.         kOrientationPortraitUpsideDown = 1,
  35.         /// 水平方向,home键在右边
  36.         kOrientationLandscapeLeft = 2,
  37.         /// 水平方向,home健在左边
  38.         kOrientationLandscapeRight = 3,
  39.     } Orientation;
  40.     

  41. //在“设备”改变了摆放方向后调用的函数,设置设备摆放方向
  42.     Orientation setOrientation(Orientation orientation);
  43. //取得窗口的状态栏所在的矩形位置
  44.     void    statusBarFrame(CCRect * rect);
  45.     //运行程序
  46.     int run();
  47. //取得当前的程序实例,这种用法可参考C++模式设计中的“单件”模式
  48.     static CCApplication& sharedApplication();
  49. //取得当前的语言类型
  50.     static ccLanguageType getCurrentLanguage();
  51. protected:
  52. //程序实例句柄
  53. HINSTANCE           m_hInstance;
  54. //加速键句柄
  55. HACCEL              m_hAccelTable;
  56. //声明为帧间隔,实际上是每两帧之间的频率次数
  57.     LARGE_INTEGER       m_nAnimationInterval;
  58. //单件的程序实例指针
  59.     static CCApplication * sm_pSharedApplication;
  60. };
  61. NS_CC_END;
  62. #endif// __CC_APPLICATION_WIN32_H__
复制代码
通过对于 CCApplication_win32.h 的代码分析,我们可以清楚 CCApplication 的功能是对于程序的控制。 
我们转到 CCApplication 类的 cpp 文件 CCApplication_win32.cpp 再来分析一下。 
读前小提示 :  
CCDirector 代表显示设备管理器。也是一个单件类。通过其静态函数 CCDirector::sharedDirector() 来访问唯一的显示设备。 
         
重点关注函数:CCApplication(), run() 。 (在CCApplication_win32.cpp)
  1. #include "CCApplication.h"
  2. //设备头文件
  3. #include "CCDirector.h"
  4. //在注册表中写入对于PVRFrame的显示和隐藏的设置
  5. static void PVRFrameEnableControlWindow(bool bEnable);
  6. //使用cocos2d的命名空间来包括后面的代码
  7. NS_CC_BEGIN;
  8. // CCApplication的静态成员指针变量, 单件对象指针
  9. CCApplication * CCApplication::sm_pSharedApplication = 0;
  10. //构造函数
  11. CCApplication::CCApplication()
  12. : m_hInstance(NULL)
  13. , m_hAccelTable(NULL)
  14. {
  15. //获取当前程序句柄
  16. m_hInstance= GetModuleHandle(NULL);
  17. //初始化
  18. m_nAnimationIntervalm_nAnimationInterval.QuadPart = 0;
  19. //断言程序中只有一个sm_pSharedApplication。确保当前类只有一个实例对象
  20. CC_ASSERT(! sm_pSharedApplication);
  21. //设置单件对象指针指向当前类对象实例    
  22. sm_pSharedApplication = this;
  23. }
  24. //析构
  25. CCApplication::~CCApplication()
  26. {
  27. //断言程序只有一个sm_pSharedApplication就是指向当前类的实例对象    
  28. CC_ASSERT(this == sm_pSharedApplication);    
  29. sm_pSharedApplication = NULL;
  30. }
  31. //程序运行
  32. int CCApplication::run()
  33. {    
  34. //设置注册表PVRFrame隐藏    
  35. PVRFrameEnableControlWindow(false);    
  36. //主消息循环    
  37. MSG msg;    
  38. LARGE_INTEGER nFreq;    
  39. LARGE_INTEGER nLast;    
  40. LARGE_INTEGER nNow;
  41. //WINDOWS高精度定时器的用法,先获取频率
  42. QueryPerformanceFrequency(&nFreq);
  43. //获取当前的计数值,即频率x当前时间    
  44. QueryPerformanceCounter(&nLast);
  45. //initInstance函数为虚函数,由派生类AppDelegate进行了重载。此段代码在调用
  46.     AppDelegate重载的initInstance函数之后调用applicationDidFinishLaunching函数完成一
  47.     些初始化处理。
  48.     //注:AppDelegate重载initInstance函数做了什么我们暂且只先认为它如平时我们
  49.     WINDOWS基本框架程序一样创建了一个Windows窗口。【伏笔1后面会有讲解】。    
  50. if (! initInstance() || ! applicationDidFinishLaunching())    
  51. {        
  52. return 0;    
  53. }
  54. //取得当前使用的OPENGL窗口管理实例对象
  55. CCEGLView& mainWnd = CCEGLView::sharedOpenGLView();
  56. //将窗口居中显示    
  57. mainWnd.centerWindow();    
  58. ShowWindow(mainWnd.getHWnd(), SW_SHOW);
  59. //非常熟悉!进入WINDOWS消息循环    
  60. while (1)
  61. {   
  62. //如果没有获取到WINDOWS消息        
  63. if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))        
  64. {            
  65. // 取得当前的计数值,即频率x当前时间            
  66. QueryPerformanceCounter(&nNow);            
  67.             //m_nAnimationInterval.QuadPart的值 为setAnimationInterval函数进行设置的     
  68.             固定值。此处是为了判断时间流逝了多久,是否应该更新显示设备            
  69. if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)            
  70. {  
  71. //如果时间流逝达到了设定的FPS时间差,则更新计数值。                
  72. nLast.QuadPart = nNow.QuadPart;  
  73. //这里是设备渲染场景的函数,【伏笔2后面会有讲解】                 
  74. CCDirector::sharedDirector()->mainLoop();            
  75. }            
  76. else           
  77. {  
  78.             //sleep0秒的意义是让CPU做下时间片切换,防止死循环而使系统其它程序得
  79.             不到响应。                
  80. Sleep(0);            
  81. }            
  82. continue;        
  83. }   
  84. //有消息获取到        
  85. if (WM_QUIT == msg.message)        
  86. {   
  87. // 如果获取的消息是退出则退出循环。            
  88. break;        
  89. }        
  90. // 如果没有定义加速键或者处理完加速键信息        
  91. if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))        
  92. {   
  93. //处理Windows消息            
  94. TranslateMessage(&msg);            
  95. DispatchMessage(&msg);        
  96. }    
  97. }    
  98. return (int) msg.wParam;

  99. //外部调用的设置帧间隔时间
  100. void CCApplication::setAnimationInterval(double interval){    
  101. //获取高精度定时器的频率    
  102. LARGE_INTEGER nFreq;
  103. QueryPerformanceFrequency(&nFreq);
  104. //计算出频率X帧间隔时差的值存入m_nAnimationInterval用做比较值    
  105. m_nAnimationInterval.QuadPart = (LONGLONG)(interval * nFreq.QuadPart);
  106. }
  107. //摆放方向变化时外部自动调用的设置摆放方向
  108. CCApplication::Orientation CCApplication::setOrientation(Orientation orientation){    
  109. //切换OPENGL视窗的宽高    
  110. CCEGLView * pView = CCDirector::sharedDirector()->getOpenGLView();    
  111. if (pView)    {        
  112. return (Orientation)pView->setDeviceOrientation(orientation);    
  113. }    
  114. return (Orientation)CCDirector::sharedDirector()->getDeviceOrientation();
  115. }
  116. //获取状态栏的位置矩形
  117. void CCApplication::statusBarFrame(CCRect * rect){    
  118. if (rect)    {        
  119. // WINDOWS系统没有状态栏,所以返回的矩形各位置都是0        
  120. *rect = CCRectMake(0, 0, 0, 0);    
  121. }
  122. }
  123. // 静态成员函数,获取单件指针
  124. CCApplication& CCApplication::sharedApplication(){    
  125.     CC_ASSERT(sm_pSharedApplication);    
  126.     return *sm_pSharedApplication;
  127. }
  128. //静态成员函数,获取当前系统的语言类型
  129. ccLanguageType CCApplication::getCurrentLanguage(){
  130. //默认为英语    
  131. ccLanguageType ret = kLanguageEnglish;
  132. //
  133. LCID localeID = GetUserDefaultLCID();
  134. unsigned short primaryLanguageID = localeID & 0xFF;
  135. switch (primaryLanguageID){
  136. case LANG_CHINESE:
  137. //中文
  138. ret = kLanguageChinese;
  139. break;
  140. case LANG_FRENCH:
  141. //法文
  142. ret = kLanguageFrench;
  143. break;
  144. case LANG_ITALIAN:
  145. //意文
  146. ret = kLanguageItalian;
  147. break;
  148. case LANG_GERMAN:
  149. //德文
  150. ret = kLanguageGerman;
  151. break;
  152. case LANG_SPANISH:
  153. //西班牙文
  154. ret = kLanguageSpanish;
  155. break;
  156. case LANG_RUSSIAN:
  157. //俄文
  158. ret = kLanguageRussian;
  159. break;
  160. }    
  161. return ret;
  162. }
  163. NS_CC_END;
  164. //在注册表中写入对于PVRFrame的显示和隐藏的设置
  165. static void PVRFrameEnableControlWindow(bool bEnable){    
  166. HKEY hKey = 0;    
  167. // 打开注册表的 PVRFrame 项    
  168.     if(ERROR_SUCCESS != RegCreateKeyExW(HKEY_CURRENT_USER,        
  169. L"Software\\Imagination Technologies\\PVRVFRame\\STARTUP\\",        
  170. 0,        
  171. 0,        
  172. REG_OPTION_NON_VOLATILE,        
  173. KEY_ALL_ACCESS,        
  174. 0,        
  175. &hKey,        
  176. NULL))    
  177. {        
  178. return;    
  179. }   
  180. const wchar_t * wszValue = L"hide_gui";    
  181. const wchar_t * wszNewData = (bEnable) ? L"NO" : L"YES";    
  182. wchar_t wszOldData[256] = {0};
  183. DWORD  dwSize = sizeof(wszOldData);
  184. //读取相应的键值
  185. LSTATUS status = RegQueryValueExW(hKey, wszValue, 0, NULL, (LPBYTE)wszOldData,   
  186. &dwSize);
  187. //如果键值不存在,或者键值存在但与当前值不同,重设键值    
  188. if (ERROR_FILE_NOT_FOUND == status              
  189.       || (ERROR_SUCCESS == status                 
  190.       && 0 != wcscmp(wszNewData, wszOldData)))    
  191. {        
  192. dwSize = sizeof(wchar_t) * (wcslen(wszNewData) + 1);        
  193. RegSetValueEx(hKey, wszValue, 0, REG_SZ, (const BYTE *)wszNewData, dwSize);    
  194. }
  195. //关闭注册表    
  196. RegCloseKey(hKey);
  197. }
复制代码
代码看完之后,我们来做一下 CCApplication 类的总结: 
在 CCApplication 的构造函数中可以看到这里定义了一个静态指针 sm_pShareApplication; 它在 CCApplication 实例化时指定为实例化单件对象的指针。在 sharedApplication 函数中返回实例化的单件对象引用。重点函数是 run 函数。在 run 函数开始调用了 initInstance 函数进行程序的初始化 , 调用返回为 true 后再调用 applicationDidFinishLaunching 函数完成一些逻辑的初始化工作 , 完成初始化后会进入 WINDOWS 消息循环,并通过高精度定时器进行 FPS 判断什么时候调用 CCDirector::sharedDirector()->mainLoop()。直待收到窗口关闭的消息退出消息循环并返回。 
好了,我们理解了 CCApplication 之后,我们再来看一下它的派生类 AppDelegate ,了解一下它都重载了哪些函数。 
读前小提示 :  CCEGLView 代表 OpenGL 显示窗口。封装了使用 OpengGL 做为显示底层 API 的一个基本的 WINDOWS 窗体的创建与控制。 
读前小提示 :  CCDirector 类中有一个函数 enableRetianDisplay 函数。这个函数主要是影响到 IOS 平台上支持高清显示的设备如 iphone4 , iphone4s 等。 如果设置enableRetinaDisplay(false), 则在iphone4平台上运行的结果是游戏的图片分辨率降低为原来的一半,因为宽高都被拉伸了一倍。如果设置enableRetinaDisplay(true), 则在iphone4平台上运行的结果是游戏的图片分辨率正常,但是放置的位置的水平方向和垂直方向都拉伸了两倍。要记住在cocos2d里面设置精灵坐标时,使用的是点,而不是像素,在普通的iphone上,一个点就等于一个像素,而在高清显示设备中,默认一个点等于二个像素。在IOS SDK 4.0及以后的SDK中支持不同的分辨率。并提供了相应的函数对逻辑点与像素的对应比例值做设置。 
重点关注函数: initInstance ()。 (在AppDelegate.h)
  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_
  21. 再来看AppDelegate.cpp文件
  22. #include "AppDelegate.h"
  23. //加入cocos2d头文件
  24. #include "cocos2d.h"
  25. #include "HelloWorldScene.h"
  26. //加入OpenGL窗口类
  27. #include "CCEGLView.h"
  28. //使用cocos2d命名空间
  29. USING_NS_CC;
  30. //构造函数
  31. AppDelegate::AppDelegate() {
  32. }
  33. //析构函数
  34. AppDelegate::~AppDelegate() {
  35. }
  36. //初始化函数,解答伏笔1
  37. bool AppDelegate::initInstance() 
  38. {
  39. //定义一个bool返回值用于判断是否正常初始化
  40. bool bRet = false;
  41. do {
  42. //通过对宏定义的匹配来判断当前编译的代码的目标平台是什么,在这里可以知道Cocos2d-x的跨平台都支持哪些平台。我们的教程只针对WIN32平台部分做讲解,其它平台大家可以自行参考WIN32平台部分进行学习。
  43. //第一种平台类型,WIN32系统
  44. #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
  45. // 创建并初始化OpenGL窗口管理实例对象,注意这里使用的是new来动态实例化的。 
  46. 当程序退出时会通过显示设备的release函数进行对窗口的释放。【伏笔3后面会有讲解】。
  47. CCEGLView * pMainWnd = new CCEGLView();
  48.     //CC_BREAK_IF宏的意思是如果括号中的语句为否则中断循环。配合do_while流程使
  49. 用。
  50. //可以看到这里的意思是,如果pMainWnd实例化失败或者pMainWnd创建窗口失
  51.     败则中断循环。在这里硬性的设定了窗口的标题和窗口的宽度高度。
  52. CC_BREAK_IF(!pMainWnd || !pMainWnd->Create(TEXT("cocos2d: Hello World"), 480, 
  53. 320));
  54. #endif  // CC_PLATFORM_WIN32   
  55. //第二种平台类型,IOS类型
  56. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
  57. #endif  // CC_PLATFORM_IOS
  58. //第三种平台类型,ANDORID类型
  59. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
  60. #endif  // CC_PLATFORM_ANDROID    
  61. //第四种平台,WOPHONE平台
  62. #if (CC_TARGET_PLATFORM == CC_PLATFORM_WOPHONE)
  63. #endif  // CC_PLATFORM_WOPHONE
  64. //第五种平台,MARMALADE
  65. #if (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE)
  66. #endif
  67. //第六种平台,LINUX
  68. #if (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
  69. #endif  // CC_PLATFORM_LINUX
  70. //第七种平台,BADA
  71. #if (CC_TARGET_PLATFORM == CC_PLATFORM_BADA)
  72. #endif  // CC_PLATFORM_BADA
  73. //第八种平台,QNX
  74. #if (CC_TARGET_PLATFORM == CC_PLATFORM_QNX)
  75. #endif // CC_PLATFORM_QNX
  76. //如果没有被中断,则成功完成初始化。
  77. bRet = true;
  78. //退出循环
  79. } while (0);
  80. return bRet;
  81. }    
  82. //重载应用程序启动后调用的处理函数
  83. bool AppDelegate::applicationDidFinishLaunching() {
  84. //通过CCDirector的静态函数sharedDirector来获取单件显示设备管理器指针
  85. CCDirector *pDirector = CCDirector::sharedDirector();
  86.     //通过CCEGLView的静态函数sharedOpenGLView来获取单件管理实例对象并将其地址
  87.     通过CCDirector的成员函数setOpenGLView传递给显示设备管理器。
  88. pDirector->setOpenGLView(&CCEGLView::sharedOpenGLView());
  89.     //打开使用高清模式,这里给屏蔽了,如未来有需要可以打开。但是在设计程序时需要
  90.     考虑到打开后对于位置的影响并提前制定相应的设计方案。
  91. //pDirector->enableRetinaDisplay(true);
  92. // 打开显示
  93. FPSpDirector->setDisplayFPS(true);
  94. // 设置当前屏幕的摆放方向,这里给屏蔽了。
  95. // pDirector->setDeviceOrientation(kCCDeviceOrientationLandscapeLeft);
  96. // 设置FPS的帧间隔时间差为60分之一秒,从而期望FPS为60帧。
  97. pDirector->setAnimationInterval(1.0 / 60);
  98. //通过HelloWorld的静态函数scene()创建返回一个场景实例
  99. CCScene *pScene = HelloWorld::scene();
  100. //运行这个场景
  101. pDirector->runWithScene(pScene);
  102. return true;
  103. }
  104. //重载应用程序转入后台时调用的函数,如电话打进来 
  105. void AppDelegate::applicationDidEnterBackground() {
  106. //暂停显示设备的渲染处理
  107. CCDirector::sharedDirector()->pause();
  108. // 如果使用了声音引擎,这里进行暂停设置。否则会影响到通话,因为暂时没用到,所以屏蔽
  109. // SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic();
  110. }
  111. //重载应用程序恢复前台时调用的函数
  112. void AppDelegate::applicationWillEnterForeground() {
  113. //恢复显示设备的渲染处理
  114. CCDirector::sharedDirector()->resume();
  115. //如果使用了声音引擎,这里进行恢复设置
  116. // SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic();
  117. }


  118. 在 InitInstance 函数中,我们看到 Cocos2d-x 将 Windows 窗口的创建与控制都封装到了 CCEGLView 类中。它已经暴露出一个 Create 函数,做为一个求知者,我们应该迫不及待的想要看到它在哪里创建窗口。继续追进去吧! GoGoGo! 
  119. 我们进入了 CCEGLView_win32.cpp ,通过文件名可以知道 CCEGLView 类应该有多个平台的版本。我们暂不理它。看一下 Create 函数。 (在CCEGLView_win32.cpp )
  120. //我们先从bool CCEGLView::Create(LPCTSTR pTitle, int w, int h)开始
  121. bool CCEGLView::Create(LPCTSTR pTitle, int w, int h){
  122. //定义一个bool型返回值
  123. bool bRet = false;
  124. do 
  125. {
  126. //检测窗口句柄是否已经存在。确保只创建一个窗口
  127. CC_BREAK_IF(m_hWnd);
  128. //进入到这里,我们应该很高兴了。没错,终于找到了创建窗口的代码。
  129. HINSTANCE hInstance = GetModuleHandle( NULL );
  130. WNDCLASS  wc;
  131. // 窗口类
  132. // 设置相关参数
  133. wc.style           = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;  
  134. wc.lpfnWndProc    = _WindowProc;
  135. // 本窗口使用的WINDOWS消息处理函数
  136. wc.cbClsExtra      = 0;                              
  137. wc.cbWndExtra     = 0;
  138. wc.hInstance       = hInstance;
  139. wc.hIcon          = LoadIcon( NULL, IDI_WINLOGO );
  140. wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
  141. wc.hbrBackground  = NULL;                           
  142. wc.lpszMenuName  = NULL;                           
  143. wc.lpszClassName  = kWindowClassName;               
  144.         //注册窗口类,如果失败,则判断失败原因是否是此类别已存在(错误码1410),如果
  145.         是,因为此类存在而导致失败,则仍然可以继续。
  146. CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError());
  147. // 取得窗口的窗体矩形
  148. RECT rcDesktop;
  149. GetWindowRect(GetDesktopWindow(), &rcDesktop);
  150. // 调用创建口函数
  151. m_hWnd = CreateWindowEx(
  152. WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,kWindowClassName,
  153. // 之前注册的窗口类
  154. pTitle,
  155. // 窗口标题
  156. WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,
  157. // 窗体样式
  158. 0, 0,              
  159. // 窗体位置
  160. 0,                                                  
  161. // 窗体宽度
  162. 0,                                                  
  163. // 窗体高度
  164. NULL,
  165. // 无父窗口
  166. NULL,
  167. // 无菜单
  168. hInstance,
  169. // 程序句柄
  170. NULL );
  171. //判断窗口是否创建成功
  172. CC_BREAK_IF(! m_hWnd);   
  173. //取得显示设备的摆放方向        
  174. m_eInitOrientation = CCDirector::sharedDirector()->getDeviceOrientation();   
  175. //通过对摆放方向的判断得出水平还是垂直        
  176. m_bOrientationInitVertical = (CCDeviceOrientationPortrait == m_eInitOrientation
  177.            || kCCDeviceOrientationPortraitUpsideDown == m_eInitOrientation) ? true : false;        
  178. m_tSizeInPoints.cx = w;        
  179. m_tSizeInPoints.cy = h;   
  180. //设置窗口大小        
  181. resize(w, h);
  182. // 使用此窗口进行OpenGL的设置
  183. m_pEGL = CCEGL::create(this);
  184. // 如果OpenGL创建失败,销毁窗体并中断
  185. if (! m_pEGL){
  186. DestroyWindow(m_hWnd);
  187. m_hWnd = NULL;
  188. break;
  189. }
  190. //将静态指针设置为当前实例对象。
  191. s_pMainWindow = this;
  192. bRet = true;
  193. } while (0);
  194. return bRet;
  195. }
复制代码
在注册类时,设定了窗口的消息处理回调函数为_WinDowProc,我们继续追入看一下程序退出时在哪里释放了Opengl窗口实例对象。呵呵,还记得前面我们埋下的伏笔3,它是用new创建出来的。而new出来的内存必须释放。程序退出会销毁窗体,消息处理函数会先后收到WM_CLOSE和WM_DESTROY。我们找一下。(还是CCEGLView_win32.cpp,在bool CCEGLView::Create()函数上面)

  1. static LRESULT CALLBACK _WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, 
  2.   LPARAM lParam){
  3. //如果OpenGL窗体初始化完成,则由OpenGL窗体进行消息处理,否则由WINDOWS
  4. 系统进行默认处理
  5.     if (s_pMainWindow && s_pMainWindow->getHWnd() == hWnd){
  6.         return s_pMainWindow->WindowProc(uMsg, wParam, lParam);
  7.     }else{
  8.         return DefWindowProc(hWnd, uMsg, wParam, lParam);
  9.     }
  10. }
复制代码
继续。(在CCEGLView_win32.cpp,在bool CCEGLView::Create()函数下面)
  1. LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam){
  2. PAINTSTRUCT ps;
  3. switch (message){

  4. //以上消息暂不理,可自行分析
  5. case WM_PAINT:
  6. //这里是刷新屏幕,因为使用了OpenGL来进行绘制,所以这里不作任何处理。
  7. BeginPaint(m_hWnd, &ps);
  8. EndPaint(m_hWnd, &ps);
  9. break;
  10. case WM_CLOSE:
  11. //调用单件显示设备管理器的end函数关闭窗口
  12. CCDirector::sharedDirector()->end();
  13. break;
  14. case WM_DESTROY:
  15. //向消息循环发送WM_QUIT消息退出程序
  16. PostQuitMessage(0);
  17. break;
  18.     default:
  19. return DefWindowProc(m_hWnd, message, wParam, lParam);
  20. }return 0;
  21. }
复制代码
进入显示设备管理器 CCDirector 的 end 函数。(在CCDirector.cpp)
  1. void CCDirector::end(){
  2.     //是否在下一个循环时清除显示设备
  3.     m_bPurgeDirecotorInNextLoop = true;
  4. }
复制代码
倒 ! 这里只是设置成员变量 m_bPurgeDirecotorInNextLoop为 true, 没有什么 delete 。怎么回事呢? 
好吧,继续分析,成员变量的名字意思是“是否在下一个循环时清除显示设备”。哦。也就是说这只是一个开关。在循环函数中判断它是否为 true 来清除显示设备。打开 mainLoop 函数。解答 伏笔 2 (在CCDirector.cpp)
  1. void CCDisplayLinkDirector::mainLoop(void){
  2. //如果清除显示设备开关为true
  3. if (m_bPurgeDirecotorInNextLoop){
  4. //清除设备
  5. purgeDirector();          
  6. m_bPurgeDirecotorInNextLoop = false;
  7. }//否则判断显示设备是否有效
  8. else if (! m_bInvalid) {
  9. //如果有效,绘制场景 
  10. drawScene(); 
  11. //调用内存管理器释放其管理的内存 
  12. CCPoolManager::getInstance()->pop(); 
  13. }
  14. }
复制代码
马上要接晓答案了(在CCDirector.cpp)
  1. void CCDirector::purgeDirector(){

  2. //以上暂不理。有兴趣的朋友请自行分析
  3. // 调用Opengl窗口管理实例对象的release函数进行释放。
  4. m_pobOpenGLView->release();
  5. m_pobOpenGLView = NULL;
  6. }
复制代码
进入 OpenGL 窗口管理类的 release 函数 (在CCEGLView_win32.cpp)
  1. void CCEGLView::release(){
  2. //销毁窗体
  3. if (m_hWnd){
  4. DestroyWindow(m_hWnd);
  5. m_hWnd = NULL;
  6. }
  7. s_pMainWindow = NULL;
  8. //注销所使用的窗口类
  9. UnregisterClass(kWindowClassName, GetModuleHandle(NULL));    
  10. //释放使用到的指针    
  11. CC_SAFE_DELETE(m_pSet);    
  12. CC_SAFE_DELETE(m_pTouch);    
  13. CC_SAFE_DELETE(m_pDelegate);
  14. CC_SAFE_DELETE(m_pEGL);
  15. //关键点:在最后释放了自已在AppDelegate::initInstance()中通过new创建对象而占用
  16. 的内存。    
  17. delete this;
  18. }
复制代码
现在我们了解了 Cocos2d-x 是如何将 WINDOWS 程序基本框架封装在自已的类之中的。运行一下程序,可们可以看到弹出的窗口显示出 HelloWorld 的画面。而当初复杂的 WINDOWS 程序基本框架。在这里只化为简单的一句 run() 。非常简洁! 
这个运行中的窗口,没有最大化系统按钮,窗体大小也被固定在 480x320 。我们希望能够更灵活的设置窗口的标题和大小。那我们必须要做一些改动。即然已经看懂了代码,下面就亲自实习一下吧。 
打开 AppDelegate 的 initInstance 函数,增加相应的参数。 (AppDelegate.cpp中的bool AppDelegate::initInstance()函数)

  1. bool AppDelegate::initInstance(LPCTSTR szTitle,UINT wWidth,UINT wHeight)

  2. 并改动下面一句
  3. CC_BREAK_IF(! pMainWnd
  4.   //改成由函数参数来创建OpenGL窗体
  5.   || ! pMainWnd->Create(szTitle, wWidth, wHeight));
  6. //|| ! pMainWnd->Create(TEXT("cocos2d: Hello World"), 480, 320));
复制代码
然后我们打开 CCApplication_win32.cpp ,找到 run 函数。做同样的修改。
  1. int CCApplication::run(LPCTSTR szTitle,UINT wWidth,UINT wHeight)

  2. 并改动下面一句
  3. if (! initInstance(szTitle,wWidth,wHeight) || ! applicationDidFinishLaunching())
  4. // if (! initInstance() || ! applicationDidFinishLaunching())
复制代码
之后我们将头文件所需要改动的函数声明也一一改动。保存,重新编译 libcocos2d 工程。再打开 HelloWorld 中的 main.cpp 修改这一句:
  1. // 我们可以在这里自已设定窗口标题和窗口大小 
  2. return cocos2d::CCApplication::sharedApplication().run(TEXT("卧槽,终于写完了,大鸡蛋请吃饭,快点...By:Net Fly"),800,600);
  3. //return cocos2d::CCApplication::sharedApplication().run();
复制代码
编译 HelloWorld 。运行一下。享受一下成果! 


好了,到这里,俺们就分析完了,其他内容大家慢慢分析吧...个人精力有限...欢迎补充....

by:Net Fly
                                   

你可能感兴趣的:(9秒分享:Cocos2d-X2.2版本框架源码分析第三讲--完结)