哎呀,这游戏实在是太好玩太有意思了(啊,个屁啊~),不过,我们还是要继续优化的喇~
这次我们就来为地图添加障碍物吧~很好玩的,一起来~
1.
再次打开我们的Tiled Map Editor
我们还要打开level01.tmx文件,首先新建一个图层,命名为barrier,也就是我们的障碍层。
然后,我们要介绍一个新的素材,meta_tiles.png:
把objects.png和meta_tiles.png都加进图块里,宽高都是32像素。
2.
添加障碍物
好,现在,我们利用下面的素材在地图的barrier层上画几笔,增加一些障碍物。
下面是我画的,太好看了~(啊才怪!):
3.
让障碍物真正成为障碍物!
别以为它叫做障碍层那它就真的可以阻止主角的前进,主角现在一样能顺利的穿过这些“障碍物”,我们还得新建一个层,叫做meta。
然后选择图块里的红色方块:
然后,右键,“图块属性”,给图块新建一个属性,叫做Collidable,值为true。
是的,我们将会利用这个属性判断是否为障碍物。
现在,我们要用红色方块在meta层上画东西,怎么画?把所有障碍物填满,记住,是在meta层上描。
看看效果,好多红色,哈哈:
OK,保存地图,然后把焦点回到代码里。
4.
通过图块属性判断是否可以通行
大家,我又要改了,我要把Player的CCTMXTiledMap* map成员变量放到Entity里,同时添加一个函数tileCoordForPosition:
01 | #ifndef __ENTITY_H__ |
02 | #define __ENTITY_H__ |
03 |
04 | #include "cocos2d.h" |
05 | #include "Controller.h" |
06 | #include "ControllerListener.h" |
07 |
08 | using namespace cocos2d; |
09 |
10 | class Entity : public CCNode, public ControllerListener { |
11 | public : |
12 | void setSprite(CCSprite* mSprite); |
13 | void setController(Controller* controller); |
14 |
15 | /* 实现SimpleMoveListener接口的方法 */ |
16 | virtual void setSimplePosition( int x, int y); |
17 | virtual CCPoint getCurPosition(); |
18 | protected : |
19 | CCSprite* mSprite; |
20 | Controller* mController; |
21 |
22 | /* map本来是在Player类的,现在我们放在这里 */ |
23 | CCTMXTiledMap* map; |
24 |
25 | /* 检测碰撞的地图层 */ |
26 | CCTMXLayer* meta; |
27 |
28 | /* 将像素坐标转换为地图格子坐标*/ |
29 | CCPoint tileCoordForPosition(CCPoint pos); |
30 | }; |
31 |
32 | #endif |
tileCoordForPosition是做什么的呢?它是用来把像素坐标转换为地图里相应的格子的。
01 | CCPoint Entity::tileCoordForPosition( CCPoint pos ) |
02 | { |
03 | CCSize mapTiledNum = map->getMapSize(); |
04 | CCSize tiledSize = map->getTileSize(); |
05 |
06 | float x = pos.x / tiledSize.width; |
07 | float y = pos.y / tiledSize.height; |
08 |
09 | /* Cocos2d-x的默认Y坐标是由下至上的,所以要做一个相减操作 */ |
10 | y = mapTiledNum.height - y; |
11 |
12 | return ccp(x, y); |
13 | } |
刚刚大家一定发现了,我们还多了一个CCTMXLayer* meta,这是啥?meta不就是我们刚刚给地图加的一个图层么?在这个图层上面我们画了很多红色格子的,而且我们还给这些红色格子加了一个Collidable属性。
我想大家应该要猜到了,我们只要判断主角即将要去的地方是不是在红色格子的位置上就可以了,如果要去的地方是红色格子的位置,那就不让主角移动~哇,听起来好简单~
5.
开始限制主角的行动
现在,请打开Player.cpp类的initWithTiledMap函数,在最开始加上这句代码(什么,你确定是一句?看起来好像是两句吧):
1 | /* 加载meta层 */ |
2 | meta = map->layerNamed( "meta" ); |
3 | meta->setVisible( false ); |
OK,最后一步,我们修改Player.cpp的setSimplePosition函数,在最前面加上以下几句代码:
01 | /* -----------------判断是否不可通行---------------- */ |
02 | /* 获得当前主角在地图中的格子位置 */ |
03 | CCPoint tiledPos = tileCoordForPosition(ccp(x, y)); |
04 |
05 | /* 获取地图格子的唯一标识 */ |
06 | int tiledGid = meta->tileGIDAt(tiledPos); |
07 |
08 | /* 不为0,代表存在这个格子 */ |
09 | if (tiledGid != 0) { |
10 | /* |
11 | 获取该地图格子的所有属性,目前我们只有一个Collidable属性, |
12 | 格子是属于meta层的,但同时也是属于整个地图的,所以在获取格子的所有属性时, |
13 | 通过格子唯一标识在地图中取得。 |
14 | */ |
15 | CCDictionary* propertiesDict = map->propertiesForGID(tiledGid); |
16 |
17 | /* 取得格子的Collidable属性值 */ |
18 | const CCString* prop = propertiesDict->valueForKey( "Collidable" ); |
19 |
20 | /* 判断Collidable属性是否为true,是的话,不让玩家移动 */ |
21 | if (prop->m_sString.compare( "true" ) == 0) { |
22 | return ; |
23 | } |
24 | } |
好吧,我感觉代码的注释已经很详细了,首先从meta层里获得某个地图格子(某个?咳咳,就是我们主角当前所在的地图格子位置)在地图中的唯一标识。然后根据这个唯一标识在地图中取得格子的所有属性。
有个疑问,为什么要从meta中取得唯一标识而不是直接格局格子位置从地图中获取格子的属性?因为,因为地图有很多个图层啊,我怎么知道你的这个格子位置是那一层的位置啊?你说你要第一行第三列的那个格子,天呐,我有ground图层,有meta图层,你是要哪个?糟糕,我太入戏的,一不小心用了第一人称。所以呢,不要让程序混乱,否则它会让你更混乱~!
最后当然是取得格子的Collidable属性,看看这个属性的值是否为true,是的话,就不让主角前进了。看,主角被那颗高大威猛的树挡着了,不能前进了~
6.
解决卡死问题
我们确实是卡住主角,不让他前进了,但是我们会发现,这一卡就把主角永远卡住了,他再也无法移动了~!这很糟糕。没关系,我们加点东西就好。当遇到障碍物时,不是让主角停止前进,而是让主角后退一个像素:
01 | /* 判断Collidable属性是否为true,是的话,不让玩家移动 */ if (prop->m_sString.compare( "true" ) == 0) { |
02 | if (x > 0) { |
03 | x -= 1; |
04 | } |
05 | else { |
06 | x += 1; |
07 | } |
08 |
09 | if (y > 0) { |
10 | y -= 1; |
11 | } |
12 | else { |
13 | y += 1; |
14 | } |
15 | } |
我们修改了setSimplePosition里的一个小地方,当Collidable属性的值为true时,我们不要直接return,而是让主角稍微往相反方向移动一个像素,这样主角就不会永远动不了了。
OK,本篇结束了,下一篇我们将加入胜利判断条件,以及加入可以让主角吃掉的东西,太帅了~