cocos2d-x游戏开发系列教程-坦克大战游戏之坦克和地图碰撞的检测上

在上篇我们可以控制坦克在地图上任意行走了,

但是实际游戏中遇到墙就应该是无法走动的,这节课我们继续完善程序,

让他能在地图检测到墙壁,而无法通过。

1.我们新建一个TileMapInfo类,来获取地图信息。

class TileMapInfo
{
public:
	bool collisionTest(CCRect rect);


	static TileMapInfo* createMapInfoWithFile(const char* tmxFile);
	void initMapInfoWithFile(const char* tmxFile);
	CC_SYNTHESIZE(CCTMXTiledMap*, mTMXTileMap, TileMap);


private:
	CCTMXLayer* mTMXLayers[2];
};

可以看到定义中有一个collisionTest碰撞检测函数,

它根据传进来的rect检测是否与地图上的砖块发生了碰撞,

既然要检测碰撞,我们就需要知道地图中砖块的类型。


2.我们还记得Tiled程序吧,他可以制作tmx格式的地图,看下他的截图


可以看到右上边有两个层,layer_0和layer_1,我们分别单独勾选两个层对比看看,注意看下面两个图的区别:



我们可以看到第二个图层里面是草地,我们知道游戏中,坦克行走到草地会被遮挡住,

这样分层两个层,我们可以在第一层和第二层之间绘制坦克达到遮挡效果。


3.我们看截图右下方,看到很多类型的图块,他们在tmx中都以gid的名字保存,

如下图我标出了前面图块的gid值。


如上图所示我们一共有 36 个图块,那么最后一个图块gid值是36。


4,那么我们先定义上面图块的7种类型

//tile类型,草地,钢铁,河流等
enum enumTileType
{
	tileNone, tileGrass, 
	tileSteel, tileWall,
	tileRiver, tileKing 
};

5.然后我们在定义一个数组,可以通过传进图块的gid值来获取图块类型,对应于上面截图中的图块:

//根据地图中gid获取对应tile的类型
static enumTileType gidToTileType[] =
{
	tileNone,

	tileNone, tileNone, tileGrass, tileGrass, tileSteel, tileSteel, 
	tileNone, tileNone, tileGrass, tileGrass, tileSteel, tileSteel,

	tileWall, tileWall, tileRiver, tileRiver, tileKing, tileKing,
	tileWall, tileWall, tileRiver, tileRiver, tileKing, tileKing,

	tileKing, tileKing, tileNone, tileNone, tileNone, tileNone,
	tileKing, tileKing, tileNone, tileNone, tileNone, tileNone
};

6.然后实现函数void initMapInfoWithFile(const char* tmxFile);,从一个tmx地图文件初始化地图信息:

void TileMapInfo::initMapInfoWithFile(const char* tmxFile)
{
	mTMXTileMap = CCTMXTiledMap::create(tmxFile);
	mTMXLayers[0] = mTMXTileMap->layerNamed("layer_0");
	mTMXLayers[1] = mTMXTileMap->layerNamed("layer_1");


	CCSize winSize = CCDirector::sharedDirector()->getWinSize();
	CCSize mapSize = mTMXTileMap->getContentSize();

	//缩放地图到合适屏幕大小
	mTMXTileMap->setScale(winSize.height / mTMXTileMap->getContentSize().height);

	//将地图放到屏幕中间
	mTMXTileMap->setPosition(ccp((winSize.width - mapSize.width * mTMXTileMap->getScale()) / 2,
		(winSize.height - mapSize.height * mTMXTileMap->getScale()) / 2));
}

7.然后实现一个静态方法,返回一个TileMapInfo的实例:

TileMapInfo* TileMapInfo::createMapInfoWithFile(const char* tmxFile)
{
	TileMapInfo* tileMapInfo = new TileMapInfo();
	tileMapInfo->initMapInfoWithFile(tmxFile);

	return tileMapInfo;
}

8.最后实现碰撞检测的函数bool TileMapInfo::collisionTest(CCRect rect)

bool TileMapInfo::collisionTest(CCRect rect)
{
	int gid = 0;
	CCSize mapSize = mTMXTileMap->getContentSize();
	CCSize tileSize = mTMXTileMap->getTileSize();

	if (rect.getMinX() < 0 || rect.getMaxX() >= mapSize.width ||
		rect.getMinY() < 0 || rect.getMaxY() >= mapSize.height)
		return true;

	//将坦克Y坐标转换为地图上的Y坐标
	float MinY = mapSize.height - rect.getMinY();
	float MaxY = mapSize.height - rect.getMaxY();
	//对坦克四个顶点进行碰撞检测
	gid = mTMXLayers[0]->tileGIDAt(ccp((int)(rect.getMinX() / tileSize.width), 
		(int)(MinY / tileSize.height)));
	if (gidToTileType[gid] != tileNone && gidToTileType[gid] != tileGrass)
		return true;
	gid = mTMXLayers[0]->tileGIDAt(ccp((int)(rect.getMinX() / tileSize.width), 
		(int)(MaxY / tileSize.height)));
	if (gidToTileType[gid] != tileNone && gidToTileType[gid] != tileGrass)
		return true;
	gid = mTMXLayers[0]->tileGIDAt(ccp((int)(rect.getMaxX() / tileSize.width), 
		(int)(MaxY / tileSize.height)));
	if (gidToTileType[gid] != tileNone && gidToTileType[gid] != tileGrass)
		return true;
	gid = mTMXLayers[0]->tileGIDAt(ccp((int)(rect.getMaxX() / tileSize.width), 
		(int)(MinY / tileSize.height)));
	if (gidToTileType[gid] != tileNone && gidToTileType[gid] != tileGrass)
		return true;

	return false;
}
可以看到碰撞检测函数比较麻烦,首先判断了传进来的矩形是否在地图中,

然后依次对矩形四个顶点进行碰撞检测,如果矩形进入了除 tileNone和tileGrass之外的区域

说明无法行走了,则返回true来表示碰撞了。

其中我们用到了tileGIDAt函数,他可以从一个tmx中表示的地图坐标获取gid。如下图:


可以看到 tmx 坐标中 3,24 所在的位置是一个tileNone类型的gid。


到这里TileMapInfo类已经完成了,下篇文章对Tank类进行一些修改。

你可能感兴趣的:(cocos2d-x游戏开发系列教程-坦克大战游戏之坦克和地图碰撞的检测上)