来源于红孩儿的游戏编程之路 http://blog.csdn.net/honghaier/article/details/7887873#comments
Cocos2d-x 的“HelloWorld” 深入分析
本节所用Cocos2d-x版本:cocos2d-1.0.1-x-0.12.0
不能免俗,一切都从“HelloWorld!”开始.打开HelloWorld工程,里面有两个文件目录Classes和win32。
Classes下有HelloWorldScene.h/cpp ,AppDelegate.h/cpp.
win32下有main.h/cpp
首先看一下win32目录下的main.h,其中定义了使用win32平台进行编译的宏和一些Windows编程头文件。
- [Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http:
- #ifndef __MAIN_H__
-
- #define __MAIN_H__
-
-
-
- #define WIN32_LEAN_AND_MEAN
-
-
-
- #include <windows.h>
-
- #include <tchar.h>
-
-
-
- #include "CCStdC.h"
-
- #endif// __MAIN_H__
再打开main.cpp.
-
-
- #include "main.h"
-
-
-
- #include "../Classes/AppDelegate.h"
-
-
-
- int APIENTRY _tWinMain(HINSTANCE hInstance,
-
- HINSTANCE hPrevInstance,
-
- LPTSTR lpCmdLine,
-
- int nCmdShow)
-
- {
-
-
-
- UNREFERENCED_PARAMETER(hPrevInstance);
-
- UNREFERENCED_PARAMETER(lpCmdLine);
-
-
-
- AppDelegate app;
-
-
-
- return cocos2d::CCApplication::sharedApplication().run();
-
- }
代码看着就这么几行,好像非常简单。那是因为Cocos2d-x把很多WINDOWS窗口程序的代码都封装到了内部。这也使得它更加简单易学。但我们也应该明白这里面是怎么一回事。
咱们转到AppDelegate.h,可以看到AppDelegate类是一个私有继承Cocos2d命名空间中的CCApplication类。它的成员函数均是重载了CCApplication类的成员函数。顾名思义,CCApplication代表了程序类。我们打开它的声明文件看一下:
通过对于CCApplication_win32.h的代码分析,我们可以清楚CCApplication的功能是对于程序的控制。
我们转到CCApplication类的cpp文件CCApplication_win32.cpp再来分析一下。
读前小提示: CCDirector代表显示设备管理器。也是一个单件类。通过其静态函数CCDirector::sharedDirector()来访问唯一的显示设备。
重点关注函数:CCApplication(),run()。
- #include "CCApplication.h"
-
- #include "CCDirector.h"
-
- static void PVRFrameEnableControlWindow(bool bEnable);
-
- NS_CC_BEGIN;
-
- CCApplication * CCApplication::sm_pSharedApplication = 0;
-
- CCApplication::CCApplication()
- : m_hInstance(NULL)
- , m_hAccelTable(NULL)
- {
-
- m_hInstance= GetModuleHandle(NULL);
-
- m_nAnimationInterval.QuadPart = 0;
-
- CC_ASSERT(! sm_pSharedApplication);
-
- sm_pSharedApplication = this;
- }
-
- CCApplication::~CCApplication()
- {
-
- CC_ASSERT(this == sm_pSharedApplication);
- sm_pSharedApplication = NULL;
- }
-
- int CCApplication::run()
- {
-
- PVRFrameEnableControlWindow(false);
-
- MSG msg;
- LARGE_INTEGER nFreq;
- LARGE_INTEGER nLast;
- LARGE_INTEGER nNow;
-
- QueryPerformanceFrequency(&nFreq);
-
- QueryPerformanceCounter(&nLast);
-
-
- if (! initInstance() || ! applicationDidFinishLaunching())
- {
- return 0;
- }
-
- CCEGLView& mainWnd = CCEGLView::sharedOpenGLView();
-
- mainWnd.centerWindow();
- ShowWindow(mainWnd.getHWnd(), SW_SHOW);
-
- while (1)
- {
-
- if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
- {
-
- QueryPerformanceCounter(&nNow);
-
- if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)
- {
-
- nLast.QuadPart = nNow.QuadPart;
-
- CCDirector::sharedDirector()->mainLoop();
- }
- else
- {
-
- Sleep(0);
- }
- continue;
- }
-
- if (WM_QUIT == msg.message)
- {
-
- break;
- }
-
- if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))
- {
-
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
- return (int) msg.wParam;
- }
-
-
- void CCApplication::setAnimationInterval(double interval)
- {
-
- LARGE_INTEGER nFreq;
- QueryPerformanceFrequency(&nFreq);
-
- m_nAnimationInterval.QuadPart = (LONGLONG)(interval * nFreq.QuadPart);
- }
-
- CCApplication::Orientation CCApplication::setOrientation(Orientation orientation)
- {
-
- CCEGLView * pView = CCDirector::sharedDirector()->getOpenGLView();
- if (pView)
- {
- return (Orientation)pView->setDeviceOrientation(orientation);
- }
- return (Orientation)CCDirector::sharedDirector()->getDeviceOrientation();
- }
-
- void CCApplication::statusBarFrame(CCRect * rect)
- {
- if (rect)
- {
-
- *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;
-
-
-
- static void PVRFrameEnableControlWindow(bool bEnable)
- {
- HKEY hKey = 0;
-
- 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中支持不同的分辨率。并提供了相应的函数对逻辑点与像素的对应比例值做设置。
重点关注函数: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"
-
- #include "cocos2d.h"
- #include "HelloWorldScene.h"
-
- #include "CCEGLView.h"
-
- USING_NS_CC;
-
- AppDelegate::AppDelegate() {
- }
-
- AppDelegate::~AppDelegate() {
- }
-
- bool AppDelegate::initInstance()
- {
-
- bool bRet = false;
- do {
-
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
-
- CCEGLView * pMainWnd = new CCEGLView();
-
-
- CC_BREAK_IF(! pMainWnd
- || ! pMainWnd->Create(TEXT("cocos2d: Hello World"), 480, 320));
- #endif // CC_PLATFORM_WIN32
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
- #endif // CC_PLATFORM_IOS
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
- #endif // CC_PLATFORM_ANDROID
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_WOPHONE)
- #endif // CC_PLATFORM_WOPHONE
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE)
- #endif
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
- #endif // CC_PLATFORM_LINUX
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_BADA)
- #endif // CC_PLATFORM_BADA
-
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_QNX)
- #endif // CC_PLATFORM_QNX
-
- bRet = true;
-
- } while (0);
- return bRet;
- }
-
- bool AppDelegate::applicationDidFinishLaunching() {
-
- CCDirector *pDirector = CCDirector::sharedDirector();
-
- pDirector->setOpenGLView(&CCEGLView::sharedOpenGLView());
-
-
-
- pDirector->setDisplayFPS(true);
-
-
-
- pDirector->setAnimationInterval(1.0 / 60);
-
- CCScene *pScene = HelloWorld::scene();
-
- pDirector->runWithScene(pScene);
- return true;
- }
-
- void AppDelegate::applicationDidEnterBackground() {
-
- CCDirector::sharedDirector()->pause();
-
-
- }
-
- void AppDelegate::applicationWillEnterForeground() {
-
- CCDirector::sharedDirector()->resume();
-
-
- }
在InitInstance函数中,我们看到Cocos2d-x将Windows窗口的创建与控制都封装到了CCEGLView类中。它已经暴露出一个Create函数,做为一个求知者,我们应该迫不及待的想要看到它在哪里创建窗口。继续追进去吧!GoGoGo!
我们进入了CCEGLView_win32.h,通过文件名可以知道CCEGLView类应该有多个平台的版本。我们暂不理它。看一下Create函数。
//
- bool CCEGLView::Create(LPCTSTR pTitle, int w, int h)
- {
-
- 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;
- 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;
-
- 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);
-
- m_pEGL = CCEGL::create(this);
-
- 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)
- {
-
- 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:
-
- BeginPaint(m_hWnd, &ps);
- EndPaint(m_hWnd, &ps);
- break;
- case WM_CLOSE:
-
- CCDirector::sharedDirector()->end();
- break;
- case WM_DESTROY:
-
- 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)
- {
- if (m_bPurgeDirecotorInNextLoop)
- {
-
- purgeDirector();
- m_bPurgeDirecotorInNextLoop = false;
- }
- else if (! m_bInvalid)
- {
-
- drawScene();
-
-
- CCPoolManager::getInstance()->pop();
- }
- }
马上要接晓答案了
- void CCDirector::purgeDirector()
- {
- …
-
- 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);
-
- delete this;
- }
现在我们了解了Cocos2d-x是如何将WINDOWS程序基本框架封装在自已的类之中的。运行一下程序,可们可以看到弹出的窗口显示出HelloWorld的画面。而当初复杂的WINDOWS程序基本框架。在这里只化为简单的一句run()。非常简洁!
这个运行中的窗口,没有最大化系统按钮,窗体大小也被固定在480x320。我们希望能够更灵活的设置窗口的标题和大小。那我们必须要做一些改动。即然已经看懂了代码,下面就亲自实习一下吧。
打开AppDelegate的initInstance函数,增加相应的参数。
- bool AppDelegate::initInstance(LPCTSTR szTitle,UINT wWidth,UINT wHeight)
并改动下面一句
- CC_BREAK_IF(! pMainWnd
-
- || ! pMainWnd->Create(szTitle, wWidth, wHeight));
-
然后我们打开CCApplication_win32.cpp,找到run函数。做同样的修改。
- int CCApplication::run(LPCTSTR szTitle,UINT wWidth,UINT wHeight)
并改动下面一句
- if (! initInstance(szTitle,wWidth,wHeight) || ! applicationDidFinishLaunching())
-
之后我们将头文件所需要改动的函数声明也一一改动。保存,重新编译libcocos2d工程。再打开HelloWorld中的main.cpp修改这一句:
//我们可以在这里自已设定窗口标题和窗口大小
- return cocos2d::CCApplication::sharedApplication().run(TEXT("第一个Cocos2d-x程序"),800,600);
-
编译HelloWorld。运行一下。享受一下成果!
对于这个框架我写的较多,因为整个Cocos2d-x的所有例子都是基于这个框架,我们必须理解清楚它是怎么一回事。之后我们将进入到引擎显示部分模块的讲解。希望大家后面继续努力!下课:)