自学cocos2d 游戏开发实例应该按什么步骤进行

Cocos2d-X游戏工具开发之一:将Cocos2d-X嵌入MFC的子窗体方法讲解
[Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:]&&
红孩儿Cocos2d-X学习园地QQ群: 加群写:Cocos2d-x
另本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!
&&&&&&& 本节所用Cocos2d-x版本:cocos2d-1.0.1-x-0.12.0
& & & &&我在之前的“如何利用Cocos2d-x开发一个游戏?”。在实际的项目开发过程中,工具是支撑开发流程规范的必要条件。一套好的工具可以使工作流程顺畅。从而极大的提高效率。我们知道Unity3d场景编辑器就是一个很好的例子。它将游戏开发中的各个流程节点的工作都集成到编辑器中,这是一个很COOL的想法。对于游戏开发团队来说,只需要掌握好这个流程,使用编辑器做好每一步的编辑工作就可以快速开发出高品质的游戏。
&&&&&& 但对于Cocos2d-x来说。虽然网络上有一些开放的工具,但零零散散的不成系统。并不方便使用。这一章来简要介绍一下如何使用MFC来进行Cocos2d-x相关工具的开发。请对MFC不熟悉的朋友先学习一下基本的对话框程序再进行本章内容的学习。
第一节:将Cocos2d-x嵌入MFC的子窗体中&&
& & [注:学习这一章之前请先学习Cocos2d-x 的“HelloWorld” 深入分析一文]
& & & &首先,我们用VC++在Cocos2d-x的目录里建立了个Unicode字符集MFC对话框程序。这里命名为Cocos2dXEditor。按照HelloWorld工程设置把包含头文件目录,库文件目录,包含库都设置好。并且画好对话框界面。
& & &如图:
& & & & &我把界面设计为三部分,左边和右边用来创建对话框面板,至于要具体显示什么根据我们的工具开发需求而定。暂时先不管。而中间放一个Picture 控件,做为Cocos2d-x的显示窗口。为了生成相应的窗口控件变量,我们需要将此Picture控件的ID改成自定义的一个ID,这里我们改成IDC_COCOS2DXWIN保存。
& & & & &我们画好界面后,选中Picture控件后右键单击,在弹出菜单中找到“添加变量”。为Picture控件生成一个控件变量。这里命名为m_Cocos2dXWin,并点击完成。 & & & & & & & & & & & & & & & & & & & &
好,现在主对话框类里有了这么一句:
m_Cocos2dXW
& & & &这个变量是CStatic类型的,它只是一个最简单的CWnd派生类。并不能显示Cocos2d-x。我们需要做点工作来创建一个新的类来替代它完成显示Cocos2d-x的工作。
&&&&&& 在工程右键弹出菜单里找到“添加”一项,在其子菜单中点击“类”。 & & & & & & & & & & & & & & & & & & & & & & & & & & & &
在弹出的“添加类”对话框中“MFC”项中的“MFC类”。 & & & & & & & & & & & & & & & & &&
& & & & &点击“添加”。这时会弹出“MFC类向导”对话框。我们在类名里输入“CCocos2dXWin”,并选择基类CWnd。然后点击“完成”。
& & & & & & & & & & & & & & & & & & & &
& & & & &向导会为我们的工程自动加入两个文件“Cocos2dXWin.h”和“Cocos2dXWin.cpp”。这才是我们要进行Cocos2d-x显示的窗体类,它的基类是CWnd,与Picture控件有相同的基类。
& & & & 打开Cocos2dXWin.h,在CCocos2dXWin类中增加一个public成员函数声明:
//创建Cocos2dX窗口
CreateCocos2dXWindow();
& & & & 我们另外增加一个private变量用来标记窗口是否已经进行了Cocos2d-x的OpenGL窗口创建。
//是否已经初始化
m_bInitCocos2dX;
& & & & & 在CPP文件中加入需要用到的头文件
#include &CCEGLView_win32.h&
#include &../Classes/AppDelegate.h&
#include &cocos2d.h&
& & & & & 下面来手动增加函数定义
/创建Cocos2dX窗口
BOOL CCocos2DXWin::CreateCocos2dXWindow()
//新建一个CRect变量获取窗口的客户区大小
CRect tClientR
GetClientRect(&tClientRect);
//调用cocos2d中的CCApplication::sharedApplication()获取Cocos2d-x程序类单件实例对象。调用其run函数。在这里我们传入四个参数,与第一章“HelloWorld”不同的是这里增加了新参数“当前窗口的句柄”。这是使用MFC来嵌入Cocos2d-x窗口的关健所在。我们暂时埋个伏笔,后面再详解。
cocos2d::CCApplication::sharedApplication().run(GetSafeHwnd(),TEXT(&第一个Cocos2d-x程序&),tClientRect.Width(),tClientRect.Height());
//这里将变量设置为TRUE
m_bInitCocos2dX = TRUE;
return TRUE;
& & & & &现在还不能运行。因为CCApplication的run函数跟本就没有HWND参数,我们必须对CCApplication动个小手术。使它能够接收到窗体的句柄并使用这个句柄来创建OpenGL。 & & & &
& & & & &找到CCApplication_win32.cpp中的run函数做以下修改:
[Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http://blog.csdn.net/honghaier]
int CCApplication::run(HWND hWnd,LPCTSTR szTitle,UINT wWidth,UINT wHeight)
PVRFrameEnableControlWindow(false);
// Main message loop:
LARGE_INTEGER nF
LARGE_INTEGER nN
QueryPerformanceFrequency(&nFreq);
//将原临时变量nLast改为成员变量m_nLast。因为后面要把每帧的渲染做到外部调用的public函数里,所以需要存一下上帧的时间。
QueryPerformanceCounter(&m_nLast);
// 关键点1:必须将参数hWnd也做为参数传给initInstance函数。
if (! initInstance(hWnd,szTitle,wWidth,wHeight) || ! applicationDidFinishLaunching())
CCEGLView& mainWnd = CCEGLView::sharedOpenGLView();
//关键点2:我们如果要使用指定的CWnd窗口来创建OpenGL,那么必须放弃使用Cocos2d-x封装好的消息循环。因为这些工作都会交由MFC来做。我们为了不破坏原来的Cocos2d-x创建窗口方式。就在这里做个判断处理,如果要使用原来由Cocos2d-x创建窗口的方式,只需要第一个参数传NULL就好了。
if(NULL == hWnd)
mainWnd.centerWindow();
ShowWindow(mainWnd.getHWnd(), SW_SHOW);
if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
// Get current time tick.
QueryPerformanceCounter(&nNow);
// If it's the time to draw next frame, draw it, else sleep a while.
if (nNow.QuadPart - m_nLast.QuadPart & m_nAnimationInterval.QuadPart)
m_nLast.QuadPart = nNow.QuadP
CCDirector::sharedDirector()-&mainLoop();
if (WM_QUIT == msg.message)
// Quit message loop.
// Deal with windows message.
if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))
TranslateMessage(&msg);
DispatchMessage(&msg);
return (int) msg.wP
& & & & & &修改
中的函数声明与cpp一致并保存,然后打开
CCApplication
AppDelegate
initInstance
函数。做同样的修改工作。
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
CCEGLView * pMainWnd = new CCEGLView();
CC_BREAK_IF(! pMainWnd
|| ! pMainWnd-&Create(hWnd,szTitle, wWidth, wHeight));
//|| ! pMainWnd-&Create(TEXT(&cocos2d: Hello World&), 480, 320));
// CC_PLATFORM_WIN32
& & & & & &修改.h中
函数声明与
保持一致,再打开CCEGL_View_win32.cpp,找到Create函数,继续手术:
bool CCEGLView::Create(HWND hWnd,LPCTSTR pTitle, int w, int h)
bool bRet =
//在这里做个判断,如果参数中的窗口句柄不为空,则使用参数句柄做为成员变量m_hWnd的值。而在
m_hWnd = hW
CC_BREAK_IF(m_hWnd);
HINSTANCE hInstance = GetModuleHandle( NULL );
// Windows Class Structure
// Redraw On Size, And Own DC For Window.
= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc
= _WindowP
// WndProc Handles Messages
wc.cbClsExtra
// No Extra Window Data
wc.cbWndExtra
// No Extra Window Data
wc.hInstance
// Set The Instance
= LoadIcon( NULL, IDI_WINLOGO ); // Load The Default Icon
wc.hCursor
= LoadCursor( NULL, IDC_ARROW ); // Load The Arrow Pointer
wc.hbrBackground
// No Background Required For GL
wc.lpszMenuName
// We Don't Want A Menu
wc.lpszClassName
= kWindowClassN
// Set The Class Name
CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError());
// center window position
GetWindowRect(GetDesktopWindow(), &rcDesktop);
// create window
m_hWnd = CreateWindowEx(
WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, // Extended Style For The Window
kWindowClassName,
// Class Name
// Window Title
WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,
// Defined Window Style
// Window Position
// Window Width
// Window Height
// No Parent Window
// No Menu
hInstance,
// Instance
CC_BREAK_IF(! m_hWnd);
m_eInitOrientation = CCDirector::sharedDirector()-&getDeviceOrientation();
m_bOrientationInitVertical = (CCDeviceOrientationPortrait == m_eInitOrientation
|| kCCDeviceOrientationPortraitUpsideDown == m_eInitOrientation) ? true :
m_tSizeInPoints.cx =
m_tSizeInPoints.cy =
resize(w, h);
// 当调用CCEGL的静态函数create来创建OpenGL窗口时,传入的参数是CCEGLView实例对象,在内部会取得实例对象的m_hWnd变量做为OpenGL的窗口句柄。这样我们就实现了使用参数传递的窗口句柄来创建OpenGL窗口。
m_pEGL = CCEGL::create(this);
if (! m_pEGL)
DestroyWindow(m_hWnd);
m_hWnd = NULL;
s_pMainWindow =
} while (0);
& & & & &这样我们就完成了对于创建窗口函数的修改。但是因为屏蔽了原窗口的创建,所以也同时屏蔽了Cocos2d-x对于窗口消息的处理。我们在类视图中找到CCocos2dXWin,在右键弹出菜单中点击“属性”。打开类属性编辑框。
& & & & & & & & & & & & & & & & & & & & & & &
& & & & & &在WindowProc一栏中增加函数WindorProc,完成后进入CCocos2dXWin的WindorProc函数,因为我们在CCEGLView的Create函数中可以找到在注册窗口类时,其设定的窗口消息处理回调函数是WindowProc.而其实例对象是单件。故可以这样修改:
LRESULT CCocos2DXWin::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
// 这里我们先判断一下是否初始化完成Cocos2dX再进行消息处理
if(m_bInitCocos2dX)
CCEGLView::sharedOpenGLView().WindowProc(message, wParam, lParam);
return CWnd::WindowProc(message, wParam, lParam);
& & & & &这样Cocos2dXWin所实例化的窗口就可以接收到鼠标等对于窗口的消息了。但是!我们屏幕了Cocos2d-x的消息循环,而Cocos2d-x的渲染处理函数是在消息循环中调用的。我们就算现在运行程序,也看不到帅气的小怪物。所以我们必须想办法实时的调用Cocos2d-x的渲染处理函数。对于工具来说,我们并不需要考虑太高的FPS刷新帧数,所以我们只需要使用一个定时器,并在定时触发函数中进行Cocos2d-x的渲染处理函数的调用就可以了。
& & & & 在CCocos2DXWin::CreateCocos2dXWindow()函数尾部加入一句:
SetTimer(1,1,NULL);
& & & & &加入一个ID为1的定时器,设置它每1毫秒响应一次,其处理函数为默认,则使用CCocos2DXWin的WINDOWS消息处理函数对于WM_TIMER进行响应就可以了。
&&&&&&&& 按之前增加WindorProc函数的方式找到CCocos2DXWin的消息WM_TIMER一项,增加函数OnTimer.如图: & & & & & & & & & & & & & & & & & & & &
& & & & & 在其生成的函数OnTimer中我们新写一个函数调用,则每次定时响应调用
void CCocos2DXWin::OnTimer(UINT_PTR nIDEvent)
//我们写一个renderWorld函数代表Cocos2d-x的世界渲染
cocos2d::CCApplication::sharedApplication().renderWorld();
CWnd::OnTimer(nIDEvent);
& & & & &我们打开CCApplication_win32.h,为CCApplication类增加一个public的renderWorld函数。
//帧循环调用渲染
bool renderWorld();
& & & & 在cpp中我们将消息循环中的相关处理移入进来
bool CCApplication::renderWorld()
LARGE_INTEGER nN
// Get current time tick.
QueryPerformanceCounter(&nNow);
// If it's the time to draw next frame, draw it, else sleep a while.
if (nNow.QuadPart - m_nLast.QuadPart & m_nAnimationInterval.QuadPart)
m_nLast.QuadPart = nNow.QuadP
CCDirector::sharedDirector()-&mainLoop();
& & & &OK,这样我们就可以在外部来调用Cocos2d-x的显示设备进行渲染处理了。当然,我们也不能忘了在CCocos2dXWin窗口销毁时把定时器资源进行一下释放。找到类消息WM_DESTROY新增函数OnDestroy。 & & & & & & & & & & & & & & & & & & &&
void CCocos2DXWin::OnDestroy()
//在Cocos2d-x的引擎文件CCEGLView_win32.cpp中CCEGLView::release()会再次发送调用DestroyWindow,所以这里用变量m_bInitCocos2dX做下判断,避免二次销毁
if(TRUE == m_bInitCocos2dX)
//退出将m_bInitCocos2dX设为FALSE
m_bInitCocos2dX = FALSE;
//释放定时器资源
KillTimer(1);
CWnd::OnDestroy();
& & & & &我们在“HelloWorld”的代码分析中可以知道在Cocos2d-x程序退出时先后响应WM_CLOSE和WM_DESTROY。在CCEGLView::WindowProc 可以看到
case WM_CLOSE:
CCDirector::sharedDirector()-&end();
case WM_DESTROY:
PostQuitMessage(0);
& & & & &而CCDirector的end函数并不是立即执行停止,他设置成员变量m_bPurgeDirecotorInNextLoop为true,并在下一帧mainLoop循环时调用purgeDirector()函数进行显示设备和场景的最终释放。所以我们要想在窗口退出时释放Cocos2d-x显示设备和场景,必须做一下相关处理。理解了这一点。其实就很简单了。
& & & & 只需要在CCocos2DXWin::OnDestroy()中增加两句代码即可:
void CCocos2DXWin::OnDestroy()
KillTimer(1);
//调用显示设备单件实例对象的end函数
CCDirector::sharedDirector()-&end();
//处理一次下一帧,必须调用.
CCDirector::sharedDirector()-&mainLoop();
CWnd::OnDestroy();
& & & &OK,CCocos2DXWin类基本完成。现在在Cocos2dXEditorDlg.h中将
m_Cocos2dXW
& & & 改为
CCocos2DXWin
m_Cocos2dXW
& & &将CCocos2DXWin的头文件包含进来,然后在控件初始化(比如OnInitDialog函数)中调用一下m_Cocos2dXWin.CreateMainCoco2dWindow()。编译成功后就算完成了将Cocos2d-x嵌入MFC的CWnd控件的过程。不过,您需要在窗口大小变化时对这个控件重设位置及大小以使窗口始终上下充满空间。这些属于MFC的基础知识,在此不再赘述。
& & & &最后运行一下看下成果吧。下面是我运行的结果(:左边面板稍加制做~):
原文链接:  众所周知,Cocos2d-x是一款不错的开源引擎,但是在Cocos2d-x中直接使用中文是无法正确显示的。比如下面的情况:&
& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & & &&
  解决这个问题常用的有三种方法:1.通过转换为UTF-8编码来显示。2.使用iconv库来解决。3。使用解析xml或者json文件来解决。这里推荐使用第三种方法来解决,因为比较简单方便,还能支持I18N国际化。下面我们来一一详细了解一下每种解决方案的具体实现:
一、通过转换为UTF-8编码的方式显示出来,建立一个方法用于转换中文到对应的UTF-8编码,具体代码如下:
1 char *HelloWorld::FontToUTF8(const char* font){
int len = MultiByteToWideChar(CP_ACP,0,font,-1,NULL,0);
wchar_t *wstr = new wchar_t[len+1];
memset(wstr,0,len+1);
MultiByteToWideChar(CP_ACP,0,font,-1,wstr,len);
len = WideCharToMultiByte(CP_UTF8,0,wstr,-1,NULL,0,NULL,NULL);
char *str = new char[len+1];
memset(str,0,len+1);
WideCharToMultiByte(CP_UTF8,0,wstr,-1,str,len,NULL,NULL);
if(wstr)delete[]
函数的使用方法如下:
1 auto label = Label::creat(HelloWorld::FontToUTF8("不如跳舞"),"Arial",36);
2 label-&setPosition(320,300);
3 this-&addChild(label);
这种方法仅仅依靠代码实现而不不需要额外的设置,但是效果并不是很稳定,容易产生某些错误或者依然乱码,因此不推荐该方法。
二、使用iconv库来显示中文
首先在创建好的cocos2d-x项目中搜索包含了iconv的文件名,然后把文件iconv.h的路径引入,如:
"cocos2d\external\win32-specific\icon\include\iconv.h"
然后在项目中创建一个GBKToUTF8方法来实现编码的转换:
1 int HelloWorld::GBKToUTF8(std::string &gbkStr,const char* toCode,const char* formCode){
iconv_t iconvH;
iconvH = iconv_open(formCode,toCode);
if(iconvH == 0){
return -1;
const char* strChar = gbkStr.c_str();
const char** pin = &strC
size_t strLength = gbkStr.length();
char *outbuf = (char*) malloc(strLength*4);
char *pBuff =
memset(outbuf,0,strLength*4);
size_t outLength = strLength*4;
if(-1 == iconv(iconvH,pin,&strLength,&outbuf,&outLength)){
iconv_close(iconvH);
return -1;
gbkStr = pB
iconv_close(iconvH);
函数的使用方法如下:
1 //在场景中加入中文标签
2 std::string hello = "不如跳舞";
3 GBKToUTF8(hello,"gb2312","utf-8");
4 CCLabelTTF *label1 = CCLabelTTF::create(hello.c_str(),"Arial",20);
5 label-&setPosition(320,250);
6 this-&addChild(label);
不过Android平台上的Cocos2d-x并没有提供相应的库,这就需要开发者自行将iconv库引入到项目中,然后编译。
由于前两种方法都比较复杂而且不稳定,因此推荐使用第三种方法(读取xml文件或者json文件的方法显示中文)。
三、读取外部xml或者Json文件显示中文
  1.xml方法
  使用xml解析中文十分的简单,首先我们需要准备一个xml文件。如下所示: 
&key&string1&/key&
&string&欢迎学习cocos2dx&/string&
&key&string2&/key&
&string&使用XML显示中文&/string&
&key&string3&/key&
&string&key与string标签中的内容是对应的&/string&
  在xml文件中,&key&和&string&两个标签要保持一一对应的关系。这样就可以通过对key标签中的内容索引到对应的中文内容,从而显示中文字符。
  需要注意的是保存此xml文件的时候编码格式要使用UTF-8(可以使用notepad++或者sublimeText之类的软件完成)。
  之后我们就可以解析xml文件来读取中文啦:
//创建词典类实例,将xml文件加载到词典中
auto *chnStrings = Dictionary::createWithContentsOfFile("CHN_Strings.xml");
//通过xml文件中的key获取value
const char *str1 = ((String*)chnStrings-&objectForKey("string1"))-&getCString();
//创建一个标签显示出来
auto* label1 = Label::create(str1, "Arial", 36);
label1-&setPosition(320,270);
addChild(label1);
const char *str2 = ((String*)chnStrings-&objectForKey("string2"))-&getCString();
auto* label2 = Label::create(str2, "Arial", 36);
label2-&setPosition(320, 180);
addChild(label2);
const char *str3 = ((String*)chnStrings-&objectForKey("string3"))-&getCString();
auto* label3 = Label::create(str3, "Arial", 36);
label3-&setPosition(320, 90);
addChild(label3);
  这样中文就可以正确的显示出来了。
  2.Json方法
&  Json的方法和xml大同小异,而且更简单,首先我们同样准备一个json文件,命名为language.json:
"start":"开始游戏",
"setting":"设置",
"exitGame":"退出游戏",
"info":"马三小伙儿",
"restart":"重新开始",
"win":"游戏胜利",
"returnMenu":"返回主菜单",
"bgMusic":"背景音乐",
"effectMusic":"音效音量"
  然后我们使用Jsoncpp库来读取json文件,jsoncpp可以到这里去下载:&。解压之后我们只需要保留其中的src\lib_json和
include\json下的文件,并将其放在项目的Classess\json目录下。或者图省事可以直接下载我这个配置好的&(提取码:40bb)。最后把json库导入到我们的cocos2d-x项目中。如图:
  为了方便使用和修改我们这里创建一个ReadJson类,这个类里面只有一个静态的std::string ReadJson::getString(std::string name)方法,用来读取json信息:
1 std::string ReadJson::getString(std::string name){
std::string data = FileUtils::getInstance()-&getStringFromFile("language.json");
if (reader.parse(data, root, false) == true)
return root[name].asCString();
  一步步解释一下函数中的内容:
  Json::Reader 这个类是用来解析json文件的;
  Json::Value & 这个类代表了json的一段数据;
  FileUtils::getInstance()-&getStringFromFile("language.json")&这个函数用来加载一个json文件,其返回值是一个字符串;
  调用reader(Json::Reader类型)的parse函数开始解析json文件,解析的结果会保存在root对象中(Json::Value类型),读取它的值很简单,就像普通的数组使用索引一样,比如root["id"]。然后还可以根据值的类型进行转换,例如:root["name"].asCString();
  我们直接在程序中直接调用这个类的静态函数就可以方便的读取json中的字段了。比如:
1 //开始游戏按钮
auto start_button = Button::create("button.png");
start_button-&setScale(2);
4 //调用ReadJson的getString()方法
start_button-&setTitleText(ReadJson::getString("start"));
start_button-&setTitleFontName("微软雅黑");
start_button-&setTitleFontSize(16);
start_button-&setPosition(Vec2(visibleSize.width / 2, visibleSize.height*0.7));
this-&addChild(start_button);
  这样中文也可以正常的显示出来了。
  总结上面的三种方法,无疑是第三种方法最方便和稳定了。而且使用解析xml或者json还有另外的优势就可以很方便地支持国际化。在游戏中设置一个语言选择按钮,然后根据选项来决定加载哪个xml或者json文件,这样可以很方便的快捷的切换游戏语言。
作者:马三小伙儿出处:/msxh/p/5588775.html
请尊重别人的劳动成果,让分享成为一种美德,欢迎转载。另外,文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!
阅读(...) 评论()}

我要回帖

更多关于 cocos2d 游戏开发实例 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信