使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

大家好,我是红孩儿.经过一年左右业余时间的持续开发,《红孩儿工具箱》已经初步完成了一些不错的功能,大家可以用它来辅助我们基于Cocos2d-x的游戏开发工作,帮助我们提升工作效率,实现一些复杂的场景和动画效果。好啦,废话不多讲,今天我来讲解一下如何使用红孩儿工具箱制作小游戏-《打地鼠》,GO!

《打地鼠》这个游戏很简单,就是玩家在看到地鼠从土地里钻出来时用挥舞的锤子砸向地鼠就可以了。这个游戏需要做一个简单的场景,然后还需要一些动画,比如地鼠从土里钻出来的动画和锤子落下的动画等,然后还需要有文字显示积分。下面我们来讲解一下如何使用红孩儿工具箱来制做这一切。

一.拼图PLIST生成
     首先我们需要从www.game2z.com  网站,在左上角的 下载红孩儿工具箱最新版 链接处下载最新的0.2.2版本。下载后我们将其解压到自定义的目录。打开后我们可以看到工具箱EXE程序和一个dx9的dll.还有一个资源目录Resource,一个测试设备图片存放目录Dev以及一个配套的SDK目录.红孩儿工具箱只需要这个EXE和对应的这个dll就可以运行在Windows操作系统了.如果你的操作系统中安装了相应的dll,那么也就是说,整个红孩儿工具箱,只需要一个3M多的EXE程序就可以正常运行了.是不是很小巧绿色环保?

下面我们在工具箱所在目录的资源目录Resource中新建立一个自已的项目资源目录DaDiShu。然后我们从Cocos2d-x的HelloLua工程资源里找到一些农场的图片,还有沙地和植物的图片,把它们拷贝到DaDiShu目录。这将是构成我们场景的重要资源。

我们将它们拷到DaDiShu下。然后启动工具箱,这时我们可以看到红孩儿工具箱的登录界面,我们一般查看可以选择离线登录,但离线登录不能导出动画和场景,主要是用于查看和编辑。如果是想导出,那就最好到www.game2z.com论坛注册一个正式用户的账号。在这里我们输入注册账号和密码后,点击“登录”,稍等片刻完成验证后即可进入工具箱主界面。

我们首先要做的,是先设置一下我们的工程资源目录,点击“设置工程资源目录”按钮,选择我们的资源目录即可,一旦设置完成,工具箱就会开始遍历和登记工程目录下所有的文件资源,这样我们就可以使用工程资源目录下的图片了.下次再使用工具箱时也会自动的设置好资源目录.

我们首先想做的是把crop.png图片进行碎图PLIST生成。Cocos2d-x是可以识别碎图PLIST文件的,工具箱也可以识别带有PLIST文件的PNG图片来使用碎图设置精灵,我们在工具箱主界面的第一个功能编辑页“拼图/切图”中就可以完成这个需求,“拼图/切图”页默认的是“拼图”导出面板,我们点击一下工具箱全局配置信息下面的“切图/拼图-[拼图”复选框,现在切换为“切图”编辑面板。这时候就可以将图片进行切图操作了。

我们点击“选择图片文件”打开crop.png图片,这时候在工具箱的左边图片元件树控件就会显示出crop这个图片树项,同时在视图窗口中会显示这张图片。如图:

我们在左边的crop树项上右键,可以弹出菜单,这时候我们选择一下“按行列数切块”,在弹出的对话框中输入块名称crop,行数填1,列数填4即可,也可以在弹出菜单中选择“固定大小切块”,在弹出的对话框中输入切块宽高和间距边距,确定后我们可以看到在crop树项下生成了相应的图块。这时我们可以选择各树项进行查看,也可以在图片上直接点击显示图块项。

我们可以看到相应的图块会显示出调整矩形编辑框,这种方式是可以让我们通过鼠标点中编辑框结点来修改图块的大小和位置的。不过在这里使用行列数切块就不需要调整了,如果小伙伴们有自已的需求,也可以通过对图片项弹出菜单中的“加入新图块”来手动进行图块的切分。

我们在编辑好图块后,确认一下图片导出的名称和目录,点击“立即导出”,这时工具箱就可以为我们的图片crop生成相应的PLIST。如图:

生成的文件:

在工具箱后面的场景制做中,我们就可以使用这个PLIST来进行各状态植物图片的摆放。

二。场景编辑

现在我们切换到场景编辑页中,如图:

我们点击“创建”,来新建一个地图,这个《打地鼠》游戏场景不需要滚屏,只有一张背景图farm.jpg大小就可以了,在这里我们输入场景名称farm,场景宽800,高800。然后确定,就创建了场景,我们可以看到主视窗里场景大小的一个边框,我们的场景就是要放在这里面的。如图:

好,我们现在来规划一下这个场景的分层。我们可以将这个场景分为三层,第一层是农场背景,第二层是沙地格子,第三层是植被。这样将沙地格子放在农场背景中,植被放在沙地格子中,就可以实现一个农场的场景了。下面我们来实现一下。

在右边的面板下部右键弹出菜单,选择“新增地图层”,然后会弹出一个地图层设置的对话框:

我们计划第一层只是放一个农场图片,所以没有必要考虑格子,输入层的名称“BK”后确定即可,之后会增加一个图层页BK,这个图层页中有一个空白树控件,是用来管理放在这一层的各种资源,比如图片,动画,粒子等。

我们在这个图层页中资源树区域右键点击,选中弹出菜单“增加新图类”项,选中后会弹出一个图片分类对话框,输入图片类名称“farm”,然后就会有一个图片类树项显示出来,我们可以在这个分类项上点击右键,在弹出菜单里我们可以看到很多可以增加的资源选项,我们在这里只加入一张背景图,所以选择“增加新图片”。

在弹出的文件选择对话框中我们找到farm.jpg,然后确定,就可以将农场背景图加入到这一层的资源树中了。需要注意的是在浏览框中文件类型我们要选择jpg格式.因为这个图片是jpg的.

我们点击图片项,可以看到在右上角的显示窗口中显示出这个资源项,然后我们把鼠标移动到主视窗中场景左上角附近点击一下,这时农场的背景图就被放入到场景中了。

右键取消选中的资源,然后我们点选场景中的这个背景图片,在它被选中后我们在右边面板中点击“物件编辑”切换到物件的位置输入面板,输入位置0,0,就可以使其完全与场景框贴合在一起,这样我们第一层背景图就编辑好了。[为了方便查看,这里我按着Ctrl键的同时用鼠标滚轮缩放视图到合适大小.

下面我们来创建第二层沙土格子的图层,我们在地图层选项页右边空白处右键,在弹出的菜单中选择“新增地图层”:

之后我们可以看到建立新图层的对话框,我们现在要考虑的是对于沙土格子land.png这张图片,他是要基于网格进行拼合的,而它本身是有斜度的。对于有斜度的网格,我们要选择斜视角的观察方式,在这里我们输入格子的宽度为lan.png的宽高182x94,之后输入网格的位置偏移,这个位置偏移就是以场景左上角为起点向右下进行偏移的距离,结果位置将作为第一个格子(0,0)的起点。这个起点在普通观察方式下就是格子的左上角,在斜视角的观察方式下则是格子左角。我们在这里可以输入X偏移30,Y偏移440,然后设置格子的行数为5,列数为3,点击确定后这一层就创建成功了,我们可以选中视图区左上角的“显示格子”看到在场景的相对偏移位置作为起点显示出了5行3列的斜视角网格。如图:

如果我们觉得格子大小或位置不合适,也可以在相应的图层页项上右键点击,在弹出菜单里点击“修改当前图层”进行反复修改:

经过反复确认后,偏移位置x设为30,y设为460比较好,能够比较准确的将5行3列的格子放置在土地区域。

好,我们在这一层的资源树控件中按照之前的方式增加图片类“land”,然后把land.png加入进来选中它,这时候我们就可以在相应的格子上刷图了,我们将鼠标在选中land.png的状态下移到场景中,如果我们选中了视图区左上角的“显示格子”复选框,那么在相应的格子上点击,就会自动在格子中刷上相应的图片。如果我们按下Ctrl键的同时从第1个格子向最后一个格子拖动那么将直接进行区域刷格子。如图:

这样,第二层的沙土格子层我们就创建出来了。现在我们要继续创建植物层,植物层不需要格子,所以和第一层背景的建立方式是一样的,在右边面板选项空白处右键,在弹出的菜单中选择“新增地图层”进入到建立新图层的对话框,输入“OBJ”直接确定就可以。下面我们将crop.png加入到这一层的资源树中,这时我们可以看到它会自动的识别所有的图块。我们可以点击图片crop.png来放到场景中,也可以选中其下的crop_0~4来放入到场景中,在这里我们随便选一些图块放到场景沙土地的相应位置上如图:

现在我们第三层也就做好了,是不是很简单?

最后我们来生成一些事件点来创建地鼠。因为地鼠是从沙土地上生成出来的,所以这些事件点也就放在格子中,我们在右边面板切换到第二层沙土格子层。在图类上右键,选择弹出菜单项“增加事件格”,取中视图区左上角的“显示格子”复选框,同时为了方便查看,我们可以选择显示方式为“高亮当前层”项,这时候只有当前层的元素是高亮显示的。然后我们在场景沙土地的一些位置点击,把创建地鼠的事件格子编辑好后,切换为“显示所有层”,整个场景就算完成了。如图:

三.动画制作

首先我从网上找到老鼠,锤子,金币的图片并将它们用photoshop制做成动画序列图。

图一:地鼠钻出来的序列图

图二:锤子落下的序列图

图三:金币闪现的序列图

好了,现在我们切换到“切图”面板,然后按照之前crop.png的切图方式来为它们生成相应的plist.

现在这些序列图已经有图块Plist了,我们开始为它们制做相就原动画.打开动画编辑面板,我们开始制做相应的序列帧动画.首先我们要建立一个图片结点.图片结点本身就是一个独立的精灵,可以表现动画.我们在左边的结点树控件的Root结点上右键单击,在弹出的菜单中选择“增加图片结点”:

工具箱在图片结点和骨骼结点的显示图标是可以自定义的,如果没有定义,此时会弹出提示未设置图片结点默认图标的消息框,确定后会弹出相应的设置对话框,这时我们可以选择Resource下的cocos2d-x.png和Bone.png来作为图片结点和骨骼结点的默认图标.

确定后就会出现cocos2d-x.png来显示在视窗的中心点了.

这时我们在这个图片结点项上右键,在弹出菜单中选择“生成固定帧间隔关键帧动画”就可以为这个图片结点弹出一个设置序列帧动画的对话框.

在“创建固定帧间隔的关键帧动画”对话框中,有两种图片源选取方式,第一种是使用图片序列,就是一般多张图片如attack_0.png,attack_1.png~attack10.png这种多张同名使用不同数字后缀的图片进行生成,因为我们的图片是拼在一起的,所以这里我们选中“使用拼合图内图块序列生成”的方式.之后点击“查找图块”,这时会弹出一个新面板.

这个新面板是做什么用的呢?它就是图片及动画资源库面板.在这里我们可以点击“导入资源文件到当前资源库中”这个按钮来将我们需要的图片加入.如果有时候我们编辑的时候修改了图片,那也可以在这里随时点击刷新资源文件进行重新加载.

我们现在将锤子图片导入进来.并选中任意一个图块:

这时候我们可以看到图片和图块都显示正确,然后我们点击“应用此图块”,这时候资源库对话框就消失了,我们会到之前的“创建固定帧间隔的关键帧动画”对话框中.可以看到图块起始为0,图块结束自动变为了2,我们把动画的间隔时间还使用默认的100毫秒,点击确定后,我们可以看到在动画编辑面板下部的帧显示区有3帧被设为了关键帧我们点击他们可以在主视窗中就显示出相应帧的动画了.

我们可以在下面的帧显示区右部分点击“播放动画”复选框来直接查看动画的播放.并且可以手动随时调节帧间隔的时间来改变动画的速度.

这样,这个锤子砸下来的动画就做好了.我们保存一下.在弹出的保存文件对话框里我们可以输入attack,格式仍为ani,确定后会再弹出一个修改动画名称的对话框,我们仍然输入attack后确定.这个动画就被保存为attack.ani文件了.同时会导出一个attack.plist.这个plist我们就可以在cocos2d-x中进行播放了.

按照上面锤子动画的制做方式我们继续完成老鼠出生的动画和弹出金币的动画,它们对应的图片为laoshu.png和jinbi.png,动画名称可以分别设为borth和goal.导出的动画也就分别是borth.plist和goal.plist.

现在三个动画和三个序列帧的PLIST就都导出了.我们就把游戏中的场景和动画制做完成了.

四.字图制作

我们现在还需要一些文字来显示游戏名称和得分.我想做成彩色的文字显示在游戏画面合适的位置.这要用到Cocos2d-x支持的字图.即使用png+fnt的方式显示字库,其中png图片存储文字的显示结果,fnt存储文字的位置信息.强大的红孩儿工具箱也提供了字图的编辑和导出功能.我们来试一下.

我们现在切换到“字体编辑”面板,然后我们在文档信息框里删除原有文字后输入“打地鼠0123456789”,接下来我们选择自已想要的字体,这里我选的是华文琥珀,字大小为36.确定后我们可以看到文档信息框里文字变成了相应的字体.

这时我们点击“开始生成”,即可以很快看到生成出来的字图:

这黑黑的文字不是我们想要的,现在我们来进行一下美化,点击右上角“编辑文字效果”按钮切换到编辑文字效果的面板。

这时我们可以选择字体颜色,调节字的间距,以及选一种自已喜欢的阴影方式。之后我们还可以在图片着色部分中加载一张过渡图片来进行着色.比如这里我们使用font_effect1.png来进行单字应用的叠加着色.同时字体用红,阴影用黑,右下描边,阴影粗细填5,间距为1,一个五彩的字图就出现了:

感觉玩的差不多了,可以点击导出fnt及png字图保存为text.png和text.fnt.这样我们的游戏就可以使用这个字图来进行游戏名称的显示和积分的显示了.

五.粒子制作

最后,我还想在农场里加入一个雪花落下的粒子效果,这样可以更加增加场景的气氛感。我们切换到工具箱的“粒子编辑”面板。在这里我们可以看到在主视窗中有一个粒子效果。右边面板有一个可以选择的粒子模版库的下拉列表和一些属性值编辑区域

在粒子模版库中放置了一些Cocos2d-x可以支持的粒子系统的样式。我们在这里要做一个下雪的效果,所以直接选择snow就可以了。

我们现在能看到下雪效果的粒子系统了,但是它的粒子是没有贴图的,还只是渐变色块,我们点击“更改图片”就可以找一个球体的图片来做为雪花的贴图。

这样漂亮的雪点的效果就出现了。

我们可以根据需要作改一些属性参数。比如横向的粒子系统宽度和起始颜色。

最后,我们点击“保存”,可以将其保存成snow.plist放在资源目录。

下雪的粒子系统我们就算做好了,下面再切换到场景编辑部分把下雪的粒子系统加入到场景中。直接在第三个图层下的图类项上右键,在弹出菜单中选择“增加粒子系统”,之后将snow.plist导入就可以了。

最后保存,并导出地图的XML就完成了场景中的下雪效果

这样我们就把工具处理部分就算完成了,我们下面进入到代码的编写部分.

六.代码编写

为了方便工程的建立,我们可以直接将Cocos2d-x中的HelloCpp工程目录复制一份出来,然后修改工程名,之后将工具箱附带SDK中的C++目录中的Map子目录中的代码拷贝到Classes中。

然后我们打开工程,将相应文件加入到工程中

现在我来介绍一下工具箱开源的场景代码,除了解析xml而加入的tinyxml之外,这里主要就是两个类文件

HHRMapLayer.h/cppHHRMapReader.h/cpp,它们分别是显示场景和解析场景文件的类。这两个类文件可以开源的哦~,大家学习之后就可以自行进行扩展并进行二次开发了。

CHHRMapReader 是用于工具箱的地图XML文件的解析,支持多层场景,任意网格设置,摆放图片和粒子系统,设置事件点,路径点。我们可以用工具箱来制做场景,导出成XML格式后用它来进行解析。

头文件代码:

#ifndef _HHRMAPREADER_H#define _HHRMAPREADER_H//==================================================================////File:        HHRMapReaer.h//Date:        2013-09-05//Auto:        Honghaier
//Blog:        http://blog.csdn.net/honghaier//Web:        http://www.game2z.com//Desc:        用于红孩儿工具箱的地图解析,目前只支持图片和粒子系统//==================================================================//#include "cocos2d.h"#include "ccTypes.h"#include <stdio.h>#include <string>#include <vector>using namespace std;using namespace cocos2d;#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)//ANDROID平台上用的,用于位置struct POINT{int         x;int  y;};#define _MAX_PATH        256#endif#define BREAK_IF(a)                if(!a)break;//地图头信息struct stMapHeader{int                m_nVersion;                                                        //版本号string        m_strMapName;                                                //场景名称int                m_nSceneWidth;                                                //场景宽度int                m_nSceneHeight;                                                //场景高度int                m_nLayerNum;                                                //场景层数};//元素类型enum        enuElementType{RET_NONE         = 0,        //什么也没有RET_IMAGE         = 1,        //静态图RET_ANI                 = 2,        //动画RET_PARTICLE = 3,        //粒子效果RET_EFFECT         = 4,        //效果RET_NPC                 = 5,        //角色RET_OBSTRUCT = 6,        //阻挡格RET_EVENT         = 7,   //事件格};//精灵扩展类class  CCSpriteEx        :public CCSprite{public://取得四边形顶点指针inline ccV3F_C4B_T2F_Quad* getQuadPtr(void) { return &m_sQuad; }        };//粒子系统扩展类class        CCParticleSystemQuad_Ex        :public CCParticleSystemQuad{public://取得PLISTconst char*         getPListPath(){return m_sPlistFile.c_str();}//打开相应的粒子PLIST文件bool  LoadParticleInfoFromPListFile(const char* szPListFile){return initWithFile(szPListFile);}};//资源信息struct stMapResInfo{enuElementType                                m_Type;                                                //类型string                                                m_strFileName;                                //文件名(图片名,动画PLIST名或粒子系统PLIST名)string                                                m_strBlockName;                                //图块名CCSpriteEx*                                        m_pSprite;                                        //精灵CCParticleSystemQuad_Ex*        m_pParticleSystem;                        //粒子系统};//批次结点处理struct stBatchNodeInfo{string                                m_strFileName;                //文件名(图片名)CCSpriteBatchNode*        m_pBatchNode;};//视角enum enuViewType{VT_NORMAL                = 0,        //普通VT_SKEW                        = 1,        //斜视角VT_FREE                        = 2,        //不使用格子};//自由物体struct stFreeObj{stMapResInfo*        m_pResInfo;                                //对应的资源索引CCSprite*                m_pSprite;                                //排序时加入的精灵float                        m_fPosX;                                //X位置float                        m_fPosY;                                //Y位置float                        m_fLowerHeight;                        //最低位置(用于排序)};//路径struct stPathNode{bool                        m_bIsTile;                                //是否是格子POINT                        m_sSrcTile;                                //起点格子,如果是格子,则为格子位置,否则为像素位置POINT                        m_sDestTile;                        //终点格子,如果是格子,则为格子位置,否则为像素位置};//事件struct stEventTile{int                                m_nEventID;                                //事件IDbool                        m_bIsTile;                                //是否是格子POINT                        m_sTile;                                //如果是格子,则为格子位置,否则为像素位置};//层结构struct stMapLayerInfo{        enuViewType                        m_ViewType;                        //视角int                                        m_nTileWidth;                //格子宽度int                                        m_nTileHeight;                //格子高度int                                        m_nTileRows;                //格子行数int                                        m_nTileCows;                //格子列数int                                        m_nStartPosX;                //横向格子起点位置int                                        m_nStartPosY;                //纵向格子起点位置stMapResInfo**                m_pTileResArray;        //格子对应资源索引vector<stFreeObj>        m_ObjectVec;                //自由物体容器int                                        m_ObjectNum;                //自由物体数量vector<stPathNode>        m_PathNodeVec;                //路径容器int                                        m_PathNodeNum;                //路径点数量vector<stEventTile> m_EventTileVec;                //事件容器int                                        m_EventTileNum;                //事件点数量};//地图解析类class CHHRMapReader{public://构造CHHRMapReader();//析构~CHHRMapReader();public://从文件中载入地图bool                        LoadDataFromFile(const char* szFileName);//从内存中载入地图bool                        LoadDataFromData(const char* szData);//清空void                        CleanUp();public://取得场景头信息stMapHeader*        GetMapHeader();//取得资源数量int                                GetResInfoNum();//取得资源指针stMapResInfo*        GetResInfoPtr(int vIndex);//取得层数量int                                GetLayerInfoNum();//取得层信息指针stMapLayerInfo*        GetLayerInfoPtr(int vLayerIndex);//取得相应格子的资源数据stMapResInfo*        GetTileResData(int vLayerIndex,int vTileX,int vTileY);//取得对应层的自由物体数量int                                GetLayerFreeObjNum(int vLayerIndex);//取得对应层的自由物体stFreeObj*                GetLayerFreeObj(int vLayerIndex,int vObjIndex);//取得路径数量int                                GetLayerPathNodeNum(int vLayerIndex);//取得对应层的制定路径stPathNode*                GetLayerPathNode(int vLayerIndex,int vPathNodeIndex);//取得指定格子是分支路径起点的分支路径数量int                                GetNextPathNodeNum(int vLayerIndex,int vTileX,int vTileY,vector<int>& vNextPathNodeVec);//取得事件点数量int                                GetLayerEventTileNum(int vLayerIndex);//取得对应层的事件点stEventTile*        GetLayerEventTile(int vLayerIndex,int vEventTileIndex);private://头信息stMapHeader                                                m_MapHeader;//资源信息容器vector<stMapResInfo>                        m_MapResInfoVec;//地图层信息容器vector<stMapLayerInfo>                        m_MapLayerInfoVec;//批次结点信息vector<stBatchNodeInfo>                        m_SpriteBatchNode;};#endif

从这个文件里我们可以看到CHHRMapReader能够从XML场景信息文件中读取场景信息,层数,网格信息,自由物体信息,路径信息,以及事件点信息,很多信息都是基于图层索引来获取的,这样是为了适应工具箱的多层随意网格的特性。

CHHRMapLayer是用于使用Cocos2d-x进行场景显示的类。他本身就可以显示出XML地图了。

#ifndef _HHRMAPLAYER_H#define _HHRMAPLAYER_H//==================================================================////File:        HHRMapLayer.h//Date:        2013-09-05//Auto:        Honghaier
//Blog:        http://blog.csdn.net/honghaier//Web:        http://www.game2z.com//Desc:        用于红孩儿工具箱的地图显示,目前只支持图片和粒子系统//==================================================================//#include "HHRMapReader.h"using namespace std;USING_NS_CC;//场景的精灵struct stHHRMapSprite{CCSprite*                                m_Sprite;CCPoint                                        m_sPosition;                        //位置};//场景的精灵层struct stHHRMapSpriteLayer{vector<stHHRMapSprite*>        m_MapSpriteVec;                        //精灵};//红孩儿工具箱的地图层显示class CHHRMapLayer        :public        CCLayer{public://构造CHHRMapLayer();//析构~CHHRMapLayer();public://加载场景XML文件bool                                LoadHHRMap(const char* szFileName);//地图读取器CHHRMapReader*                GetMapRender();//设置观察点void                                SetCameraPos(float fX,float fY);//取得摄像机的当前位置CCPoint                                GetCameraPosition();//移动摄像机的位置void                                MoveCamera(float vOffsetX,float vOffsetY);//通过位置取得格子POINT                                GetClickTile(int vLayerIndex,int vPosX,int vPosY);//通过格子取得位置(格子中心点位置)CCPoint                                GetTileCenterPos(int vLayerIndex,int vTileX,int vTileY,bool bIsScreenPos = true);//放入一个新的精灵到一个格子上stHHRMapSprite*                AddSpriteToTile(CCSprite*        pSprite,int vShowIndex,int vLayerIndex,int vTileX,int vTileY);//放入一个新的精灵到场景中stHHRMapSprite*                AddSpriteToMap(CCSprite*        pSprite,int vShowIndex,int vPosX,int vPosY);//开启精灵的动画播放void                                RunSpriteAction(CCSprite*        pSprite);//取得放入的精灵数量int                                        GetSpriteNum(int vLayerIndex);//取得指定的精灵stHHRMapSprite*                GetSprite(int vLayerIndex,int vSpriteIndex);//删除指定的精灵void                                DelSprite(CCSprite*        pSprite);//删除所有的精灵void                                DelAllSprites();//设置使用按下拖放void                                SetTouchDrag(bool bTouchDrag);public://渲染virtual void                draw(void);//加载时调用virtual void                onEnter();//释放时调用virtual void        onExit();//响应触屏事件处理virtual void                ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);virtual void                ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);virtual void                ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);virtual CCObject*        copyWithZone(CCZone *pZone);virtual void                touchDelegateRetain();virtual void                touchDelegateRelease();private://更新场景void                                UpdateScene();//渲染指定格子void                                RenderTile(int vLayerIndex,int vTileX,int vTileY,ccColor4B color);//渲染对应的图素void                                RenderElement(enuViewType        vCameraType,CCSize        vTileSize,stMapResInfo*        pRenderInfo,float vPosX,float vPosY,float vPosZ);private://地图读取器CHHRMapReader                                m_MapReader;//当前放在场景层中的精灵vector<stHHRMapSpriteLayer>        m_SpriteLayerVec;//当前观察点CCPoint                                                m_sCameraPos;//按下时记录上一个点的位置CCPoint                                                m_sTouchLastPos;//是否响应拖动处理bool                                                m_bTouchDrag;//上次更新的时间                double                                                m_dwLastTime;//错误返回字符串string                                                m_ErrorStr ;}        ;#endif

从这个代码中我们可以看到CHHRMapLayer就是一个CCLayer,它拥有CCLayer的特性,可以直接放在场景中进行显示。它还可以进行进行格子的获取以及将一个精灵放置在格子上。

我们这现在就使用这两个类来进行《打地鼠》的制做。

我们打开HelloWorldScene.h,在这里引入”HHRMapLayer.h”,并为HelloWorld创建一个地图显示层的成员指针用于实例化这个地图显示层。并加入基于鼠标位置显示在屏幕上的锤子精灵以及显示分数的字图文字标签。

#ifndef __HELLOWORLD_SCENE_H__#define __HELLOWORLD_SCENE_H__#include "cocos2d.h"#include "HHRMapLayer.h"class HelloWorld : public cocos2d::CCLayer{public:// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphonevirtual bool init();  // there's no 'id' in cpp, so we recommend returning the class instance pointerstatic cocos2d::CCScene* scene();// a selector callbackvoid menuCloseCallback(CCObject* pSender);//老鼠出生的回调函数void LaoShuBorth(float dt);//渲染virtual void                draw(void);//响应触屏事件处理virtual void                ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);//删除精灵void                                DelSpriteFunc(CCNode* pTarget);protected://地图显示层CHHRMapLayer*                m_pHHRMapLayer;                        //锤子CCSprite*                        m_pChuiZiSprite;//计分器CCLabelBMFont*                m_pCountLabel;//计数int                                        m_nAttackMouseCount;CREATE_FUNC(HelloWorld);};#endif // __HELLOWORLD_SCENE_H__

然后我们打开 HelloWorldScene.cpp,在 HelloWorld::init函数中加载地图并创建相应的精灵

// on "init" you need to initialize your instancebool HelloWorld::init(){//// 1. super init firstif ( !CCLayer::init() ){return false;}CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();/// 2. add a menu item with "X" image, which is clicked to quit the program//    you may modify it.// add a "close" icon to exit the progress. it's an autorelease objectCCMenuItemImage *pCloseItem = CCMenuItemImage::create("CloseNormal.png","CloseSelected.png",this,menu_selector(HelloWorld::menuCloseCallback));pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 ,origin.y + pCloseItem->getContentSize().height/2));// create menu, it's an autorelease objectCCMenu* pMenu = CCMenu::create(pCloseItem, NULL);pMenu->setPosition(CCPointZero);this->addChild(pMenu, 1);//因为我们要显示的文字是汉字,所以为了避免乱码,我们在这里将文字存入到XML中,然后在Cocos2d-x中读取。CCDictionary *strings = CCDictionary::createWithContentsOfFile("string.plist"); const char *title = ((CCString*)strings->objectForKey("title"))->m_sString.c_str(); CCLabelBMFont* pLable = CCLabelBMFont::create(title, "text.fnt");pLable->setPosition(ccp(visibleSize.width - 120, visibleSize.height - 100));this->addChild(pLable,1);//显示计数m_pCountLabel = CCLabelBMFont::create("0", "text.fnt");m_pCountLabel->setPosition(ccp(visibleSize.width - 120, visibleSize.height - 200));this->addChild(m_pCountLabel,1);m_nAttackMouseCount = 0;//-===载入地图=========================================m_pHHRMapLayer = new CHHRMapLayer();if(true == m_pHHRMapLayer->LoadHHRMap("park.xml")){m_pHHRMapLayer->autorelease();addChild(m_pHHRMapLayer,0,0);m_pHHRMapLayer->SetTouchDrag(false);}else{delete m_pHHRMapLayer ;m_pHHRMapLayer = NULL;}CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache();//锤子的拼图frameCache->addSpriteFramesWithFile("chuizi.plist");//老鼠的拼图frameCache->addSpriteFramesWithFile("laoshu.plist");//金币的拼图frameCache->addSpriteFramesWithFile("jinbi.plist");CCAnimationCache::purgeSharedAnimationCache();CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();//老鼠出现的动画animCache->addAnimationsWithFile("dishu_borth.plist");//锤子击打的动画animCache->addAnimationsWithFile("attack.plist");//金币弹出奖励的动画animCache->addAnimationsWithFile("jiangli.plist");//锤子m_pChuiZiSprite = CCSprite::create();CCSpriteFrame *frame = frameCache->spriteFrameByName("ChuiZi_0");m_pChuiZiSprite->setDisplayFrame(frame);m_pChuiZiSprite->setAnchorPoint(ccp(0.8,0.2));addChild(m_pChuiZiSprite);//每2秒出现一只老鼠schedule(schedule_selector(HelloWorld::LaoShuBorth), 2.0f);srand(time(0));//设置可以触屏响应setTouchEnabled(true);SimpleAudioEngine::sharedEngine()->preloadEffect("hit.mp3");return true;}

首先是创建了一个显示《打地鼠》标题的文字标签,然后是显示计数的文字标签。在这里我们使用了中文,为了避免因编码显示错乱,我们创建了一个 XML文件 string.plist来使用 UTF8存储文字“打地鼠”并在 Cocos2d-x中使用。

随后我们实例化地图层,并调用其LoadHHRMap来加载一个工具箱导出的XML地图文件。再后面就是将拼图PLIST和动画PLIST加载进来,创建锤子精灵和动画,设定一个定时器,每2秒调用一次回调函数LaoShuBorth,哈哈,我那烂烂的E语。为了更加生动,我们在这里还预加入一个音乐文件hit.map来播放击中老鼠时的音效。

之后我们来编写LaoShuBorth,它每2秒被回调的时候,我们可以创建老鼠的精灵和动画并放入到场景的相应事件点格子位置就可以了。事件点即可以基于格子,也可以基于像素,这个最好在代码中判断一下。

void HelloWorld::LaoShuBorth(float dt){if(m_pHHRMapLayer){CHHRMapReader*        tpMapReader = m_pHHRMapLayer->GetMapRender();if( tpMapReader ){int                nEventTileNum                = tpMapReader->GetLayerEventTileNum(1);int                nEventTileIndex                = rand() % nEventTileNum ;stEventTile*        pEventTile        = tpMapReader->GetLayerEventTile(1,nEventTileIndex);if(pEventTile->m_bIsTile){//取得事件IDint nEventID = pEventTile->m_nEventID ;CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache();CCSprite *dishu = CCSprite::create();CCSpriteFrame *frame = frameCache->spriteFrameByName("dishu_0");dishu->setDisplayFrame(frame);CCSize winSize = CCDirector::sharedDirector()->getWinSize();dishu->setAnchorPoint(ccp(0.5,0.0));m_pHHRMapLayer->AddSpriteToTile(dishu,1,1,pEventTile->m_sTile.x,pEventTile->m_sTile.y);CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();CCAnimation *Borth = animCache->animationByName("borth");Borth->setRestoreOriginalFrame(true);CCAnimate *BorthAni = CCAnimate::create(Borth);// run the animationdishu->runAction( CCSequence::create( BorthAni,  CCCallFuncN::create(this, callfuncN_selector(HelloWorld::DelSpriteFunc)),NULL));m_pHHRMapLayer->RunSpriteAction(dishu);//由场景层管理,不需要放入当前层//addChild(dishu);}}}}

这里要注意的是,如果我们放入一个精灵到地图中,我们调用地图层的 AddSpriteToTile函数,但如果这个精灵本身是个序列帧动画,我们还要在它runAction之后再调用一下地图层的 RunSpriteAction 保证它被更新播放。在给它运行一个动画时,最好放入一个动画序列,在这个动画序列的最后再调用一个删除精灵的回调函数以保证精灵可以在合适的时机释放。

//删除精灵void        HelloWorld::DelSpriteFunc(CCNode* pTarget){if(m_pHHRMapLayer){CCSprite        *pMouseSprite = dynamic_cast<CCSprite*>(pTarget);if(pMouseSprite){m_pHHRMapLayer->DelSprite(pMouseSprite);}}}

这样我们运行一下就可以看到我们前面用工具箱制做的地图了,也可以发现每2秒会从事件点对应的沙地格子上生成一个老鼠。

现在我们希望在鼠标移动时,锤子能跟随移动,怎么做呢?我们需要重载一下HelloWorld的draw函数,并在此中加入获取鼠标位置的代码,然后将鼠标位置设置给锺子。

//渲染void HelloWorld::draw(void){POINT        tCurrPos;::GetCursorPos(&tCurrPos);CCEGLView*        pGELView = CCDirector::sharedDirector()->getOpenGLView();::ScreenToClient(pGELView->getHWnd(),&tCurrPos);if(m_pChuiZiSprite){CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();m_pChuiZiSprite->setPosition(ccp(tCurrPos.x/pGELView->getFrameZoomFactor() ,visibleSize.height - tCurrPos.y/pGELView->getFrameZoomFactor()));}CCLayer::draw();}

下面就只有点击鼠标时,播放锤子打击老鼠时弹出金币,和加分的处理了,在 Cocos2d-x中我们可以重载 HelloWorld的 ccTouchesBegan函数来实现对鼠标按下的响应并设置 HelloWorld层开启触屏事件响应。如果是在手机或 Pad上,那就是我们点击屏幕时的响应函数。

//响应触屏事件处理void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent* event){CCSetIterator iter = pTouches->begin();for (; iter != pTouches->end(); iter++){CCTouch* pTouch = (CCTouch*)(*iter);CCPoint        tClickPt = pTouch->getLocation();CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();CCAnimation *Borth = animCache->animationByName("chuizi");Borth->setRestoreOriginalFrame(true);CCAnimate *AttackAni = CCAnimate::create(Borth);// 运行击打动画m_pChuiZiSprite->runAction(AttackAni);// 点击位置,做一个(100,50)的偏移,将攻击点移到锤子头部位置附近POINT        tCurrPoint;tCurrPoint.x = tClickPt.x ;tCurrPoint.y = tClickPt.y ;CCPoint        tCameraPos = m_pHHRMapLayer->GetCameraPosition();//击中的老鼠int                nLaoShuNum = m_pHHRMapLayer->GetSpriteNum(1);for(int s = 0 ; s < nLaoShuNum ; s++){stHHRMapSprite*                tpHHRMapSprite = m_pHHRMapLayer->GetSprite(1,s);if(tpHHRMapSprite){if(tpHHRMapSprite->m_Sprite){int nScreenPosX = tpHHRMapSprite->m_sPosition.x - tCameraPos.x;int nScreenPosY = tpHHRMapSprite->m_sPosition.y - tCameraPos.y;RECT tClickRect;tClickRect.left                = nScreenPosX ;tClickRect.top                = nScreenPosY ;tClickRect.right        = nScreenPosX + tpHHRMapSprite->m_Sprite->getContentSize().width ;tClickRect.bottom        = nScreenPosY + tpHHRMapSprite->m_Sprite->getContentSize().height;if(PtInRect(&tClickRect,tCurrPoint)){m_pHHRMapLayer->DelSprite(tpHHRMapSprite->m_Sprite);m_nAttackMouseCount++;char szTemp[10];sprintf(szTemp,"%d",m_nAttackMouseCount);m_pCountLabel->setString(szTemp);SimpleAudioEngine::sharedEngine()->playEffect("hit.mp3");//击中的格子POINT        tClickTile = m_pHHRMapLayer->GetClickTile(1,nScreenPosX,nScreenPosY);if(tClickTile.x >= 0 && tClickTile.y >= 0){CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache();CCSprite *jinbi = CCSprite::create();CCSpriteFrame *frame = frameCache->spriteFrameByName("JinBi_0");jinbi->setDisplayFrame(frame);CCSize winSize = CCDirector::sharedDirector()->getWinSize();jinbi->setAnchorPoint(ccp(0.5,0.0));m_pHHRMapLayer->AddSpriteToTile(jinbi,2,1,tClickTile.x,tClickTile.y);//跳金币动画CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();CCAnimation *pAnimation = animCache->animationByName("jiangli");Borth->setRestoreOriginalFrame(true);CCAnimate *JiangLiAni = CCAnimate::create(pAnimation);//渐隐消失动画CCActionInterval*  action1 = CCFadeOut::create(1.0f);//运行动画序列,先跳金币后消失jinbi->runAction( CCSequence::create( JiangLiAni, action1,  CCCallFuncN::create(this, callfuncN_selector(HelloWorld::DelSpriteFunc)),NULL));//设置这个精灵运行动画m_pHHRMapLayer->RunSpriteAction(jinbi);}break;}}}}}}        

这里面的代码较多,主要流程就是先获取到鼠标点位置,然后遍历场景中的所有地鼠的精灵,之后通过点与矩形的位置判断来确定是否击中地鼠。如果击中,增加分数并播放地鼠的精灵并播放击中的音效。同时在相应的格子上创建金币的精灵动画并播放。

好了,这样《打地鼠》游戏就算完成了,运行一下,嘿嘿,还不错嘛~

结束语

谢谢大家的对工具箱的支持和关注,我会继续完善工具箱并编写更多的游戏实例。敬请期待!

另外有任何关于工具箱的问题可以到http://www.game2z.com工具箱专版与我进行交流!

我的新浪微博:http://weibo.com/u/1834515945 工具箱用户群:20970366

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏相关推荐

  1. 使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

    大家好,我是红孩儿.上一节我们学习了使用<红孩儿工具箱>开发<打地鼠>游戏.这一节我们继续学习使用<红孩儿工具箱>来开发<坦克大战>游戏. <坦克 ...

  2. 基于C#实现的坦克大战游戏的最短路

    1 项目概述 1.1 项目背景 <坦克大战>(Battle City)是1985年日本南梦宫Namco 游戏公司开发并且在任天堂FC平上,推出的一款多方位平面射击游戏.游戏以坦克战斗及保卫 ...

  3. 基于java swing的坦克大战游戏

    一.功能描述 java坦克大战主要功能: 1. 图形用户界面: 2. 有坦克.森林.河流.墙体等元素: 3. 界面中有一个我方大本营,我方大本营被敌方攻击则输游戏: 4. 墙体分为普通墙体和金属墙体两 ...

  4. [源码和文档分享]基于C#实现的坦克大战游戏的最短路

    1 项目概述 1.1 项目背景 <坦克大战>(Battle City)是1985年日本南梦宫Namco 游戏公司开发并且在任天堂FC平上,推出的一款多方位平面射击游戏.游戏以坦克战斗及保卫 ...

  5. java 坦克大战_基于JAVA实现的坦克大战游戏

    一.课程题目 实现一个java版本的坦克大战游戏. 功能提示: 游戏要有图形用户界面,界面能够反映游戏所有的细节 界面中要有坦克,墙,树林,河流 界面中要有一个"家"," ...

  6. 使用红孩儿工具箱完成基于Cocos2d-x的简单游戏动画界面

    [Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier 红孩儿Cocos2d-X学习园地QQ3群:205100149,47 ...

  7. 基于JavaSwing开发坦克大战游戏(单人或双人版) 课程设计 大作业 毕业设计

    基于JavaSwing开发坦克大战游戏(单人或双人版):   (大作业/毕业设计) 开发环境: Windows操作系统 开发工具: MyEclipse/Eclipse+Jdk 运行效果图:  基于Ja ...

  8. 基于Pygame开发的最完美的飞机大战游戏

    基于Pygame开发的最完美的飞机大战游戏 后期会给大家上源码,以及完整的实现思路.

  9. python 游戏开发框架_Python开发 基于python实现坦克大战游戏

    这篇文章主要为大家详细介绍了基于python实现坦克大战游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文实例为大家分享了python实现坦克大战游戏的具体代码, ...

最新文章

  1. eclipse如何部署到tomcat上的
  2. python展示数据库视图_在Django的视图中使用数据库查询的方法
  3. 【牛客 - NC93】设计LRU缓存结构(模拟)
  4. 关于移动端布局和pc端写法
  5. devops失败的原因_如果没有这7个部门的支持,您的DevOps尝试将失败。
  6. eclipse中导入jar包源文件
  7. Keil 5 安装教程
  8. 计算机职业素养论文1500字,职业素养论文1500字 [职业素养教育论文]
  9. Excel表格中如何批量删除工作表
  10. linux应用程序使用aplay播放,Linux中如何解决Aplay不能播放问题
  11. 训练误差和泛化误差、K折交叉验证
  12. Windows系统目录及常用快捷键
  13. 小知识--Windows10许可证即将过期
  14. IIO子系统(Linux驱动开发篇)
  15. [NOIP2016]天天爱跑步 题解(树上差分) (码长短跑的快)
  16. H5直播之MSE(Media Source Extensions)
  17. php 中cookie的简介,setcookile() 的用法,如何理解cookie
  18. 【Linux】SSH相关命令
  19. 计算机视觉知识学习总结
  20. 2021年P气瓶充装考试题及P气瓶充装最新解析

热门文章

  1. 总结网上及自己在Webstrom汉化后无法打开设置上的解决方法
  2. C/C++:变长参数技巧汇总
  3. 数据库系统之:三级模式-两层映射详解
  4. SAR 101:合成孔径雷达简介
  5. UE4 手电筒射线检测
  6. Android 并发/多线程 的基础与应用
  7. 同样是网页,不过加图了,图片在下面
  8. OD破解X盾2022的尝试hh
  9. canal 使用详解
  10. Linux使用 iftop 监控网卡的实时流量