Cocos2d-x 的“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程序实例
AppDelegateapp;
// 运行程序实例
returncocos2d::CCApplication::sharedApplication().run();
}
代码看着就这么几行,好像非常简单。那是因为 Cocos2d-x把很多 WINDOWS 窗口程序的代码都封装到了内部。这也使得它更加简单易学。但我们也应该明白这里面是怎么一回事。
咱们转到AppDelegate.h ,可以看到 AppDelegate 类是一个私有继承 Cocos2d 命名空间中的 CCApplication 类。它的成员函数均是重载了 CCApplication类的成员函数。顾名思义, CCApplication代表了程序类。我们打开它的声明文件(CCApplication_win32.h)看一下:
#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 boolinitInstance() = 0;
//程序启动后调用的函数,在这里创建设备与场景
virtual boolapplicationDidFinishLaunching() = 0;
//当程序转入后台,如电话打入时调用
virtual voidapplicationDidEnterBackground() = 0;
//当程序转入前台,再次恢复时调用
virtual voidapplicationWillEnterForeground() = 0;
//在“设备”设置FPS时调用的函数,设置帧间隔时间
voidsetAnimationInterval(double interval);
//声明一个枚举,列出当前设备的摆放方向
typedef enum
{
/// 垂直方向, home 键在下面
kOrientationPortrait =0,
/// 垂直方向, home键在上面
kOrientationPortraitUpsideDown = 1,
/// 水平方向,home键在右边
kOrientationLandscapeLeft = 2,
/// 水平方向,home健在左边
kOrientationLandscapeRight = 3,
} Orientation;
//在“设备”改变了摆放方向后调用的函数,设置设备摆放方向
OrientationsetOrientation(Orientation orientation);
//取得窗口的状态栏所在的矩形位置
void statusBarFrame(CCRect * rect);
//运行程序
int run();
//取得当前的程序实例,这种用法可参考C++模式设计中的“单件”模式
static CCApplication&sharedApplication();
//取得当前的语言类型
static ccLanguageTypegetCurrentLanguage();
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()。(在CCApplication_win32.cpp)
#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_nAnimationIntervalm_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);
//主消息循环
MSGmsg;
LARGE_INTEGERnFreq;
LARGE_INTEGERnLast;
LARGE_INTEGERnNow;
//WINDOWS高精度定时器的用法,先获取频率
QueryPerformanceFrequency(&nFreq);
//获取当前的计数值,即频率x当前时间
QueryPerformanceCounter(&nLast);
//initInstance函数为虚函数,由派生类AppDelegate进行了重载。此段代码在调用
AppDelegate重载的initInstance函数之后调用applicationDidFinishLaunching函数完成一
些初始化处理。
//注:AppDelegate重载initInstance函数做了什么我们暂且只先认为它如平时我们
WINDOWS基本框架程序一样创建了一个Windows窗口。【伏笔1后面会有讲解】。
if (!initInstance() || ! applicationDidFinishLaunching())
{
return0;
}
//取得当前使用的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_INTEGERnFreq;
QueryPerformanceFrequency(&nFreq);
//计算出频率X帧间隔时差的值存入m_nAnimationInterval用做比较值
m_nAnimationInterval.QuadPart= (LONGLONG)(interval * nFreq.QuadPart);
}
//摆放方向变化时外部自动调用的设置摆放方向
CCApplication::Orientation CCApplication::setOrientation(Orientationorientation){
//切换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(){
//默认为英语
ccLanguageTyperet = kLanguageEnglish;
//
LCIDlocaleID = GetUserDefaultLCID();
unsignedshort primaryLanguageID = localeID & 0xFF;
switch(primaryLanguageID){
caseLANG_CHINESE:
//中文
ret =kLanguageChinese;
break;
caseLANG_FRENCH:
//法文
ret =kLanguageFrench;
break;
caseLANG_ITALIAN:
//意文
ret =kLanguageItalian;
break;
caseLANG_GERMAN:
//德文
ret =kLanguageGerman;
break;
caseLANG_SPANISH:
//西班牙文
ret =kLanguageSpanish;
break;
caseLANG_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\\ImaginationTechnologies\\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);
//读取相应的键值
LSTATUS status = 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上,一个点就等于一个像素,而在高清显示设备中,默认一个点等于二个像素。在IOS SDK 4.0及以后的SDK中支持不同的分辨率。并提供了相应的函数对逻辑点与像素的对应比例值做设置。
重点关注函数: initInstance ()。(在AppDelegate.h)
#ifndef _APP_DELEGATE_H_
#define _APP_DELEGATE_H_
#include "CCApplication.h"
class AppDelegate : privatecocos2d::CCApplication
{
public:
//构造函数
AppDelegate();
//析构函数
virtual~AppDelegate();
//重载初始化函数
virtualbool initInstance();
//重载应用程序启动后调用的处理函数
virtualbool applicationDidFinishLaunching();
//重载应用程序转入后台时调用的函数
virtualvoid applicationDidEnterBackground();
//重载应用程序恢复前台时调用的函数
virtualvoid applicationWillEnterForeground();
};
#endif // _APP_DELEGATE_H_
再来看AppDelegate.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);
returnbRet;
}
//重载应用程序启动后调用的处理函数
bool AppDelegate::applicationDidFinishLaunching() {
//通过CCDirector的静态函数sharedDirector来获取单件显示设备管理器指针
CCDirector*pDirector = CCDirector::sharedDirector();
//通过CCEGLView的静态函数sharedOpenGLView来获取单件管理实例对象并将其地址
通过CCDirector的成员函数setOpenGLView传递给显示设备管理器。
pDirector->setOpenGLView(&CCEGLView::sharedOpenGLView());
//打开使用高清模式,这里给屏蔽了,如未来有需要可以打开。但是在设计程序时需要
考虑到打开后对于位置的影响并提前制定相应的设计方案。
//pDirector->enableRetinaDisplay(true);
// 打开显示
FPSpDirector->setDisplayFPS(true);
// 设置当前屏幕的摆放方向,这里给屏蔽了。
//pDirector->setDeviceOrientation(kCCDeviceOrientationLandscapeLeft);
// 设置FPS的帧间隔时间差为60分之一秒,从而期望FPS为60帧。
pDirector->setAnimationInterval(1.0/ 60);
//通过HelloWorld的静态函数scene()创建返回一个场景实例
CCScene*pScene = HelloWorld::scene();
//运行这个场景
pDirector->runWithScene(pScene);
returntrue;
}
//重载应用程序转入后台时调用的函数,如电话打进来
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.cpp,通过文件名可以知道 CCEGLView类应该有多个平台的版本。我们暂不理它。看一下Create函数。(在CCEGLView_win32.cpp)
//我们先从boolCCEGLView::Create(LPCTSTR pTitle, int w, int h)开始
bool CCEGLView::Create(LPCTSTR pTitle, int w, int h){
//定义一个bool型返回值
bool bRet =false;
do
{
//检测窗口句柄是否已经存在。确保只创建一个窗口
CC_BREAK_IF(m_hWnd);
//进入到这里,我们应该很高兴了。没错,终于找到了创建窗口的代码。
HINSTANCEhInstance = 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);
returnbRet;
}
在注册类时,设定了窗口的消息处理回调函数为_WinDowProc,我们继续追入看一下程序退出时在哪里释放了Opengl窗口实例对象。呵呵,还记得前面我们埋下的伏笔3,它是用new创建出来的。而new出来的内存必须释放。程序退出会销毁窗体,消息处理函数会先后收到WM_CLOSE和WM_DESTROY。我们找一下。(还是CCEGLView_win32.cpp,在boolCCEGLView::Create()函数上面)
static LRESULT CALLBACK _WindowProc(HWND hWnd, UINT uMsg, WPARAMwParam,
LPARAM lParam){
//如果OpenGL窗体初始化完成,则由OpenGL窗体进行消息处理,否则由WINDOWS
系统进行默认处理
if (s_pMainWindow&& s_pMainWindow->getHWnd() == hWnd){
return s_pMainWindow->WindowProc(uMsg,wParam, lParam);
}else{
returnDefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
继续。(在CCEGLView_win32.cpp,在boolCCEGLView::Create()函数下面)
LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAMlParam){
PAINTSTRUCTps;
switch(message){
…
//以上消息暂不理,可自行分析
caseWM_PAINT:
//这里是刷新屏幕,因为使用了OpenGL来进行绘制,所以这里不作任何处理。
BeginPaint(m_hWnd,&ps);
EndPaint(m_hWnd,&ps);
break;
caseWM_CLOSE:
//调用单件显示设备管理器的end函数关闭窗口
CCDirector::sharedDirector()->end();
break;
caseWM_DESTROY:
//向消息循环发送WM_QUIT消息退出程序
PostQuitMessage(0);
break;
default:
returnDefWindowProc(m_hWnd, message, wParam, lParam);
}return 0;
}
进入显示设备管理器 CCDirector的 end 函数。(在CCDirector.cpp)
void CCDirector::end(){
//是否在下一个循环时清除显示设备
m_bPurgeDirecotorInNextLoop= true;
}
倒 ! 这里只是设置成员变量 m_bPurgeDirecotorInNextLoop为 true,没有什么 delete 。怎么回事呢?
好吧,继续分析,成员变量的名字意思是“是否在下一个循环时清除显示设备”。哦。也就是说这只是一个开关。在循环函数中判断它是否为 true 来清除显示设备。打开 mainLoop函数。解答伏笔 2(在CCDirector.cpp)
void CCDisplayLinkDirector::mainLoop(void){
//如果清除显示设备开关为true
if(m_bPurgeDirecotorInNextLoop){
//清除设备
purgeDirector();
m_bPurgeDirecotorInNextLoop= false;
}//否则判断显示设备是否有效
else if (!m_bInvalid) {
//如果有效,绘制场景
drawScene();
//调用内存管理器释放其管理的内存
CCPoolManager::getInstance()->pop();
}
}
马上要接晓答案了(在CCDirector.cpp)
void CCDirector::purgeDirector(){
…
//以上暂不理。有兴趣的朋友请自行分析
// 调用Opengl窗口管理实例对象的release函数进行释放。
m_pobOpenGLView->release();
m_pobOpenGLView= NULL;
}
进入 OpenGL 窗口管理类的 release函数(在CCEGLView_win32.cpp)
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创建对象而占用
的内存。
deletethis;
}
现在我们了解了 Cocos2d-x 是如何将 WINDOWS 程序基本框架封装在自已的类之中的。运行一下程序,可们可以看到弹出的窗口显示出 HelloWorld的画面。而当初复杂的 WINDOWS程序基本框架。在这里只化为简单的一句 run()。非常简洁!
这个运行中的窗口,没有最大化系统按钮,窗体大小也被固定在 480x320。我们希望能够更灵活的设置窗口的标题和大小。那我们必须要做一些改动。即然已经看懂了代码,下面就亲自实习一下吧。
打开 AppDelegate 的 initInstance 函数,增加相应的参数。 (AppDelegate.cpp中的boolAppDelegate::initInstance()函数)
bool AppDelegate::initInstance(LPCTSTR szTitle,UINT wWidth,UINTwHeight)
并改动下面一句
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);
//returncocos2d::CCApplication::sharedApplication().run();