【cocos2D-x学习】6.简单游戏的框架——扫雷V0.1

来源:互联网 发布:游戏脚本python 编辑:程序博客网 时间:2024/06/05 17:13

【目标】为扫雷搭建简单的框架


【参考】

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;}

       然后在 AppDelegate 里面用 MineSweepScene 替代 HelloWorld 即可:

CCScene *pScene = MineSweepScene::create();//HelloWorld::scene();

2、 雷区游戏层 MineArea

       和上面类似,实现 CREATE_FUNC 和 init。然后在  MineSweepScene 新增一个成员变量来引用  MineArea,并在其init的时候初始化:

bool MineSweepScene::init() {    CCScene::init();    //初始化子层级    mMineArea = MineArea::create();    addChild(mMineArea, 0);        return true;}

        现在大体的框架就已经搭建好了,程序已经可以运行,只不过什么都看不到罢了。如果想要测试程序的有效性,还可以copy点原来helloWorldScene里面的东西到 MineArea里面。


三、用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);

        这里的addTargetedDelegate很关键,其完整声明如下:

    void addTargetedDelegate(CCTouchDelegate *pDelegate, int nPriority, bool bSwallowsTouches);

         三个参数中,第一个参数是touch事件的处理者,CCLayer是这个类的子类,可以直接传this;第二个是优先级,值越小优先级越高,可以为负;第三个值是个flag,表示是否消费掉这个事件,如果设置为true,则优先级比自己低的,都将不再受到这个touch事件。

         通过这两个关键点打开开关之后,实现以下这些方法就可以接收到对应的触屏事件,可以在其中打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);}

3、设置图块的GID

        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。

        在这个最简陋的程序的基础上,后面继续开发,来实现一个比较完整的扫雷游戏。

原创粉丝点击