【Unity实战教程】unity用二进制存档和读档地图属性

前言

  1. 本内容为本人原创提供
  2. 本内特为学习者提供原创完整的工程源码下载,可参考学习或其他用途。
  3. 本内容从地图相关属性出发,对数据存取操作,一步一步推进,能直观感受。
  4. 如果后续有其他的存取游戏数据的方式,将继续提供给大家借鉴参考,希望能协助大家完成自己项目。
  5. 本文涉及地图属性的难度等级,消耗变量,地图元素坐标等数据的存取。

目录

前言

一、什么是Binary存取

二、BinaryWriter和BinaryReader认识

1、BinaryWriter

2、BinaryReader

三、unity实战

1、c#对二进制数据存取操作的起始工作

2、其他准备

3、保存数据

4、读取数据

5、删除数据

四 、游戏中其他函数方法

五、运行图示

六、总结

七、工程下载地址  


一、什么是Binary存取

        binary 是二进制,也就是0/1计数方法,是字节序列。

        binary 存取是可用于存储非传统数据,如图像、音频和视频文件、程序可执行文件等。也可以用于与字符串类似的目的(例如,Word格式存储文档),这种特性也可以让我们可以用于储存游戏数据。


二、BinaryWriter和BinaryReader认识

1、BinaryWriter

         是将值写入当前流。

Write(String)将有长度前缀的字符串按 BinaryWriter 的当前编码写入此流,并根据所使用的编码和写入流的特定字符,提升流的当前位置。
Write(Int64)将 8 字节带符号整数写入当前流,并将流的位置提升 8 个字节。
Write(Int32)将 4 字节带符号整数写入当前流,并将流的位置提升 4 个字节。
Write(Int16)将 2 字节无符号整数写入当前流,并将流的位置提升 2 个字节。
Write(Boolean)将单字节 Boolean 值写入当前流,其中 0 表示 false,1 表示 true。
Write(Byte[], Int32, Int32)将字节数组区域写入当前流。
Write(Char[])将字符数组写入当前流,并根据所使用的 Encoding 和向流中写入的特定字符,提升流的当前位置。
......

        内容还是比较多,不做具体罗列了,本文主要采用 Write(String)Write(Int32),保存地图属性数据。

2、BinaryReader

        从基础流中读取字节,并将流的当前位置前移。

Read()    从基础流中读取字符,并根据所使用的 Encoding 和从流中读取的特定字符,提升流的当前位置。
Read(Span<Byte>)    从当前流读取字节序列,并将流中的位置向前移动读取的字节数。
Read(Span<Char>)    从当前流中读取与提供的缓冲区长度相同的字符数,将其写入提供的缓冲区,然后根据所使用的 Encoding 和从流中读取的特定字符,将当前位置前移。
Read(Byte[], Int32, Int32)从字节数组中的指定点开始,从流中读取指定的字节数。
Read(Char[], Int32, Int32)从字符数组中的指定点开始,从流中读取指定的字符数。

        本文主要采用 Read()  读取地图属性数据。

        注:以上表格数据引用于微软文档。


三、unity实战

        本次实战采用地图属性单元展开,对地图属性进行存档和读档操作。

1、c#对二进制数据存取操作的起始工作


        1.1、地图属性:

        首先,创建一个地图属性类,把地图内容属性规范好,当然也能创建地图属性的结构体。如果数据多,并且比较复杂,可以选择类:

public class Map
{
    public int map_id;
    public int map_lv;
    public string map_name;
    public int map_stone;
    public int map_general;
    public string map_bgstr;
    public List<string> map_monsteridList;
    public Dictionary<string, Vector3> map_objectDic;

    ...
}

        类中的变量需要显示在 inspector 面板上,就不能用 get; set 

        1.2、构造Map函数

        构造map函数,并给地图的参数带入其中

/// <summary>
/// 结构化
/// </summary>
/// <param name="id"></param>
/// <param name="monsterList"><怪物文本集合/param>
/// <param name="mapbossList">地图名文本集合</param>
/// <param name="mapnameList">需要生成的源obj</param>
/// <param name="map_objectmeshList">源物体</param>
/// <param name="meshFilter">物体的meshFilter</param>
public Map(int id, List<string> monsterList, List<string> mapbossList, List<string> mapnameList, List<GameObject> map_objectmeshList, MeshFilter meshFilter)
{
    ...
}

        设置map属性

     //数据设置
     map_id = id;
     map_lv = 1;
     map_name = mapnameList[UnityEngine.Random.Range(0, mapnameList.Count)];
     map_stone = UnityEngine.Random.Range(1, 10);
     map_general = UnityEngine.Random.Range(0, mapbossList.Count);
     map_bgstr = string.Empty;

        从第一个参数,monsterList 中随机获取 count 个不重复元素(GetListRandomElements 方法本文后面查阅)

     int count = UnityEngine.Random.Range(1, monsterList.Count - 3);
     map_monsteridList = new List<string>(monsterList.GetListRandomElements(count));

        从第二个参数,map_objectmeshList 中随机获取 6 个不重复元素(具体数量自己定)

     int dicount = 6;
     List<GameObject> map_objectmeshlist = 
         new List<GameObject>(map_objectmeshList.GetListRandomElements(dicount)) ;

        获取mesh的顶点坐标集合,并将地表的 vertices 坐标转为世界坐标 ,坐标一定要转世界坐标这样是为了放置地图元素,比如树木,石头等,需要利用世界坐标

     List<Vector3> vector3list = new List<Vector3>();
     Mesh mesh = meshFilter.mesh;
     Vector3[] vertices = new Vector3[mesh.vertices.Length];
     for (int i = 0; i < mesh.vertices.Length; i++)
     {
         vertices[i] = meshFilter.transform.TransformPoint(mesh.vertices[i]);
     }

        从 vertices 中随机获取 dicount 个不重复元素,并根据物体的名字和坐标,添加到字典map_objectDic

    vector3list = new List<Vector3>(vertices.ToList().GetListRandomElements(dicount));
    map_objectDic = new Dictionary<string, Vector3>();
    for (int i = 0; i < dicount; i++)
    {
        map_objectDic.Add(map_objectmeshlist[i].name, vector3list[i]);
    }

        整个map属性类就完毕了。


2、其他准备

         2.1、添加引用

using System.IO;
using System.Linq;

        System.IO,对数据流进行操作。

        System.Linq,数据增,添,删,查等操作。

        2.2、文件路径设置

    private string binaryPath = Application.dataPath + "/Save/gameBinary.map";

        文件格式定为 .map

        2.3、创建游戏管理类

        创建一个管理游戏的类,并且单例它     

    private static Gamemanager _instance;
    public static Gamemanager Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Gamemanager();
            }
            return _instance;
        }
    }

3、保存数据

        将地图数据以二进制形式进行保存

        2.1、按路径取文件流

    FileStream steam = File.Open(binaryPath, FileMode.Create);

        2.2、二进制取流文件

  //二进制取流文件
  using (BinaryWriter header = new BinaryWriter(steam))
  {

   ...

  }

        保存map类中的声明的所有变量数据

     header.Write(Gamemanager.Instance.mapList.Count);
     for (int i = 0; i < Gamemanager.Instance.mapList.Count; i++)
     {
         header.Write(Gamemanager.Instance.mapList[i].map_id);
         header.Write(Gamemanager.Instance.mapList[i].map_lv);
         header.Write(Gamemanager.Instance.mapList[i].map_name);

         header.Write(Gamemanager.Instance.mapList[i].map_stone);
         header.Write(Gamemanager.Instance.mapList[i].map_general);
         header.Write(Gamemanager.Instance.mapList[i].map_bgstr);

         header.Write(Gamemanager.Instance.mapList[i].map_monsteridList.Count);
         for (int j = 0; j < Gamemanager.Instance.mapList[i].map_monsteridList.Count; j++)
         {
             header.Write(Gamemanager.Instance.mapList[i].map_monsteridList[j]);
         }

         header.Write(Gamemanager.Instance.mapList[i].map_objectDic.Count);
         for (int j = 0; j < Gamemanager.Instance.mapList[i].map_objectDic.Count; j++)
         {
             KeyValuePair<string, Vector3> pair = Gamemanager.Instance.mapList[i].map_objectDic.ElementAt(j);
             header.Write(pair.Key);
             header.Write(pair.Value.ToString());
         }
     }
  •    保存数组、字典(ListArrayDictionary)数据,一定记得遍历前保存它们的 count 长度
  •    字典 key value 分别保存
  •    保存数据一定要根据声明的变量,逐一保存

4、读取数据
FileStream steam = File.Open(binaryPath, FileMode.Open);
using (BinaryReader reader = new BinaryReader(steam, Encoding.UTF8))
{
    int count = reader.ReadInt32();
    for (int i = 0; i < count; i++)
    {
        Gamemanager.Instance.mapList[i].map_id = reader.ReadInt32();
        Gamemanager.Instance.mapList[i].map_lv = reader.ReadInt32();
        Gamemanager.Instance.mapList[i].map_name = reader.ReadString();

        Gamemanager.Instance.mapList[i].map_stone = reader.ReadInt32();
        Gamemanager.Instance.mapList[i].map_general = reader.ReadInt32();
        Gamemanager.Instance.mapList[i].map_bgstr = reader.ReadString();

        int count2 = reader.ReadInt32();
        Gamemanager.Instance.mapList[i].map_monsteridList = new List<string>();
        for (int j = 0; j < count2; j++)
        {
            Gamemanager.Instance.mapList[i].map_monsteridList.Add(reader.ReadString());
        }

        int count3 = reader.ReadInt32();
        Gamemanager.Instance.mapList[i].map_objectDic = new Dictionary<string, Vector3>();
        for (int j = 0; j < count3; j++)
        {   
            string key = reader.ReadString();
            string value = reader.ReadString();
            //字符转坐标
            string[] sarry = value.Replace("(", string.Empty).Replace(")", string.Empty).Split(',');
            Vector3 v3 = new Vector3(float.Parse(sarry[0]), float.Parse(sarry[1]), float.Parse(sarry[2]));
            //加入到字典中
            Gamemanager.Instance.mapList[i].map_objectDic.Add(key, v3);
        }
    }
}
  •   读取数组,字典时,优先读取它们的长度
  •   读取的方法 ReadInt32 ,ReadString 不要搞错了,不然会报错
  •   读取后,如果有特殊变量,比如坐标,记得字符转坐标,(stringVector3) 

5、删除数据

        判断文件是否存在

    //判断文件是否存在
    public bool IsHaveBinaryDoc()
    {
        return File.Exists(binaryPath);
    }

         删除文件

    //删除文件
    public void DeleteBinaryDoc()
    {
        if (IsHaveBinaryDoc())
            File.Delete(binaryPath);
    }

四 、游戏中其他函数方法

        4.1、初始化

        初始化是游戏中经常需要的方法,帮我们准备一些进入游戏的初始数据和视觉展示,这些视觉包括,UI,模型,shader渲染等。

        初始化游戏中的模型,创建模型数据集合

    //从playerfab LIST 中生成地图实体
    public void CreateMapObj()
    {
        map_objlist = new List<GameObject>();

        for (int i = 0; i < map_objectmeshList.Count; i++)
        {
            GameObject obj = Instantiate(map_objectmeshList[i], mapObj.transform);
            obj.name = obj.name.Replace("(Clone)", string.Empty);
            map_objlist.Add(obj);
            obj.SetActive(false);
        }
        map_objlist.GetListRandomElements(mapList.Count);
    }

        接着初始化

    //初始化
    public void InitGame() 
    {
        mapattributeList = FindObjectsOfType<Mapattribute>().ToList();
        mapattributeList.Sort((x, y) => x.mapMent.map_id.CompareTo(y.mapMent.map_id));
        mapList = new List<Map>();
        int tempId = 0;
        foreach (var item in mapattributeList)
        {
            item.terrainMeshf.gameObject.SetActive(true);
            item.mapMent = new Map(tempId, monsterList, mapbossList, mapnameList, map_objectmeshList, item.terrainMeshf.GetComponent<MeshFilter>());
            item.terrainMeshf.gameObject.SetActive(false);
            mapList.Add(item.mapMent);
            tempId++;
        }
        chooseMap = mapList[chooseIndex];
        CreateMapObj();
    }

        本文主要讲解二进制数据存取,这里就不对这些细讲,提供源码,可以自己下载看看,希望能给你学习提供帮助。


五、运行图示


六、总结

  •    与 xml/jsong/excel 存取数据,二进制存取执行效率是其中最好。
  •    二进制读写进制不方便直观查看游戏数据,也不利于临时修改存档中的数据,但是可以用二进制存取 复杂的,庞大的数据,比如地图数据。
  •    如果需要保存数组和字典数据,必须先存它们的长度,读取的时候要先读取长度,否则报    错。
  •    保存的顺序和读取的顺序必须保持一致,否则报错。

写着写着就这么多了,可能不是特别全,不介意费时就看看吧。有时间还会接着更新。


七、工程下载地址  

【unity实战案例】Binary存取工程下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学与用

原创作者,在线要饭

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

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

打赏作者

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

抵扣说明:

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

余额充值