本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!
Cocos2d-x 的“HelloWorld” 深入分析
不能免俗,一切都从“HelloWorld!”开始.打开HelloWorld工程,里面有两个文件目录Classes和win32。
Classes下有HelloWorldScene.h/cpp,AppDelegate.h/cpp.
win32下有main.h/cpp
首先看一下win32目录下的main.h,其中定义了使用win32平台进行编译的宏和一些Windows编程头文件。
#ifndef __MAIN_H__ #define __MAIN_H__ //定义使用WIN32平台进行编译的宏 #define WIN32_LEAN_AND_MEAN // 所用到的Windows编程所用头文件 #include <windows.h> #include <tchar.h> // Cocos的C语言头文件 #include "CCStdC.h" #endif// __MAIN_H__
再打开main.cpp.
//加入main.h头文件 #include "main.h" //加入使用的AppDelegate类头文件 #include "../Classes/AppDelegate.h" //WinMain主函数 int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { //UNREFERENCED_PARAMETER用于在VC编译器下告知编译器,不必检测改变量是否使用的警告。 UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // 创建一个Cocos2d-x程序实例 AppDelegate app; // 运行程序实例 return cocos2d::CCApplication::sharedApplication().run(); }
代码看着就这么几行,好像非常简单。那是因为Cocos2d-x把很多WINDOWS窗口程序的代码都封装到了内部。这也使得它更加简单易学。但我们也应该明白这里面是怎么一回事。
咱们转到AppDelegate.h,可以看到AppDelegate类是一个私有继承Cocos2d命名空间中的CCApplication类。它的成员函数均是重载了CCApplication类的成员函数。顾名思义,CCApplication代表了程序类。我们打开它的声明文件看一下:
#ifndef __CC_APPLICATION_WIN32_H__ #define __CC_APPLICATION_WIN32_H__ //Windows头文件 #include <Windows.h> //Cocos2d-x公共头文件,声明了一些公共函数以及语言类型枚举ccLanguageType #include "CCCommon.h" //使用cocos2d的命名空间来包括后面的代码 NS_CC_BEGIN; //声明一下CCRect类,在CCApplication中会用到CCRect类指针 class CCRect; class CC_DLL CCApplication { public: //构造 CCApplication(); //析构 virtual ~CCApplication(); //初始化 virtual bool initInstance() = 0; //程序启动后调用的函数,在这里创建设备与场景 virtual bool applicationDidFinishLaunching() = 0; //当程序转入后台,如电话打入时调用 virtual void applicationDidEnterBackground() = 0; //当程序转入前台,再次恢复时调用 virtual void applicationWillEnterForeground() = 0; //在“设备”设置FPS时调用的函数,设置帧间隔时间 void setAnimationInterval(double interval); //声明一个枚举,列出当前设备的摆放方向 typedef enum { /// 垂直方向, home 键在下面 kOrientationPortrait = 0, /// 垂直方向, home 键在上面 kOrientationPortraitUpsideDown = 1, /// 水平方向,home键在右边 kOrientationLandscapeLeft = 2, /// 水平方向,home健在左边 kOrientationLandscapeRight = 3, } Orientation; //在“设备”改变了摆放方向后调用的函数,设置设备摆放方向 Orientation setOrientation(Orientation orientation); //取得窗口的状态栏所在的矩形位置 void statusBarFrame(CCRect * rect); //运行程序 int run(); //取得当前的程序实例,这种用法可参考C++模式设计中的“单件”模式 static CCApplication& sharedApplication(); //取得当前的语言类型 static ccLanguageType getCurrentLanguage(); protected: //程序实例句柄 HINSTANCE m_hInstance; //加速键句柄 HACCEL m_hAccelTable; //声明为帧间隔,实际上是每两帧之间的频率次数 LARGE_INTEGER m_nAnimationInterval; //单件的程序实例指针 static CCApplication * sm_pSharedApplication; }; NS_CC_END; #endif// __CC_APPLICATION_WIN32_H__
通过对于CCApplication_win32.h的代码分析,我们可以清楚CCApplication的功能是对于程序的控制。
我们转到CCApplication类的cpp文件CCApplication_win32.cpp再来分析一下。
读前小提示:CCDirector代表显示设备管理器。也是一个单件类。通过其静态函数CCDirector::sharedDirector()来访问唯一的显示设备。
重点关注函数:CCApplication(),run()。
#include "CCApplication.h" //设备头文件 #include "CCDirector.h" //在注册表中写入对于PVRFrame的显示和隐藏的设置 static void PVRFrameEnableControlWindow(bool bEnable); //使用cocos2d的命名空间来包括后面的代码 NS_CC_BEGIN; // CCApplication的静态成员指针变量, 单件对象指针 CCApplication * CCApplication::sm_pSharedApplication = 0; //构造函数 CCApplication::CCApplication() : m_hInstance(NULL) , m_hAccelTable(NULL) { //获取当前程序句柄 m_hInstance= GetModuleHandle(NULL); //初始化m_nAnimationInterval m_nAnimationInterval.QuadPart = 0; //断言程序中只有一个sm_pSharedApplication。确保当前类只有一个实例对象 CC_ASSERT(! sm_pSharedApplication); //设置单件对象指针指向当前类对象实例 sm_pSharedApplication = this; } //析构 CCApplication::~CCApplication() { //断言程序只有一个sm_pSharedApplication就是指向当前类的实例对象 CC_ASSERT(this == sm_pSharedApplication); sm_pSharedApplication = NULL; } //程序运行 int CCApplication::run() { //设置注册表PVRFrame隐藏 PVRFrameEnableControlWindow(false); //主消息循环 MSG msg; LARGE_INTEGER nFreq; LARGE_INTEGER nLast; LARGE_INTEGER nNow; //WINDOWS高精度定时器的用法,先获取频率 QueryPerformanceFrequency(&nFreq); //获取当前的计数值,即频率x当前时间 QueryPerformanceCounter(&nLast); //initInstance函数为虚函数,由派生类AppDelegate进行了重载。此段代码在调用AppDelegate重载的initInstance函数之后调用applicationDidFinishLaunching函数完成一些初始化处理。 //注:AppDelegate重载initInstance函数做了什么我们暂且只先认为它如平时我们WINDOWS基本框架程序一样创建了一个Windows窗口。【伏笔1后面会有讲解】。 if (! initInstance() || ! applicationDidFinishLaunching()) { return 0; } //取得当前使用的OPENGL窗口管理实例对象 CCEGLView& mainWnd = CCEGLView::sharedOpenGLView(); //将窗口居中显示 mainWnd.centerWindow(); ShowWindow(mainWnd.getHWnd(), SW_SHOW); //非常熟悉!进入WINDOWS消息循环 while (1) { //如果没有获取到WINDOWS消息 if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // 取得当前的计数值,即频率x当前时间 QueryPerformanceCounter(&nNow); //m_nAnimationInterval.QuadPart的值 为setAnimationInterval函数进行设置的固定值。此处是为了判断时间流逝了多久,是否应该更新显示设备 if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart) { //如果时间流逝达到了设定的FPS时间差,则更新计数值。 nLast.QuadPart = nNow.QuadPart; //这里是设备渲染场景的函数,【伏笔2后面会有讲解】 CCDirector::sharedDirector()->mainLoop(); } else { //sleep0秒的意义是让CPU做下时间片切换,防止死循环而使系统其它程序得不到响应。 Sleep(0); } continue; } //有消息获取到 if (WM_QUIT == msg.message) { // 如果获取的消息是退出则退出循环。 break; } // 如果没有定义加速键或者处理完加速键信息 if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg)) { //处理Windows消息 TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } //外部调用的设置帧间隔时间 void CCApplication::setAnimationInterval(double interval) { //获取高精度定时器的频率 LARGE_INTEGER nFreq; QueryPerformanceFrequency(&nFreq); //计算出频率X帧间隔时差的值存入m_nAnimationInterval用做比较值 m_nAnimationInterval.QuadPart = (LONGLONG)(interval * nFreq.QuadPart); } //摆放方向变化时外部自动调用的设置摆放方向 CCApplication::Orientation CCApplication::setOrientation(Orientation orientation) { //切换OPENGL视窗的宽高 CCEGLView * pView = CCDirector::sharedDirector()->getOpenGLView(); if (pView) { return (Orientation)pView->setDeviceOrientation(orientation); } return (Orientation)CCDirector::sharedDirector()->getDeviceOrientation(); } //获取状态栏的位置矩形 void CCApplication::statusBarFrame(CCRect * rect) { if (rect) { // WINDOWS系统没有状态栏,所以返回的矩形各位置都是0 *rect = CCRectMake(0, 0, 0, 0); } } ////////////////////////////////////////////////////////////////////////// // 静态成员函数,获取单件指针 ////////////////////////////////////////////////////////////////////////// CCApplication& CCApplication::sharedApplication() { CC_ASSERT(sm_pSharedApplication); return *sm_pSharedApplication; } //静态成员函数,获取当前系统的语言类型 ccLanguageType CCApplication::getCurrentLanguage() { //默认为英语 ccLanguageType ret = kLanguageEnglish; // LCID localeID = GetUserDefaultLCID(); unsigned short primaryLanguageID = localeID & 0xFF; switch (primaryLanguageID) { case LANG_CHINESE://中文 ret = kLanguageChinese; break; case LANG_FRENCH://法文 ret = kLanguageFrench; break; case LANG_ITALIAN://意文 ret = kLanguageItalian; break; case LANG_GERMAN://德文 ret = kLanguageGerman; break; case LANG_SPANISH://西班牙文 ret = kLanguageSpanish; break; case LANG_RUSSIAN://俄文 ret = kLanguageRussian; break; } return ret; } NS_CC_END; ////////////////////////////////////////////////////////////////////////// //在注册表中写入对于PVRFrame的显示和隐藏的设置 ////////////////////////////////////////////////////////////////////////// static void PVRFrameEnableControlWindow(bool bEnable) { HKEY hKey = 0; // 打开注册表的 PVRFrame 项 if(ERROR_SUCCESS != RegCreateKeyExW(HKEY_CURRENT_USER, L"Software\\Imagination Technologies\\PVRVFRame\\STARTUP\\", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, NULL)) { return; } const wchar_t * wszValue = L"hide_gui"; const wchar_t * wszNewData = (bEnable) ? L"NO" : L"YES"; wchar_t wszOldData[256] = {0}; DWORD dwSize = sizeof(wszOldData); //读取相应的键值 LONGstatus = RegQueryValueExW(hKey, wszValue, 0, NULL, (LPBYTE)wszOldData, &dwSize); //如果键值不存在,或者键值存在但与当前值不同,重设键值 if (ERROR_FILE_NOT_FOUND == status || (ERROR_SUCCESS == status && 0 != wcscmp(wszNewData, wszOldData))) { dwSize = sizeof(wchar_t) * (wcslen(wszNewData) + 1); RegSetValueEx(hKey, wszValue, 0, REG_SZ, (const BYTE *)wszNewData, dwSize); } //关闭注册表 RegCloseKey(hKey); }
代码看完之后,我们来做一下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上,一个点就等于一个像素,而在高清显示设备中,默认一个点等于二个像素。在IOSSDK4.0及以后的SDK中支持不同的分辨率。并提供了相应的函数对逻辑点与像素的对应比例值做设置。
重点关注函数:initInstance()。
#ifndef _APP_DELEGATE_H_ #define _APP_DELEGATE_H_ #include "CCApplication.h" class AppDelegate : private cocos2d::CCApplication { public: //构造函数 AppDelegate(); //析构函数 virtual ~AppDelegate(); //重载初始化函数 virtual bool initInstance(); //重载应用程序启动后调用的处理函数 virtual bool applicationDidFinishLaunching(); //重载应用程序转入后台时调用的函数 virtual void applicationDidEnterBackground(); //重载应用程序恢复前台时调用的函数 virtual void applicationWillEnterForeground(); }; #endif // _APP_DELEGATE_H_ 再来看cpp文件 #include "AppDelegate.h" //加入cocos2d头文件 #include "cocos2d.h" #include "HelloWorldScene.h" //加入OpenGL窗口类 #include "CCEGLView.h" //使用cocos2d命名空间 USING_NS_CC; //构造函数 AppDelegate::AppDelegate() { } //析构函数 AppDelegate::~AppDelegate() { } //初始化函数,解答伏笔1 bool AppDelegate::initInstance() { //定义一个bool返回值用于判断是否正常初始化 bool bRet = false; do { //通过对宏定义的匹配来判断当前编译的代码的目标平台是什么,在这里可以知道Cocos2d-x的跨平台都支持哪些平台。我们的教程只针对WIN32平台部分做讲解,其它平台大家可以自行参考WIN32平台部分进行学习。 //第一种平台类型,WIN32系统 #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) // 创建并初始化OpenGL窗口管理实例对象,注意这里使用的是new来动态实例化的。当程序退出时会通过显示设备的release函数进行对窗口的释放。【伏笔3后面会有讲解】。 CCEGLView * pMainWnd = new CCEGLView(); //CC_BREAK_IF宏的意思是如果括号中的语句为否则中断循环。配合do_while流程使用。 //可以看到这里的意思是,如果pMainWnd实例化失败或者pMainWnd创建窗口失败则中断循环。在这里硬性的设定了窗口的标题和窗口的宽度高度。 CC_BREAK_IF(! pMainWnd || ! pMainWnd->Create(TEXT("cocos2d: Hello World"), 480, 320)); #endif // CC_PLATFORM_WIN32 //第二种平台类型,IOS类型 #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) #endif // CC_PLATFORM_IOS //第三种平台类型,ANDORID类型 #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #endif // CC_PLATFORM_ANDROID //第四种平台,WOPHONE平台 #if (CC_TARGET_PLATFORM == CC_PLATFORM_WOPHONE) #endif // CC_PLATFORM_WOPHONE //第五种平台,MARMALADE #if (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE) #endif //第六种平台,LINUX #if (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX) #endif // CC_PLATFORM_LINUX //第七种平台,BADA #if (CC_TARGET_PLATFORM == CC_PLATFORM_BADA) #endif // CC_PLATFORM_BADA //第八种平台,QNX #if (CC_TARGET_PLATFORM == CC_PLATFORM_QNX) #endif // CC_PLATFORM_QNX //如果没有被中断,则成功完成初始化。 bRet = true; //退出循环 } while (0); return bRet; } //重载应用程序启动后调用的处理函数 bool AppDelegate::applicationDidFinishLaunching() { //通过CCDirector的静态函数sharedDirector来获取单件显示设备管理器指针 CCDirector *pDirector = CCDirector::sharedDirector(); //通过CCEGLView的静态函数sharedOpenGLView来获取单件管理实例对象并将其地址通过CCDirector的成员函数setOpenGLView传递给显示设备管理器。 pDirector->setOpenGLView(&CCEGLView::sharedOpenGLView()); //打开使用高清模式,这里给屏蔽了,如未来有需要可以打开。但是在设计程序时需要考虑到打开后对于位置的影响并提前制定相应的设计方案。 //pDirector->enableRetinaDisplay(true); // 打开显示FPS pDirector->setDisplayFPS(true); // 设置当前屏幕的摆放方向,这里给屏蔽了。 // pDirector->setDeviceOrientation(kCCDeviceOrientationLandscapeLeft); // 设置FPS的帧间隔时间差为60分之一秒,从而期望FPS为60帧。 pDirector->setAnimationInterval(1.0 / 60); //通过HelloWorld的静态函数scene()创建返回一个场景实例 CCScene *pScene = HelloWorld::scene(); //运行这个场景 pDirector->runWithScene(pScene); return true; } //重载应用程序转入后台时调用的函数,如电话打进来 void AppDelegate::applicationDidEnterBackground() { //暂停显示设备的渲染处理 CCDirector::sharedDirector()->pause(); // 如果使用了声音引擎,这里进行暂停设置。否则会影响到通话,因为暂时没用到,所以屏蔽 // SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic(); } //重载应用程序恢复前台时调用的函数 void AppDelegate::applicationWillEnterForeground() { //恢复显示设备的渲染处理 CCDirector::sharedDirector()->resume(); //如果使用了声音引擎,这里进行恢复设置 // SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic(); }
在InitInstance函数中,我们看到Cocos2d-x将Windows窗口的创建与控制都封装到了CCEGLView类中。它已经暴露出一个Create函数,做为一个求知者,我们应该迫不及待的想要看到它在哪里创建窗口。继续追进去吧!GoGoGo!
我们进入了CCEGLView_win32.h,通过文件名可以知道CCEGLView类应该有多个平台的版本。我们暂不理它。看一下Create函数。
//
bool CCEGLView::Create(LPCTSTR pTitle, int w, int h) { //定义一个bool型返回值 bool bRet = false; do { //检测窗口句柄是否已经存在。确保只创建一个窗口 CC_BREAK_IF(m_hWnd); //进入到这里,我们应该很高兴了。没错,终于找到了创建窗口的代码。 HINSTANCE hInstance = GetModuleHandle( NULL ); WNDCLASS wc;// 窗口类 // 设置相关参数 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = _WindowProc;// 本窗口使用的WINDOWS消息处理函数 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon( NULL, IDI_WINLOGO ); wc.hCursor = LoadCursor( NULL, IDC_ARROW ); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = kWindowClassName; //注册窗口类,如果失败则判断失败原因是否是此类别已存在(错误码1410),如果是因为此类存在而导致失败,则仍然可以继续。 CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError()); // 取得窗口的窗体矩形 RECT rcDesktop; GetWindowRect(GetDesktopWindow(), &rcDesktop); // 调用创建口函数 m_hWnd = CreateWindowEx( WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, kWindowClassName,// 之前注册的窗口类 pTitle,// 窗口标题 WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,// 窗体样式 0, 0, // 窗体位置 0, // 窗体宽度 0, // 窗体高度 NULL,// 无父窗口 NULL,// 无菜单 hInstance,// 程序句柄 NULL ); //判断窗口是否创建成功 CC_BREAK_IF(! m_hWnd); //取得显示设备的摆放方向 m_eInitOrientation = CCDirector::sharedDirector()->getDeviceOrientation(); //通过对摆放方向的判断得出水平还是垂直 m_bOrientationInitVertical = (CCDeviceOrientationPortrait == m_eInitOrientation || kCCDeviceOrientationPortraitUpsideDown == m_eInitOrientation) ? true : false; m_tSizeInPoints.cx = w; m_tSizeInPoints.cy = h; //设置窗口大小 resize(w, h); // 使用此窗口进行OpenGL的设置 m_pEGL = CCEGL::create(this); // 如果OpenGL创建失败,销毁窗体并中断 if (! m_pEGL) { DestroyWindow(m_hWnd); m_hWnd = NULL; break; } //将静态指针设置为当前实例对象。 s_pMainWindow = this; bRet = true; } while (0); return bRet; }
在注册类时,设定了窗口的消息处理回调函数为_WinDowProc,我们继续追入看一下程序退出时在哪里释放了Opengl窗口实例对象。呵呵,还记得前面我们埋下的伏笔3,它是用new创建出来的。而new出来的内存必须释放。程序退出会销毁窗体,消息处理函数会先后收到WM_CLOSE和WM_DESTROY。我们找一下。
static LRESULT CALLBACK _WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { //如果OpenGL窗体初始化完成,则由OpenGL窗体进行消息处理,否则由WINDOWS系统进行默认处理 if (s_pMainWindow && s_pMainWindow->getHWnd() == hWnd) { return s_pMainWindow->WindowProc(uMsg, wParam, lParam); } else { return DefWindowProc(hWnd, uMsg, wParam, lParam); } }
继续。
LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; switch (message) {…//其它消息暂不理,可自行分析 case WM_PAINT: //这里是刷新屏幕,因为使用了OpenGL来进行绘制,所以这里不作任何处理。 BeginPaint(m_hWnd, &ps); EndPaint(m_hWnd, &ps); break; case WM_CLOSE: //调用单件显示设备管理器的end函数关闭窗口 CCDirector::sharedDirector()->end(); break; case WM_DESTROY: //向消息循环发送WM_QUIT消息退出程序 PostQuitMessage(0); break; default: return DefWindowProc(m_hWnd, message, wParam, lParam); } return 0; }
进入显示设备管理器CCDirector的end函数。
void CCDirector::end() { m_bPurgeDirecotorInNextLoop = true; }
倒!这里只是设置成员变量m_bPurgeDirecotorInNextLoop为true,没有什么delete。怎么回事呢?
好吧,继续分析,成员变量的名字意思是“是否在下一个循环时清除显示设备”。哦。也就是说这只是一个开关。在循环函数中判断它是否为true来清除显示设备。打开mainLoop函数。解答伏笔2
void CCDisplayLinkDirector::mainLoop(void) {//如果清除显示设备开关为true if (m_bPurgeDirecotorInNextLoop) { //清楚设备 purgeDirector(); m_bPurgeDirecotorInNextLoop = false; }//否则判断显示设备是否有效 else if (! m_bInvalid) { //如果有效,绘制场景 drawScene(); //调用内存管理器释放其管理的内存 CCPoolManager::getInstance()->pop(); } }
马上要接晓答案了
void CCDirector::purgeDirector() { …//其它暂不理。有兴趣的朋友请自行分析 // 调用Opengl窗口管理实例对象的release函数进行释放。 m_pobOpenGLView->release(); m_pobOpenGLView = NULL; }
进入OpenGL窗口管理类的release函数
void CCEGLView::release() {//销毁窗体 if (m_hWnd) { DestroyWindow(m_hWnd); m_hWnd = NULL; } s_pMainWindow = NULL; //注销所使用的窗口类 UnregisterClass(kWindowClassName, GetModuleHandle(NULL)); //释放使用到的指针 CC_SAFE_DELETE(m_pSet); CC_SAFE_DELETE(m_pTouch); CC_SAFE_DELETE(m_pDelegate); CC_SAFE_DELETE(m_pEGL); //关键点:在最后释放了自已在AppDelegate::initInstance()中通过new创建对象而占用的内存。 delete this; }
现在我们了解了Cocos2d-x是如何将WINDOWS程序基本框架封装在自已的类之中的。运行一下程序,可们可以看到弹出的窗口显示出HelloWorld的画面。而当初复杂的WINDOWS程序基本框架。在这里只化为简单的一句run()。非常简洁!
这个运行中的窗口,没有最大化系统按钮,窗体大小也被固定在480x320。我们希望能够更灵活的设置窗口的标题和大小。那我们必须要做一些改动。即然已经看懂了代码,下面就亲自实习一下吧。
打开AppDelegate的initInstance函数,增加相应的参数。
bool AppDelegate::initInstance(LPCTSTR szTitle,UINT wWidth,UINT wHeight)
并改动下面一句
CC_BREAK_IF(! pMainWnd //改成由函数参数来创建OpenGL窗体 || ! pMainWnd->Create(szTitle, wWidth, wHeight)); //|| ! pMainWnd->Create(TEXT("cocos2d: Hello World"), 480, 320));
然后我们打开CCApplication_win32.cpp,找到run函数。做同样的修改。
int CCApplication::run(LPCTSTR szTitle,UINT wWidth,UINT wHeight)
并改动下面一句
if (! initInstance(szTitle,wWidth,wHeight) || ! applicationDidFinishLaunching()) // if (! initInstance() || ! applicationDidFinishLaunching())
之后我们将头文件所需要改动的函数声明也一一改动。保存,重新编译libcocos2d工程。再打开HelloWorld中的main.cpp修改这一句:
//我们可以在这里自已设定窗口标题和窗口大小
return cocos2d::CCApplication::sharedApplication().run(TEXT("第一个Cocos2d-x程序"),800,600); //return cocos2d::CCApplication::sharedApplication().run();
编译HelloWorld。运行一下。享受一下成果!
对于这个框架我写的较多,因为整个Cocos2d-x的所有例子都是基于这个框架,我们必须理解清楚它是怎么一回事。之后我们将进入到引擎显示部分模块的讲解。希望大家后面继续努力!下课:)