在上篇我们可以控制坦克在地图上任意行走了,
但是实际游戏中遇到墙就应该是无法走动的,这节课我们继续完善程序,
让他能在地图检测到墙壁,而无法通过。
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类进行一些修改。