不能免俗,一切都从“HelloWorld!”开始.打开HelloWorld工程,里面有两个文件目录Classes和win32。
Classes下有HelloWorldScene.h/cpp ,AppDelegate.h/cpp.
win32下有main.h/cpp
首先看一下win32目录下的main.h,其中定义了使用win32平台进行编译的宏和一些Windows编程头文件。
- [Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http://blog.csdn.net/honghaier]
- #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类中有一个函数enableRetinaDisplay函数。这个函数主要是影响到IOS平台上支持高清显示的设备如iphone4,iphone4s等。如果设置enableRetinaDisplay(false), 则在iphone4平台上运行的结果是游戏的图片分辨率降低为原来的一半,因为宽高都被拉伸了一倍。如果设置enableRetinaDisplay(true), 则在iphone4平台上运行的结果是游戏的图片分辨率正常,但是放置的位置的水平方向和垂直方向都拉伸了两倍。要记住在cocos2d里面设置精灵坐标时,使用的是点,而不是像素,在普通的iphone上,一个点就等于一个像素,而在高清显示设备中,默认一个点等于二个像素。在IOS SDK 4.0及以后的SDK中支持不同的分辨率。并提供了相应的函数对逻辑点与像素的对应比例值做设置。