先贴个图:(精灵左下角,中间的那段不和谐的是所谓的墙,那白色字体的是菜单,是不是很丑。。)
在精灵移动的时候如果碰到了墙或者是什么别的我们不想让它过的物体,我们就要阻止它了。。这就是所谓的碰撞。。本质上来说,就是一个算法问题。。我先贴下我的代码,再来解释吧。。
typedef enum{PASS=0,MAPLIMIT=1,WALL=2}COLLISIONTYPE;
typedef enum{DOWN=0,UP=3,LEFT=1,RIGHT=2}DIRECTIONS; COLLISIONTYPE HelloWorld::canPass(cocos2d::CCPoint position) { if(position.x<=32||position.y<=32||position.x>=mapSize.width*mapTileSize.width-32|| position.y>=mapSize.height*mapTileSize.height) return MAPLIMIT; CCPoint nextPostionForTileCoord=positionToTileCoord(position); int tileGIDUP=map->layerNamed("wall")->tileGIDAt (CCPoint((int)nextPostionForTileCoord.x,(int)(nextPostionForTileCoord.y-1.0))); int tileGIDDOWN=map->layerNamed("wall")->tileGIDAt (CCPoint((int)nextPostionForTileCoord.x,(int)(nextPostionForTileCoord.y+1.0))); int tileGIDRIGHT=map->layerNamed("wall")->tileGIDAt (CCPoint((int)(nextPostionForTileCoord.x+1.0),(int)nextPostionForTileCoord.y)); int tileGIDLEFT=map->layerNamed("wall")->tileGIDAt (CCPoint((int)(nextPostionForTileCoord.x-1.0),(int)nextPostionForTileCoord.y)); int tileGIDUPLEFT=map->layerNamed("wall")->tileGIDAt (CCPoint((int)(nextPostionForTileCoord.x-1),(int)(nextPostionForTileCoord.y-1.0))); int tileGIDUPRIGHT=map->layerNamed("wall")->tileGIDAt (CCPoint((int)(nextPostionForTileCoord.x+1),(int)(nextPostionForTileCoord.y-1.0))); int tileGIDDOWNRIGHT=map->layerNamed("wall")->tileGIDAt (CCPoint((int)(nextPostionForTileCoord.x+1),(int)(nextPostionForTileCoord.y+1))); int tileGIDDOWNLEFT=map->layerNamed("wall")->tileGIDAt (CCPoint((int)(nextPostionForTileCoord.x-1),(int)(nextPostionForTileCoord.y+1))); if(tileGIDUP||tileGIDDOWN||tileGIDRIGHT||tileGIDLEFT||tileGIDUPLEFT|| tileGIDUPRIGHT||tileGIDDOWNRIGHT||tileGIDDOWNLEFT) { return WALL; } return PASS; }
COLLISIONTYPE是我定义的精灵行走的类型,如果碰到了不能过的物体,那么现在的状态就是WALL,如果是因为地图大小的限制,那么就是MAPLIMIT,正常情况下是PASS。
碰撞监测肯定是随时都发生的吧。这个肯定是毫无疑问的,那么我们就可以把这个判断加在update()方法中:
if(isWalking){ COLLISIONTYPE pass_type=canPass(heroPosition); if(pass_type==WALL) { heroSprite->stopAllActions(); if(directionCurrent==UP) heroSprite->setPosition(CCPoint(heroPosition.x,heroPosition.y-0.1)); else if (directionCurrent==DOWN) { heroSprite->setPosition(CCPoint(heroPosition.x-0.1,heroPosition.y+0.5)); } else if (directionCurrent==LEFT) { heroSprite->setPosition(CCPoint(heroPosition.x+0.1,heroPosition.y)); } else heroSprite->setPosition(CCPoint(heroPosition.x-0.1,heroPosition.y)); isWalking=false; }
COLLISIONTYPE pass_type=canPass(heroPosition);
只要在update()中加这句就Ok了,就可以监测碰撞了。。。那么碰撞之后,我们要干嘛呢???毫无疑问,我们就要把精灵给停止下来。。停止精灵目前在进行的所以的action。所以就有了这句: heroSprite->stopAllActions();
先暂时不看接下来的代码。我们返回到 COLLISIONTYPE HelloWorld::canPass(cocos2d::CCPoint position)
这个函数,我是怎么判断它什么时候是WALL,什么时候是MAPLIMIT的呢?
首先,我们知道精灵也是有大小的,(我这里是宽高均为64像素的正方形),锚点肯定在(0.5,0.5)这个位置,即精灵的中心。
接下来,我们来判断什么时候会为MAPLIMIT,相信大家肯定知道了,只要精灵的左边界和map的左边界相同时;或:右边界和右边界相同;(上边界和下边界一样的)。
if(position.x<=32||position.y<=32||position.x>=mapSize.width*mapTileSize.width-32|| position.y>=mapSize.height*mapTileSize.height) return MAPLIMIT;这段代码应该没问题了吧。。。注意这里是32,因为就是左边界到中心的距离呀。。(关于这个坐标的问题,我还不是很懂,新手上路中。。),我猜测position是指锚点的位置(至少我这里测试的是这样的,但是网上和书上说的锚点和postion是不同的,position是左下角的位置)。。先放放这里。。大家可以自己测试一下。。
然后到了和墙碰撞的这一部分了,我的算法是:计算这个精灵的左上角,正上方,右上角,正右方,右下角,正下方,左下角,正左方这8个位置所在的那个Tile是不是在有墙的图层中,大家肯定会调用
map->layerNamed("wall")->tileGIDAt(....)这个函数吧。。这就是判断这个Tile是不是在“wall”图层中,如果是,那么返回一个int 类型的GID(唯一的吧。。反正不是0。。)如果不是那就返回0。这里面的参数是CCPoint类型的,而且还是TileMap的坐标点,而我们一般用的都是cocos2d-x的坐标(和opengl的坐标一样),那么这就涉及到两种坐标系的转换:
CCPoint HelloWorld::positionToTileCoord(CCPoint position) { float x=(float)(position.x)/mapTileSize.width; float y=(float)(mapSize.height*mapTileSize.height-position.y)/mapTileSize.height; return ccp(x,y); }这个很简单吧。。我就不解释了。。
大家肯定还会对
if(directionCurrent==UP) heroSprite->setPosition(CCPoint(heroPosition.x,heroPosition.y-0.1));
精灵碰撞的问题就可以解决了,效果如图:
这是我自己编的一个游戏的一部分,在此将其记录下来,和大家分享一下。大家有什么问题或意见可以向我提出来,一起交流交流。后面继续写我游戏的其他一部分。目前只是实现了场景的移动,精灵的移动和碰撞监测。开始我的游戏之旅。