Unity TileMap 存档 保存地图

关注公众号,获取更多干货。

Unity TileMap 存档 保存地图_第1张图片

下面是正文:


最近在恶补Unity的一些模块,前几天朋友推荐我看一下Unity2017.2以上才支持的TileMap,他说用这个做2D关卡贼方便,所以我就去看了一眼,的确很棒,包括官方出的Demo也是看一眼基本上就能学会,所以说到这里不得不骄傲一下,Unity现在是越做越牛x,身为Unity的攻城狮我真是感到无比的咳咳咳咳咳…

好了废话不能多说,先说说我为啥要写这篇博客,其实TileMap这个工具是真的很好用,但是有一个问题,一般需要设计2D关卡的游戏,都是需要很多关的,就连给我介绍这个TileMap的朋友都说,他现在在做的游戏才刚刚200关,嗯?才200关?才?
那么问题来了,这个地图是怎么保存的呢,200个Scene?还是做了200个预制体?不过这个我倒是没有问,然后我百度了一下TileMap保存地图,还真没有人写过类似保存TileMap地图、载入TileMap地图的,那索性我就来搞一下吧,虽然这只是第一个版本,但是还是欢迎大家和我一起完善,写的不好的地方请见谅哈。

其实到了这里,不妨会有人问我,这玩意有啥用,其实说没用真没啥用,要说有用的话还特别的有用,就是你把这些东西都弄完,然后让你们的策划学会咋用这个工具,关卡搭建的事情交给ta就好了,一劳永逸。
而且如果你的地形地图需要放在服务器动态加载的话,这个功能就不错啦。

为了防止有人还没用过或者还不是很熟悉TileMap,这里我就贴一个链接,大家可以通过这个前辈的博客,来学习或了解一下TileMap。

通灵召唤术:教程|Unity中使用Tilemap快速创建2D游戏世界


如果上面那个文章大家都吃透了的话,我就开始讲解我的逻辑啦。

0x00) 导出\保存 地图

class 0x00_1{
	画笔\瓦片 也就是 Tile 我暂时把他定为两大类,Rule Tile 和 Default Tile;
	Rule Tile 就是导入插件后的可自定义的高级画笔,Defaule Tile就是默认的画笔;
	我们需要把这两种画笔区分开来,通过Unity编辑器拖拽,让我的脚本识别你的所有Tile;
	手动编辑Init();
	初始化所有Tile,并将Tile添加到Dictionary中方便后期载入地图使用。
}

class 0x00_2{
	在每一层TileMap上挂载一个ReadTilemap脚本,用来读取地图和保存地图;
}

0x01) 导入\载入 地图

class 0x01_1{
	通过Unity编辑器拖拽,让我的脚本识别所有需要载入地图的TileMap;
}	
class 0x01_2{
	通过TileMap名称,找到对应的地图文件,开始载入地图;
}

0x02) ReadTilemap.cs脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class ReadTilemap : MonoBehaviour
{
    /// 
    ///  Maps to be saved
    /// 
    private Tilemap tilemap;

    /// 
    /// The boundaries of the map need to be preserved
    /// 
    public BoundsInt area;

    // Start is called before the first frame update
    void Start()
    {
        tilemap = GetComponent<Tilemap>();
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.S))
        {
            ExportMap();
        }

        if (Input.GetKeyDown(KeyCode.C))
        {
            ClearMap();
        }
    }

    public void ExportMap()
    {
        TileBase[] tileArray = tilemap.GetTilesBlock(area);

        print(tilemap.name + " - Area size: " + tileArray.Length);

        int tilecount = 0;

        Dictionary<string, string> data = 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;
                }

                print(tilemap.GetTile(new Vector3Int(i, j, 0)).ToString());

                Vector3Int temp = new Vector3Int(i, j, 0);

                data.Add(i + "," + j, tilemap.GetTile(temp).name);

                tilecount++;
            }
        }

        string jsonData = LitJson.JsonMapper.ToJson(data);

        MapsMode._Instance.ExportMap(jsonData, tilemap.name);

        print(tilemap.name + " - The number of tiles in the area : " + tilecount);

    }

    public void ClearMap()
    {
        tilemap.ClearAllTiles();
    }



}

0x03) ReadTilemap.cs脚本使用讲解

class 0x03_1{
	每一个需要保存地图的TileMap都要挂载这个脚本;
	保存地图是会保存多个文件,每个TileMap会保存成一个文件;
}	
class 0x03_2{
	文件路径是Application.dataPath + "/Maps/" + Maps[i].name + ".txt";
	也就是你的Assets路径下的Maps文件夹;
	地图文件的名字就是TileMap的GameObject名字加上.txt;
}	
class 0x03_3{
	这个脚本使用的时候有一个需要注意的地方;
	public BoundsInt area;
	这个值需要手动输入;
	我先将一下这个是干啥用的,其实很简单,这个就是TileMap的边界;
	我是根据这个边界来遍历里面的所有Tile;
}
class 0x03_4{
	area边界的值可以用TileMap给到工具去测量;
}

0x04) MapsMode.cs脚本

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Tilemaps;

public class MapsMode : MonoBehaviour
{

    public static MapsMode _Instance;

    void Awake()
    {
        if (_Instance == null)
        {
            _Instance = this;
        }
    }

    /// 
    /// Rule Tile
    /// 
    public TileBase GrassPlatform;
    public TileBase Waterfall;


    /// 
    /// Default Tile
    /// 
    public TileBase[] EnvironmentProps;


    /// 
    /// All Tile
    /// 
    public Dictionary<string, TileBase> Tiles = new Dictionary<string, TileBase>();

    /// 
    /// All Tile Map
    /// 
    public Tilemap[] Maps;

    void Start()
    {
        Init();
    }

    public void Init()
    {
        Tiles.Add("GrassPlatform-RuleTile", GrassPlatform);
        Tiles.Add("Waterfall-RuleTile", Waterfall);
        for (int i = 0; i < EnvironmentProps.Length; i++)
        {
            Tiles.Add("EnvironmentProps_TileSet_" + i, EnvironmentProps[i]);
        }

        print("Init() is over.");
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.I))
        {
            ImportMap();

        }
    }

    public void ImportMap()
    {
        for (int i = 0; i < Maps.Length; i++)
        {
            FileInfo fi = new FileInfo(Application.dataPath + "/Maps/" + Maps[i].name + ".txt");

            if (fi.Exists)
            {
                StreamReader sr = fi.OpenText();

                string data = sr.ReadToEnd();

                print("ReadData ===> " + data);

                Dictionary<string, string> mapData = LitJson.JsonMapper.ToObject<Dictionary<string, string>>(data) as Dictionary<string, string>;

                foreach (KeyValuePair<string, string> kvp in mapData)
                {
                    string dataKey = kvp.Key;
                    string dataValue = kvp.Value;

                    string[] posData = dataKey.Split(',');

                    Vector3Int pos = new Vector3Int(int.Parse(posData[0]), int.Parse(posData[1]), 0);

                    TileBase tileData = getTile(dataValue);

                    Maps[i].SetTile(pos, tileData);

                    print("Create Tile ===> " + pos + " : " + tileData);
                }

                sr.Close();

                sr.Dispose();


            }
            else
            {
                print("ERROR !!! ===> File Not Found.");
            }
        }
    }

    public void ExportMap(string data, string mapName)
    {
        string filePath = Application.dataPath + "/Maps/" + mapName + ".txt";

        print("FilePath: " + Application.dataPath + "/Maps/" + mapName + ".txt");

        FileInfo fi = new FileInfo(filePath);

        if (fi.Exists)
        {
            print("File is Exists,Delete!");

            fi.Delete();

            fi.Refresh();
        }

        StreamWriter sw = fi.CreateText();

        sw.Write(data);

        sw.Flush();

        sw.Close();

        sw.Dispose();

        fi.Refresh();
    }


    public TileBase getTile(string tileName)
    {

        TileBase tile;

        if (Tiles.TryGetValue(tileName,out tile))
        {
            return tile;
        }
        else
        {
            return null;
        }

    }
}

0x05) MapsMode.cs脚本使用讲解

class 0x05_1{
	首选根据不同要求,要将项目中使用到的所有Tile都添加到这个脚本的记录当中;
	Rule TIle 与 默认的 Tile 要区分记录;
	通过Unity拖拽添加后,在Init()函数中初始化,将所有Tile都添加到同一字典中方便查找;
	Rule Tile比较好添加,但是默认的Tile有很多的瓦片,下面教大家一个简便的方法;
}	

class 0x05_2{
	导入Tile后需要手动编辑Init()函数;
	Rule Tile直接Add就OK;
	由于默认的Tile是数组,所以需要循环遍历Add;
}	
class 0x05_3{
	让脚本识别场景中所有的TileMap同0x05_1拖拽即可;
}	

0x06) 测试

class 0x06{
	运行项目,在控制台打印Init() is over.后即可测试;
	按下“S”即可保存地图 - Save;
	按下“C”即可清空地图 - Clear;
	按下“I”即可载入地图 - Import;
}	


0x07) 新建地形运行保存,删除部分地形在运行,先删除所有后再加载

0x08) 加载时添加延时效果


好啦,以上就是我的全部代码啦,这次就不贴工程文件了,毕竟写的不是可以应用到实际项目的,还有好多东西需要扩展。

例如Editor编辑扩展、动态加载Tile、动态加载Tilemap、保存的地图问价加密、加载地图文件时解密
再或者可以做成那种在服务器后台加载地图等等,欢迎大家一起与我共同扩展这个小功能吧。

最后,感谢大家的阅读~

本文永久链接:https://blog.csdn.net/Aries_H/article/details/101760141
转载请注明出处。谢谢。

你可能感兴趣的:(自学记录)