魔力宝贝高清单机计划(二) 地图转为tiled map

文章目录

  • 地图格式
  • tiled map格式学习
  • 转换步骤

代码地址: https://github.com/mversace/CrossGateRemastered

地图格式

地图文件头:

#pragma pack(1)
struct tMapHead
{
	char cFlag[12];
	unsigned int w;
	unsigned int h;
}; // 后续紧跟w*h
#pragma pack()

后边紧跟 w*h*2的3组数据,分别是地表、物件、未知

tiled map格式学习

tiled map支持很多格式。这里为了方便转成了json格式。
tiled map,一张地图有一个例如map.json,其中的图块集也会存储为 xx.json
官方文档:https://doc.mapeditor.org/en/stable/reference/json-map-format/

魔力地图本身是45度的。所以tiled map中要设置地图属性为45度
我在这里是自己随便拼了一张地图,然后查看生成的json格式,对比着做的。跟官网的字段不太一样。官网的其实是比较老的。

因为写json,采用了一个第三方库:https://github.com/nlohmann/json

转换步骤

  1. 读取地图数据:
    这里的两个map存储的是图块索引,对应到魔力图片中的tileid字段,因为tiled map图块集json中是顺序存储的,所以这个map最终会根据tileid排序,最终为他们编号为 1-n,这也就是为什么采用map而不是unordered_map。
unsigned short n = 0;
int i = 0;
while (2 == fread_s(&n, 2, 1, 2, pFile))
{
	if (i < dataLen)
	{
		// 获取地表信息
		_vTileData.push_back((int)n);

		if (n > 0)
			_mapTileMap[n] = 0;
	}
	else if (i < dataLen * 2)
	{
		// 物件信息
		_vObjectData.push_back((int)n);

		if (n > 0)
			_mapObjectMap[n] = 0;
	}
	else
	{
		// 未知信息
		break;
	}
	++i;
}
  1. 拷贝图片到想要的目录, 并且声称图片集
    考虑到方便以后单张地图修改,这里每张地图建立了一个文件夹,把涉及到的所有tile、object拷贝进来并生成图块集json
    代码就不贴了。

  2. 生成tiled map需要的json子项–地表层
    对于最终的tiled map地图来说,该json中存储有所有的地表数据以及物件数据。
    接下来就是生成对应的地表数据。
    这里需要注意的是。魔力地图存数据的格式为左下到右上,tiled map中为右上到右下。所以需要对vec进行一次顺时针90度的转换。同时宽高也要转换下

// 地表基础数据
_tileJsonData["width"] = _mapHead.h;  // 注意这里是反的
_tileJsonData["height"] = _mapHead.w;
_tileJsonData["name"] = "layer"; // 如果写中文要写成utf-8格式
_tileJsonData["id"] = 1;
_tileJsonData["opacity"] = 1;
_tileJsonData["type"] = "tilelayer";
_tileJsonData["visible"] = true;
_tileJsonData["x"] = 0;
_tileJsonData["y"] = 0;
_tileJsonData["data"] = nlohmann::json::array();

// 把魔力地图转为w*h的二维数组,需要顺时针旋转90度以适配tiled map
int len = _mapHead.w * _mapHead.h;
for (int i = 0; i < len; ++i)
{
	int row = i % _mapHead.h;
	int col = _mapHead.w - i / _mapHead.h - 1;
	int idx = row * _mapHead.w + col;

	_tileJsonData["data"].push_back(_mapTileMap[_vTileData[idx]]);
}
  1. 生成tiled map需要的json子项–物件层
    对于物件来说,在tiled map中是以坐标的形式存在的。注意tiled map中的坐标体系。可以自行体验下。这里不再解释。
    对于每张物件,根据上一篇博文,可以取得该图片的偏移。
    最终步骤就是:取得该坐标上的物件,通过公式来映射到tiled map的坐标系,最终得出该物件在本地图中的x和y

注意:这里有个魔数。按照规则来看,这个值应该是47/2=23,但是效果不对,最终调整为魔数13完美契合。

/**
* 原始地图中,会拿出当前坐标的中心点为锚点,图片左上角放到锚点,然后x和y加上对应的偏移开始绘制
   在tiled map中,把物件直接放到对应位置,则这张图片相对于该坐标中心点(锚点)的偏移为 -w/2, 23-h
   而偏移应该为xOffset以及yOffset,做出对应的处理

   横向方向还需要的偏移xTemp = xOffset-(-w/2)
   因为地图tile大小为64*47。
   如果xTemp = -64,则x += -47, y -= -47
   如果xTemp = 64,则x += 47, y -= 47
   最终也就是 x += (xOffset + w / 2) / 64 * 47, y -= x += (xOffset + w / 2) / 64 * 47

   竖向方向还需要的偏移yTemp = yOffset-(23-h)
   yTemp为正,则 (x,y) += |yTemp|,为负,则(x,y) += |yTemp| 最终也就是(x,y) += (yOffset-(23-h))
 */
double xOffset = (jsonItem["xOffset"].get() + jsonItem["width"].get() / 2.0) / 64.0 * 47.0;
double yOffset = (jsonItem["yOffset"].get() + jsonItem["height"].get() - 13); // 13 magic number...


_objJsonData["objects"].push_back({
	{"gid", _mapObjectMap[objId] + idxBegin},
	{"id", ++id},
	{"name", ""},
	{"rotation", 0},
	{"type", ""},
	{"visibale", true},
	{"width", jsonItem["width"]},
	{"height", jsonItem["height"]},
	{"x", (row + 1) * 47 + yOffset + xOffset },
	{"y", (_mapHead.w - col) * 47 + yOffset - xOffset }
});
  1. 组装最终的tiled map json
void CGetCGMap::buildTiledMap()
{
	std::cout << "build tiled Map json" << std::endl;

	nlohmann::json tiledMapJson;
	tiledMapJson["width"] = _mapHead.h; // 注意这里是反的
	tiledMapJson["height"] = _mapHead.w;
	tiledMapJson["infinite"] = false;
	tiledMapJson["orientation"] = "isometric";
	tiledMapJson["renderorder"] = "left-up";
	tiledMapJson["tileversion"] = "1.2.2";
	tiledMapJson["tilewidth"] = 64;		// 这里写死,特殊的地图图块很大,自己拼就可以了
	tiledMapJson["tileheight"] = 47;
	tiledMapJson["type"] = "map";
	tiledMapJson["version"] = 1.2;

	tiledMapJson["tilesets"] = nlohmann::json::array();
	tiledMapJson["tilesets"].push_back({
		{"firstgid", 1},
		{"source", _strTileJsonName}
	});
	tiledMapJson["tilesets"].push_back({
		{"firstgid", (_mapTileMap.size() / 1000 + 1) * 1000},
		{"source", _strObjJsonName}
	});

	tiledMapJson["layers"] = nlohmann::json::array();
	tiledMapJson["layers"].push_back(_tileJsonData);
	tiledMapJson["layers"].push_back(_objJsonData);

	std::ofstream ofs;
	ofs.open((_strTiledMapPath + _strMapOnlyName + "_map.json").c_str());
	if (ofs.good())
	{
		ofs << std::setw(4) << tiledMapJson << std::endl;
		ofs.close();
	}
}

本次工作仅仅生成了map/0下的地图。因为一些新地图地表大小变化了。而且在代码中实际上写死了地表tile的宽高。
对于新地图可以自行生成,直接拼接也很简单,因为图块很大。
法兰城预览:

你可能感兴趣的:(魔力宝贝)