cocos2d-x之碰撞监测(精灵与墙)

先贴个图:(精灵左下角,中间的那段不和谐的是所谓的墙,那白色字体的是菜单,是不是很丑。。委屈委屈

cocos2d-x之碰撞监测(精灵与墙)_第1张图片

在精灵移动的时候如果碰到了墙或者是什么别的我们不想让它过的物体,我们就要阻止它了。。这就是所谓的碰撞。。本质上来说,就是一个算法问题。。我先贴下我的代码,再来解释吧。。

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);
}
这个很简单吧。。我就不解释了。。
在canPass这个函数中的那个变量nextPostionForTileCoord,我名字取的不是很好,因为本来我的方法不是这样的考虑的,变量名也没改过来,大家将就着看吧。。回到正题:只要有一个位置是在属于"wall"图层的Tile中on个,那么我们就停止精灵的所有action。

大家肯定还会对

if(directionCurrent==UP)
				heroSprite->setPosition(CCPoint(heroPosition.x,heroPosition.y-0.1));

类似的代码有点疑问。其实这也是我调试中出现了问题才加上去的。。大家仔细想想我们之前的做法是不是有什么漏洞??当有监测到有碰撞时,精灵就马上停了下来,而且边缘和与之碰撞的物体相接触,融合,即肯定存在一个我们之前的那8个角中的一个在物体上(仔细考虑考虑)。如果我们再想让精灵运动起来,那么同时也会开始监测是不是有碰撞产生,(插一句:在物体没有运动的时候不会,我们添加了一个isWalking的变量,记录下物体的运动情况),而那个与物体接触的角就会使判断一直为:Wall,我们的精灵就一直不能移动。为了解决这个问题,我在他们运动的方向上减去了0.1或者加了0.1,大家看代码应该能懂什么时候该减,什么时候该加。。还是举个例子吧:如果精灵向上(cocos2d-x的y的正方向,TileMap的负方向)运动,那么肯定是和上面的某个物体碰撞,所以我们的y值减去了一个0.1,而x就可以保持不变,取0.1是为了让玩家看不出来变化。

精灵碰撞的问题就可以解决了,效果如图:

cocos2d-x之碰撞监测(精灵与墙)_第2张图片

这是我自己编的一个游戏的一部分,在此将其记录下来,和大家分享一下。大家有什么问题或意见可以向我提出来,一起交流交流。后面继续写我游戏的其他一部分。目前只是实现了场景的移动,精灵的移动和碰撞监测。开始我的游戏之旅。

你可能感兴趣的:(游戏,算法,cocos2d-x)