文章首发见博客:https://mwhls.top/4816.html。
无图/格式错误/后续更新请见首发页。
更多更新请到mwhls.top查看
欢迎留言提问或批评建议,私信不回。
汇总:Unity 记录
摘要:点击tilemap,文件 保存/读取 该地图区块数据。
思路-2023/08/17
- 保存和读取上,都很土,都是两层循环,暂时没找到可以批量处理的。
- 如果大家有更好的方式欢迎留言,我找到更好的方式之后也会在这里更新。
- 分块:地图被分为若干块,每块大小一样,以避免大地图长时间加载。
- 存储形式:地图暂时以二维数组保存,二维数组的xy即地图块内的xy,值为tile ID(见上篇Sec. 4.2)。
- 保存:Tilemap中,
tile.name
为tile的文件名,因此tilemap保存为ID数组时,需要将tile转name,在从name转ID。 - 读取:与保存相反,ID需要转为tile,因此一个额外的
ID2tileName
字典被设计,即ID转name转tile。 - Tile保存:在读取中,一个10x10的block会循环100次,其中包含诸多重复Tile。在上篇中,Tile通过文件读取。为减少IO操作,Tile文件在被首次读取时,会被记录,因此一个
name2tile
字典被设计。
数据结构-2023/08/16
public struct TileInfo{
public string ID;
public string name;
public string description;
public string path;
};
public struct TilesInfo{
public string version;
public string date;
public Dictionary<string, TileInfo> tiles;
}
public struct TilemapInfo{
public int[] block_offset;
public List<List<string>> map;
}
private static TilesInfo tilemap_info;
private Dictionary<string, TileBase> name2tile;
private Dictionary<string, string> ID2tileName;
public Vector3Int block_size;
初始化相关-2023/08/16
- 加载上篇的
TilesInfo
,即名字转路径,用于从本地读取tile。
void load_tiles_info(){
// load config
string tiles_info_path = "Assets/Resources/Saved/TilesInfo.json";
string jsonText = File.ReadAllText(tiles_info_path);
tilemap_info = JsonConvert.DeserializeObject<TilesInfo>(jsonText);
// init
name2tile = new Dictionary<string, TileBase>();
ID2tileName = new Dictionary<string, string>();
// init ID2tileName
ID2tileName.Add("0", "");
foreach (var tile_kv in tilemap_info.tiles){
ID2tileName.Add(tile_kv.Value.ID, tile_kv.Key);
}
}
转换函数-2023/08/16
- tile名为空,ID为"0"时表示无tile。
public TileBase map_name_to_tile(string name){
if (name == ""){
// No tile
return null;
}
if (name2tile.ContainsKey(name)){
// Tile have been loaded
return name2tile[name];
}
else{
// Load tile
string tile_path = tilemap_info.tiles[name].path;
TileBase tile = Resources.Load<TileBase>(tile_path);
name2tile.Add(name, tile);
return tile;
}
}
public TileBase map_ID_to_tile(string ID){
return map_name_to_tile(ID2tileName[ID]);
}
public string map_tile_to_ID(TileBase tile){
if (tile == null){
return "0";
}
else{
return tilemap_info.tiles[tile.name].ID;
}
}
保存与加载-2023/08/17
- 保存与加载代码的结构类似,
- 首先生成文件名,得到地图块在tilemap上的位置,
- 遍历 待保存/待读取 的数据,将地图块中的相对位置转为tilemap中的绝对位置,
- 将绝对位置上的数据 记录至二维数组/SetTile到tilemap
public void save_tilemap(Tilemap tilemap, int[] block_offsets){
// file name
string filename = "Map_" + block_offsets[0] + "-" + block_offsets[1] + ".json";
string file_path = "Assets/Resources/Saved/" + filename;
// Get actual position of block
Vector3Int map_offset = new Vector3Int(block_offsets[0] * block_size[0], block_offsets[1] * block_size[1]);
// Init tilemap struct
TilemapInfo tilemap_info = new TilemapInfo {};
tilemap_info.block_offset = block_offsets;
tilemap_info.map = new List<List<string>>();
// Loop for saving
for (int x = 0; x < block_size[0]; x++){
List<string> map_row = new List<string>();
for (int y = 0; y < block_size[1]; y++){
// Get actual position of tile
Vector3Int tile_offset = new Vector3Int(map_offset[0] + x, map_offset[1] + y);
TileBase tile = tilemap.GetTile(tile_offset);
// Record tile info
string tile_ID = map_tile_to_ID(tile);
map_row.Add(tile_ID);
}
tilemap_info.map.Add(map_row);
}
// save
string tilemap_info_json = JsonConvert.SerializeObject(tilemap_info, Formatting.Indented);
File.WriteAllText(file_path, tilemap_info_json);
}
public void load_tilemap(Tilemap tilemap, int[] block_offsets){
// read file
string filename = "Map_" + block_offsets[0] + "-" + block_offsets[1] + ".json";
string file_path = "Assets/Resources/Saved/" + filename;
if (!File.Exists(file_path)){
// No data
return;
}
string jsonText = File.ReadAllText(file_path);
TilemapInfo block_info = JsonConvert.DeserializeObject<TilemapInfo>(jsonText);
// Get actual position of block
Vector3Int map_offset = new Vector3Int(block_offsets[0] * block_size[0], block_offsets[1] * block_size[1]);
// Loop for loading
for (int x = 0; x < block_size[0]; x++){
List<string> map_row = new List<string>();
for (int y = 0; y < block_size[1]; y++){
// Get actual position of tile
Vector3Int tile_offset = new Vector3Int(map_offset[0] + x, map_offset[1] + y);
string tile_ID = block_info.map[x][y];
// Load tile
TileBase tile = map_ID_to_tile(tile_ID);
tilemap.SetTile(tile_offset, tile);
}
}
点击以保存/读取-2023/08/17
- 和Sec. 3.1的拖动鼠标放置tile的事件一样实现,首先鼠标位置转tilemap坐标,再转block位置,再运行保存函数与读取函数。
- 效果和想象的一样,挺好
if (input_base.isKeydown("Fire2")){
Vector3Int pos_tilemap = tilemap_modify.WorldToCell(input_base.get_mouse_pos(1));
int[] block_offset = {Mathf.Abs(pos_tilemap.x) / tilemap_base.block_size[0], Mathf.Abs(pos_tilemap.y) / tilemap_base.block_size[1]};
if (pos_tilemap.x < 0){
block_offset[0] = -block_offset[0] - 1;
}
if (pos_tilemap.y < 0){
block_offset[1] = -block_offset[1] - 1;
}
Debug.Log("Mouse pos: [" + pos_tilemap.x + ", " + pos_tilemap.y + "].");
Debug.Log("Block offset: [" + block_offset[0] + ", " + block_offset[1] + "].");
tilemap_base.save_tilemap(tilemap_modify, block_offset);
}
if (input_base.isKeydown("Fire3")){
Vector3Int pos_tilemap = tilemap_modify.WorldToCell(input_base.get_mouse_pos(1));
int[] block_offset = {Mathf.Abs(pos_tilemap.x) / tilemap_base.block_size[0], Mathf.Abs(pos_tilemap.y) / tilemap_base.block_size[1]};
if (pos_tilemap.x < 0){
block_offset[0] = -block_offset[0] - 1;
}
if (pos_tilemap.y < 0){
block_offset[1] = -block_offset[1] - 1;
}
Debug.Log("Mouse pos: [" + pos_tilemap.x + ", " + pos_tilemap.y + "].");
Debug.Log("Block offset: [" + block_offset[0] + ", " + block_offset[1] + "].");
tilemap_base.load_tilemap(tilemap_modify, block_offset);
}