【目标】为扫雷搭建简单的框架
【参考】
cocos2D-x testCpp工程
《cocos2D-x权威指南》
一、框架结构
扫雷的场景很简单,游戏界面只有一个。在MS的这个游戏场景中,绘制有雷区、计时、重新开始等菜单(WIN7上貌似没了)。本程序目前版本只实现雷区部分,点击雷区中某个未知点会将该点扫开。当扫完或者碰到雷的时候,应该提示玩家游戏结果。
那么,按照CCScene、CCLayer这种抽象来划分,扫雷的场景框架如下:
场景 | 场景层次 | 场景元素 | ||
游戏场景 | —— | 雷区游戏层 | —— | 雷区 【本版本实现】 |
| | —— | 计时 | ||
—— | 剩余雷数 | |||
| | —— | 菜单 | ||
| | —— | 游戏结果层 | —— | 游戏结果 |
—— | 重启按钮 |
在这个最基本的0.1版本中,只实现最核心的部分,即雷区游戏层及它的雷区元素。对应的类是
游戏场景 => MineSweepScene : CCScene
雷区游戏层 => MineArea : CCLayer
雷区 => 未单独抽象,在 MineArea 中实现
二、搭建框架
1、游戏场景类 MineSweepScene
首先搭建 MineSweepScene 类,来替代原生的 helloworldScene。 使用cocos建立一个CCnode的子类,最基本的初始化只需要 引入 CREATE_FUNC (即create函数,。里面会调用init函数),并实现 virtual 的init函数即可。我们现在只是搭个架子,因此 init里面只需要调用父类的init即可:
bool MineSweepScene::init() { CCScene::init(); return true; }
CCScene *pScene = MineSweepScene::create();//HelloWorld::scene();
和上面类似,实现 CREATE_FUNC 和 init。然后在 MineSweepScene 新增一个成员变量来引用 MineArea,并在其init的时候初始化:
bool MineSweepScene::init() { CCScene::init(); //初始化子层级 mMineArea = MineArea::create(); addChild(mMineArea, 0); return true; }
三、用TILE实现雷区
扫雷的雷区场景具有明显的格块特征,用TILE来实现会比较容易,正好拿来练习一下TILE。
1、工具
使用的TILE地图编辑器是 Tiled Map Editor,下载地址可见 http://www.mapeditor.org/。介绍这个工具的网址也很多,一搜一大坨。我这里只用到最基本的功能。
2、素材收集
编辑地图之前,先要为地图元素收集素材,从MS的扫雷程序中搞下来了以下纹理组成纹理地图:
每个块的大小是 32 * 32 px。
3、创建地图
收集好材料之后,打开TILED,从 文件-> 新文件,创建一个新的地图,设置高和宽(我设置的是10),块大小设置为32。
然后将准备好的纹理地图图块加进来,从 地图 -》 新图块 ,然后添加这张素材图片,注意这里的块高度和宽度一定要和图片中的吻合,即必须是32。
添加完成后,就会在图块区域看到这些图块了,这些图块是有编号的,注意是从1开始,0是表示什么都没有,另外是顺序是一行一行的排的。
4、绘制地图
下一步是绘制,已有的图层由于现在什么元素都没有,所以和灰色背景融为一体了,比较坑爹,选中某个图块,在指定的层画即可(支持ctrl 和 shift)。我这里需要两层图层。一层是cover层,命名为layer1_cover(这个图层的名称代码里面要用,尽量不要用UNICODE坑自己),全部用图块1涂满,相当于雷区的地表土壤层,需要挖开才能看到地下到底是什么。第二层是填埋层,命名为layer2_minearea,先全部用空白的安全区(图块5)填满。
另外需要注意的是调整图层的顺序,根据我们这里的逻辑,cover层应该在area层之上,所以在图层区域,也将cover层调整到area层上面。
画完保存即可,我这里保存的名称是“mine_sweep_map.tmx”。
5、在代码中加载地图
在代码中引用TILE地图很简单,只需要在 MineArea的init函数中加入以下代码:
map = CCTMXTiledMap::create("mine_sweep_map.tmx"); map->setAnchorPoint(ccp(0.5, 0.5)); map->setPosition(ccp(winSize.width/2, winSize.height/2)); addChild(map, 0);就可以像其他的元素一样,将一个tmx的TILE地图显示出来。当然为了要引用到这个地图,必须先将 地图文件 mine_sweep_map.tmx 和 纹理文件 mine_sweep_element.png 放到 Resources 文件夹下
四、处理触屏事件
现在能看到图像了,要允许玩家来进行扫雷活动,就需要处理触屏动作。这段我是参考 cocos提供的TestCpp工程中的PerformanceTest 下的 PerformanceTouchesTest
类似于Opengl,首先要对本层级启用触屏功能。包括两个关键点:
1、在init函数中设置setTouchEnabled
2、覆写 registerWithTouchDispatcher 函数,将自己添加到touch事件的发送对象中:
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
void addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches);
通过这两个关键点打开开关之后,实现以下这些方法就可以接收到对应的触屏事件,可以在其中打LOG来观察执行情况:
virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event); virtual void ccTouchMoved(CCTouch* touch, CCEvent* event); virtual void ccTouchEnded(CCTouch* touch, CCEvent* event); virtual void ccTouchCancelled(CCTouch* touch, CCEvent* event);注意这里的 ccTouchBegan 的返回值,其含义同样是是否消费掉这个touchBegin事件。
五、改变地图的图元
当用户触摸雷区上某一点的时候,该点应该要被翻开。从地图上来说,就是要使得cover层上对应点的图元消失(或者说是置为0)。这个处理在cocos中也很简单,其流程为:
用图层的名称找到对应的图层 -> 根据给出的地图坐标值找到图层中的对应块 -> 取出或设置该块的GID
1、找到图层:
CCTMXLayer * coverLayer = map->layerNamed("layer1_cover");图层的名称就是在这里排上用场的。
2、确定地图坐标值
首先获得的是触屏点的坐标值,由于触屏的坐标系一般是MM_TEXT的,即左上角为原点,正方向是右下。所以这个值在老版本的cocos中还需要转换,不过我用的 2.0.4,直接用 touch->getLocation(); 就可以获得在OpenGL坐标系下的坐标值了。如果需要原来的触屏坐标系下的值,可以调用 getLocationInView
拿到这个坐标值,还需要转换成地图坐标值。地图实际上也是MM_TEXT坐标系的,X和Y的索引都是从0开始,每个格子是一个坐标。例如左上角的格子是(0, 0),它右边是(1, 0),以此类推。下面给出这段OpenGL坐标值转换成地图坐标值的代码:
CCPoint convertViewPointToMapCoord(CCTMXTiledMap * map, const CCPoint & point) { //注意map可能不是从0开始的,所以要减去边界 float mapMinX = map->getPosition().x - map->getAnchorPoint().x * map->getContentSize().width; float mapMinY = map->getPosition().y - map->getAnchorPoint().y * map->getContentSize().height; int indexX = (point.x - mapMinX) / map->getTileSize().width; int indexY = (point.y - mapMinY) / map->getTileSize().height; //地图是按照 MM_TEXT 坐标系的,行数从上往下增加的,再注意到一个 10*10 的map 的 height 是10,但是索引只能到9 indexY = map->getMapSize().height - 1 - indexY; return ccp(indexX, indexY); }
GID就是在前面绘制TILE地图里面的加载的图元的索引,例如我这里普通cover块的GID就是1,安全块的GID是 5,数字1的GID是6,数字2的GID是7……如果什么都没有,GID就是0。
现在要翻开这块cover块,那么就把对应图块的GID设置成0即可。
六、尾声
理论上,有了以上的知识,就已经可以完成扫雷的基本功能了。剩下的不过是一些布雷和如何将雷区整块点开的小算法了。代码例程可见 http://download.csdn.net/detail/ronintao/5723073。
在这个最简陋的程序的基础上,后面继续开发,来实现一个比较完整的扫雷游戏。