unity 简单实现tilemap的保存和读取(以便用于关卡编辑器)

        最近在学习使用Tiledmap,用于制作地图确实很方便快捷,但总不能一个关卡就做一个prefab?还是必须将其保存为文本文件才行啊,于是有了以下的尝试:  

      先定义一个Tiledmap类,用于存储单个Tiledmap信息

[System.Serializable]
public class SimpleTile
{
    [SerializeField]
    public string name;
    [SerializeField]
    public Dictionary<string, string> tiles = new Dictionary<string, string>();
}

tiles是保存的所有tile块。key为坐标值,value为使用的tilemap的名称。

        然后需要一个管理同一个Grid下所有Tiledmap的类:

[System.Serializable]
public class SimpleGrid
{
    [SerializeField]
    public Dictionary<string, SimpleTile> tiles = new Dictionary<string, SimpleTile>();
}

tiles 的key是tiledMap在Hierarchy中的名称。

之后当然是先保存当前Grid下的所有Tiledmap:

public void SaveLevel()
    {

        Tilemap[] tilemaps = grid.GetComponentsInChildren<Tilemap>();
        SimpleGrid simpleGrid = new SimpleGrid(); 
        for (int i = 0; i < tilemaps.Length; i++)
        {
            //循环读取每一个Tiledmap
            ExportMap(tilemaps[i]);
        }
        Debug.LogWarning(simpleGrid.tiles.Count);
        //将赋值完成的数据转换为json字符串。方便文件存储
        //JsonConvert 是使用的Newtonsoft.Json 的第三方插件,
        //unity自带的JsonUtility可能无法将类正确转换成string。我刚开始就是用的它,发现simpleGrid.tiles.Count一直为0
        mtempJson =  JsonConvert.SerializeObject(simpleGrid);
        
        //获取到字符串,就可以存储到本地啦,关于存储的环节可以使用File类,或者第三方插件完成
        Debug.LogWarning(mtempJson);
    }
    public void ExportMap(Tilemap tilemap)
    {
        //获取到tiledmap的格子信息
        BoundsInt area = tilemap.cellBounds;

        Dictionary<string, string> data = new Dictionary<string, string>();
        simpleGrid.tiles[tilemap.name] = new SimpleTile();
        simpleGrid.tiles[tilemap.name].name = tilemap.name;
        simpleGrid.tiles[tilemap.name].tiles = new Dictionary<string, string>();

        for (int i = area.xMin; i < area.xMax; i++)
        {
            for (int j = area.yMin; j < area.yMax; j++)
            {
                if(tilemap.GetTile(new Vector3Int(i, j, 0)) == null)
                {
                    //空白的地方就无需保存
                    continue;
                }
                //将已有的tile根据自身坐标存储。
                //之前想直接存储tilemap.cellBounds ,但是在转json时会报错,所以还是这样一个个存吧
                simpleGrid.tiles[tilemap.name].tiles[i+","+j] = tilemap.GetTile(new Vector3Int(i,j,0)).name;
                //---------------这个只是为了测试方便------
                //Tiles存储了地图上用到的瓦片信息。正常使用时应该在初始化时就将其赋值。
                //public Dictionary<string, TileBase> Tiles = new Dictionary<string, TileBase>(); key为瓦片的名称,value为使用的瓦片
                if(!Tiles.ContainsKey(tilemap.GetTile(new Vector3Int(i, j, 0)).name))
                {
                   Tiles[tilemap.GetTile(new Vector3Int(i, j, 0)).name] = tilemap.GetTile(new Vector3Int(i, j, 0));
                }
            }
        }

    }

        之后是读取文件并将其重新绘制到界面。读取文件的方式依旧可以使用File类或者第三方插件,此处不做过多讲解。下面是解析字符串并绘制到界面:

 public void CreateLevel(string json)
    {
        if (json == null)
        {
            Debug.LogWarning("传入字符串为null");
            return;
        }
        //先将json字符串还原为SimpleGrid类
        SimpleGrid simpleGrid = JsonConvert.DeserializeObject<SimpleGrid>(json);
        if (simpleGrid == null)
        {
            Debug.LogWarning("传入的字符串不能正确解析");
            return;
        }
        //先创建一个Grid
        GameObject grid = new GameObject("Grid");
        grid.AddComponent<Grid>();

        Debug.LogWarning("keys::"+simpleGrid.tiles.Count);
        Debug.LogWarning("Tiles::" + Tiles.Count);

        //循环创建每一个Tiledmap,如果想做渐进加载的效果,可以将这个方法修改为协程方式处理
        foreach (var item in simpleGrid.tiles.Keys)
        {
            //创建Tiledmap并将父级设为刚才的Grid
            GameObject tile = new GameObject(item);
            tile.transform.SetParent(grid.transform);
            //添加Tilemap 组件
            Tilemap tilemap = tile.AddComponent<Tilemap>();
            //一定要添加TilemapRenderer组件,之前忘了添加,一直没显示瓦片,还找了半天bug。。。
            tile.AddComponent<TilemapRenderer>();
            //将瓦片信息读取出来
            SimpleTile simpleTile = simpleGrid.tiles[item];
            foreach (var vec2 in simpleTile.tiles.Keys)
            {
                string[] pos = vec2.Split(",");
                //在指定坐标下设置瓦片
                tilemap.SetTile(new Vector3Int(int.Parse(pos[0]), int.Parse(pos[1]), 0), getTile(simpleTile.tiles[vec2]));
            }
        }

    }
    /// <summary>
    /// 根据瓦片名称获取到瓦片的类
    /// </summary>
    /// <param name="tileName"></param>
    /// <returns></returns>
    public TileBase getTile(string tileName)
    {

        TileBase tile;
        //这个Tiles 是刚才在存储时赋值的
        if (Tiles.TryGetValue(tileName, out tile))
        {
            return tile;
        }
        else
        {
            return null;
        }
    }

It‘s Done!!

注::刚才看到有些部分和制作的关卡内容有出入,仔细检查后发现是因为TilemapRenderer的层级没有处理,所以在保存的时候应该保存下TilemapRenderer的SortingLayer和Order in Layer并在创建时赋值。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大藏小隐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值