• 数据持久化(Json,二进制,PlayerPrefs)


    数据持久化

    数据持久化概述

    定义:内存中的数据模型与存储模型的相互转化。
    类比:将游戏数据存储到硬盘,从硬盘中读取游戏数据。

    1.数据持久化——JSON

    1.Json简介

    定义:JavaScript对象简谱,一种全国通用的轻量级的数据交换格式。主要在网络通信中传输数据,或本地数据的存储与读取。

    特点:纯文本,层级结构,都具有描述性,键值对存储。

    基本语法演示

    //大括号包裹的代表一个类
    {
    	"name" : "Shawn",
    	"age" : 18,
    	"sex" : true,
    	"height" : 1.8,
    	//中括号代表数组
    	"Students" : [{"name" : "小红","sex" : false},
    				  {"name" : "小名","sex" : false}],
    	"home":{"address" : "成都","street" : "春熙路"},
    	"son" : null
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Excel表转Json
    访问网址:https://www.bejson.com/json/col2json


    2.JsonUtility相关知识点

    • 1.定义:Unity自带的用于解析Json的公共类。
    • 2.作用:对象序列化为Json,Json反序列化为对象。
    • 3.用法:
      序列化
    //存储字符串到指定路径文件中
    //Param1:路径(可读写文件夹)
    //Param2:文本内容
    File.WriteAllText(Application.persistentDataPath + ”Test.json“,"hhhhhh");
    //从指定路径中读取字符串
    string str = File.ReadAllText(Application.persistentDataPath + "Test.json");
    
    //将类对象序列化为Json字符串
    Student s = new Student();
    string str = JsonUtility.ToJson(s);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    反序列化

    //读取文件中的Json字符串并将其转换为类对象
    类名 变量 = JsonUtility.FromJson<类名>(jsonStr);
    
    • 1
    • 2
    • 4.注意:
      – 1.自定义类序列化时需要加上特性 [System.Serializable]
      – 2.想要序列化私有的或者保护的变量可以加上特性 [SerializeField]
      – 3.JU不支持字典存储。
      – 4.JU存储null对象不会是null,而是变量类型默认值。
      – 5.JU无法直接读取(反序列化)数据集合(需要包装一层类)
      – 6.文本编辑器的编码格式必须是UTF-8,否则会报错。

    3.LitJson相关知识

    定义:第三方库,由C#编写,体积小速度快易于使用。
    使用步骤

    • 1.进入其Github官方文档。
    • 2.获取代码引入项目即可。(最终拷贝的是LitJson文件夹核心代码模块,无需其他的文件)
    • 3.用法
      序列化
    Student t = new Student();
    //序列化(引入命名空间)
    string jsonStr = JsonMapper.ToJson(t);
    File.WriteAllText(Application.persistentDataPath + ”Test.json“,jsonStr);
    
    • 1
    • 2
    • 3
    • 4

    反序列化

    jsonStr = File.WriteAllText(Application.persistentDataPath + ”Test.json“);
    //反序列化
    JsonData data = JsonMapper.ToObject(jsonStr);
    
    //需要用索引器去访问对象内容
    data["name"];
    
    //也可以通过泛型直接返回对象
    Student s = JsonMapper.ToObject<Student>(jsonStr);
    
    //LitJson可以直接读取数组,字典类型
    Student[] stus = JsonMapper.ToObject<Student[]>(jsonStr);
    List<Student> stus1 = JsonMapper.ToObject<List<Student>>();
    Dictionary<string,int> dic = JsonMapper.ToObject<Dictionary<string,int>>(jsonStr);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 4.注意
      – 1.相对JsonUtility来说不需要加特性。
      – 2.不能序列号私有变量。
      – 3.支持字典类型,字典的键建议都是字符串。
      – 4.需要引用LitJson 命名空间。
      – 5.可以准确地保存null类型。
      – 6.反序列化时要保证指定对象存在无参构造函数。
      – 7.文本编码必须是UTF-8。

    JsonUtility与LitJson对比
    相同点:

    • 1.都是用于Json的序列化反序列化
    • 2.Json文档编辑器必须都是UTF-8
    • 3.都是通过静态类(JsonUtility和JsonMapper)进行方法调用

    不同点:

    • 1.JU是Unity自带,LitJson是第三方,需要引入。
    • 2.JU使用自定义类需要加[System.Serializable]特性,LitJson不需要。
    • 3.JU支持私有变量(加特性[SerializField]),LitJson不需要。
    • 4.JU不支持字典,LitJson支持(key只能是字符串)。
    • 5.JU不能直接将数据反序列化为数据集合(字典),LitJson可以。
    • 6.JU堆自定义类不要求无参构造,LitJson要求必须存在无参构造。
    • 7.JU存储空对象时会存储默认值而不是null,LitJson会存null。

    4.JsonMgr管理器的书写

    /// 
    /// 序列化和反序列化Json时  使用的是哪种方案
    /// 
    public enum JsonType
    {
        JsonUtlity,
        LitJson,
    }
    
    /// 
    /// Json数据管理类 主要用于进行 Json的序列化存储到硬盘 和 反序列化从硬盘中读取到内存中
    /// 
    public class JsonMgr
    {
        private static JsonMgr instance = new JsonMgr();
        public static JsonMgr Instance => instance;
    
        private JsonMgr() { }
    
        //存储Json数据 序列化
        public void SaveData(object data, string fileName, JsonType type = JsonType.LitJson)
        {
            //确定存储路径
            string path = Application.persistentDataPath + "/" + fileName + ".json";
            //序列化 得到Json字符串
            string jsonStr = "";
            switch (type)
            {
                case JsonType.JsonUtlity:
                    jsonStr = JsonUtility.ToJson(data);
                    break;
                case JsonType.LitJson:
                    jsonStr = JsonMapper.ToJson(data);
                    break;
            }
            //把序列化的Json字符串 存储到指定路径的文件中
            File.WriteAllText(path, jsonStr);
        }
    
        //读取指定文件中的 Json数据 反序列化
        public T LoadData<T>(string fileName, JsonType type = JsonType.LitJson) where T : new()
        {
            //确定从哪个路径读取
            //首先先判断 默认数据文件夹中是否有我们想要的数据 如果有 就从中获取
            string path = Application.streamingAssetsPath + "/" + fileName + ".json";
            //先判断 是否存在这个文件
            //如果不存在默认文件 就从 读写文件夹中去寻找
            if(!File.Exists(path))
                path = Application.persistentDataPath + "/" + fileName + ".json";
            //如果读写文件夹中都还没有 那就返回一个默认对象
            if (!File.Exists(path))
                return new T();
    
            //进行反序列化
            string jsonStr = File.ReadAllText(path);
            //数据对象
            T data = default(T);
            switch (type)
            {
                case JsonType.JsonUtlity:
                    data = JsonUtility.FromJson<T>(jsonStr);
                    break;
                case JsonType.LitJson:
                    data = JsonMapper.ToObject<T>(jsonStr);
                    break;
            }
    
            //把对象返回出去
            return data;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    2.数据持久化——二进制

    1.二进制简介

    写在前面:C#变量的本质即为二进制。二进制文件读写的本质即为 将各类型变量转换为字节数组,将其直接存储到文件中,这样的作法不仅可以节约存储空间,提升效率,也可以提高安全性。

    各类型数据与二进制相互转化

    //将各类型转字节
    byte[] bytes = BitConverter.GetBytes(256);
    print(bytes[0]);//0
    print(bytes[1]);//1(第二位的1代表二进制八位,即十进制的256)
    
    //将字节数组转换为其他类型
    //param2:从第几个索引开始
    int i = BitConverter.ToInt32(bytes,0)
    
    //以指定编码格式转换字节数组
    byte[] bytes = Encoding.UTF8.GetBytes("我是猪");
    //以指定编码格式转换其他类型
    string s = Encoding.UTF8.GetString(bytes);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.文件操作

    API简介:

    //判断文件是否存在
    //Param:文件路径
    bool isExist = File.Exists(Application.dataPath + "/data.json");
    
    //创建文件
    FileStream fs = File.Create(Application.dataPath + "/data.json");
    
    //写入文件
    byte[] bytes = BitConverter.GetBytes(999);
    File.WriteAllBytes(Application.dataPath + "/data.json",bytes);
    
    //写入字符串数组文件(自动空行)
    string[] strs = new string[]("123","我是猪");
    File.WriteAllLines(Application.dataPath + "/data.json",strs);
    
    //将指定字符串存入指定路径(支持转义字符)
    File.WriteAllText(Application.dataPath + "/data.json","哈哈哈哈\n嘿嘿嘿");
    
    //读取文件(类比上述API,将Write改写为Read)
    
    //删除文件
    File.Delete(Application.dataPath + "/data.json");
    
    //复制文件
    //Param1:源文件
    //Param2:目标文件
    //Param3:如果已存在是否要覆盖原文件
    File.Copy(Application.dataPath + "/data.json",Application.dataPath + "/data.guan",true);
    
    //文件替换
    //Param3:备份文件路径
    File.Replace(Application.dataPath + "/data.json",Application.dataPath + "/data.guan",Application.dataPath + "/data备份.txt");
    
    //打开文件并写入或读取
    //Param2:文件模式,如果没有自动创建
    //Param3:访问模式,只读只写
    FileStream fs = File.Open(Application.dataPath + "/data.json",FileMode.OpenOrCreate,FileAccess.ReadWrite);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    3.流

    //打开或创建文件
    FileStream fs = new FileStream(Application.dataPath + "/data.json",FileMode.CreateOrCreate,FileAccess.ReadWrite);
    
    //文本字节长度
    print(fs.Length);
    
    //将字节写入文件,写入后,一定要执行一次
    fs.Flush();
    //关闭流,文件读写完毕执行
    fs.Close();
    //缓存资源的销毁回收
    fs.Dispose();
    
    
    //写入字节
    FileStream fs = new FileStream(Application.persistentDataPath + "/Lesson.txt",FileMode.OpenOrCreate,FileAccess.Write);
    byte[] test = new byte[1024];
    byte[] bytes = BitConverter.GetBytes(999);
    //Param2:开始字节索引
    //Param3:写入多少字节
    fs.Write(bytes,0,bytes.Length);
    
    //写入字符串时,先写入长度
    bytes = Encoding.UTF8.GetBytes("猪猪猪猪");
    fs.Write(BitConverter.GetBytes(bytes.Length),0,4);
    fs.Write(bytes,0,bytes.Length);
    fs.Flush();
    fs.Dispose();
    
    //====================一个一个读取=======================
    //读取字节
    FileStream fs2 = File.Open(Application.persistentDataPath + "/Lesson.txt",FileMode.Open,FileAccess.Read);
    
    //挨个读取字节数组
    byte[] bytes2 = new byte[4];
    int index = fs2.Read(bytes2,0,bytes.Length);
    int i = BitConverter.ToInt32(bytes2,0);
    print("取出来的第一个整数" + i);
    print("索引下标" + index);
    
    //读取字符串
    index = fs2.Read(bytes2,0,4);
    int length = BitConverter.ToInt32(bytes2,0);
    //重新声明一个字节数组
    bytes2 = new byte[length];
    index = fs.Read(bytes2,0,length);
    print(Encoding.UTF8.GetString(bytes2));
    fs.Dispose();
    
    //====================一次性读取========================
    FileStream fs3 = File.Open(Application.persistentDataPath + "/Lesson.txt",FileMode.Open,FileAccess.Read);
    byte[] bytes3 = new byte[fs3.Length];
    fs3.Read(bytes3,0,(int)fs3.Length);
    fs3.Dispose();
    print(BitConverter.ToInt32(bytes3,0));
    //得出字符串长度
    int length3 = BitConverter.ToInt32(bytes3,4);
    //得到字符串
    print(Encoding.UTF8.GetString(bytes3,8,length3));
    
    //===============通过using改进IO===================
    using(FileStream fs3 = File.Open(Application.persistentDataPath + "/Lesson.txt",FileMode.Open,FileAccess.Read))
    {
    	byte[] bytes3 = new byte[fs3.Length];
    	fs3.Read(bytes3,0,(int)fs3.Length);
    	fs3.Dispose();
    	print(BitConverter.ToInt32(bytes3,0));
    	//得出字符串长度
    	int length3 = BitConverter.ToInt32(bytes3,4);
    	//得到字符串
    	print(Encoding.UTF8.GetString(bytes3,8,length3));
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    4.文件夹

    //判断文件夹是否存在
    Directory.Exists(Application.dataPath + "/test");
    
    //创建文件夹并返回
    DirectoryInfo dirInfo = Directory.CreateDirectory(Application.dataPath + "/test");
    
    //删除文件夹
    //Param2:是否删除非空目录
    Directory.Delete(Application.dataPath + "/test",true);
    
    //得到指定路径下的所有文件夹名
    string[] strs = Directory.GetDirectories(Application.dataPath);
    
    //得到指定路径下所有文件名
    strs = Directory.GetFiles(Application.dataPath);
    
    //移动文件夹
    Directory.Move(Application.dataPath + "/test",Application.dataPath + "/123");
    
    //===============DirectoryInfo类==================
    
    //目录信息类DirectoryInfo
    //文件夹的全路径
    dirInfo.FullName;
    //文件夹名
    dirInfo.Name;
    //上级文件夹信息
    dirInfo = Directory.GetParent(Application.dataPath + "/test")
    
    //查找子文件夹以及文件信息
    DirectoryInfo[] dirInfos = dirInfo.GetDirectories();
    FileInfo[] fileInfos = dirInfo.GetFiles();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    5.二进制序列化工具

    补充知识1:Unity编辑器添加选项入口

    //必须为静态方法,通过类名点方式调用
    //必须存在两个或以上斜杠
    //这个类可以用在任何类中
    [MenuItem("GameTool/Test/GenerateExcelInfo")]
    private static void Test(){
    	Debug.Log("测试");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    补充知识2:刷新Project窗口内容

    AssetDatabase.Refresh();
    
    • 1

    补充知识3:Editor文件夹
    项目打包时,该文件夹下的文件无法打包。通常可以把编辑器相关代码放置其中。

    导入Excel的Dll文件
    打开Excel表

    [MenuItem("GameTool/OpenExcel")]
    private static void OpenExcel()
    {
    	//文件流打开Excel
    	using(FileStream fs = File.Open(Application.dataPath + "/ArtRes/Excel/PlayerInfo.xlsx",FileMode.Open,FileAccess.Read)){
    		//通过我们文件流获取Excel
    		IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
    		//将表中数据转换为DataSet数据类型,方便获取其中内容
    		DataSet result = excelReader.AsDataSet();
    		//得到Excel文件中所有信息
    		for (int i = 0;i < result.Tables.Count;i++)
    		{
    			print(result.Tables[i].TableName);//表名
    			print(result.Tables[i].Rows.Count);//行数
    			print(result.Tables[i].Columns.Count);//列数
    		}
    		fs.Close();
    		
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    获取Excel表中单元格的信息

    [MenuItem("GameTool/读取具体信息")]
    private static void ReadExcel()
    {
    	using(FileStream fs = File.Open(Application.dataPath + "/ArtRes/Excel/PlayerInfo.xlsx",FileMode.Open,FileAccess.Read)){
    		IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
    		DataSet result = excelReader.AsDataSet();
    
    		for (int i = 0;i < result.Tables.Count;i++)
    		{
    			//得到一张表的具体数据
    			DataTable table = result.Tables[i];
    			//得到其中一行数据
    			DataRow row = table.rows[0];
    			//得到某一个单元格的信息
    			string ans = row[1].ToString();
    			
    			//遍历所有行的数据
    			for (int j = 0;j < table.Rows.Count;j++){
    				row = table.Rows[j];
    				//得到每一个单元格的内容信息
    				for (int k = 0;k < table.Columns.Count;k++){
    					print(row[k].ToString());
    				}
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    需求分析:通过一张Excel表格生成以下信息:

    • 1.数据结构类
    • 2.装数据的容器
    • 3.二进制存储文件
    写一个工具读取Excel中的信息

    (放入Editor下,无需打包)

    public class ExcelTool 
    {
    	//文件目录中Excel的路径
    	public static string EXCEL_PATH = Application.dataPath + "/ArtRes/Excel/";
    
    	//生成的数据结构类的路径
    	public static string DATA_CLASS_PATH = Application.dataPath + "/Scripts/ExcelData/DataClass/";
    
    	//生成的容器类的路径
    	public static string DATA_CONTAINER_PATH = Application.dataPath + "/Scripts/ExcelData/DataContainer/";
    
    	//生成的二进制文件路径
    	public static string DATA_BINARY_PATH = Application.streamingAssetsPath + "/Binary/";
    
    	//在编辑器中添加按钮
    	[MenuItem("GameTool/GenerateExcel")]
    	private static void GenerateExcelInfo()
    	{
    		//得到指定路径下所有文件信息(Excel表)
    		FileInfo[] infos = Directory.CreateDirectory(EXCEL_PATH).GetFiles();;
    
    		//数据表容器
    		DataTableCollection tableCollection;
    		
    		foreach(FileInfo info in infos)
    		{
    			//处理excel文件
    			if (info.Extension != ".xlsx" && info.Extension != ".xls")
    			continue;
    
    			//使用流的相关知识获取表内数据
    			using(FileStream fs = info.Open(FileMode.Open,FileAccess.Read))
    			{
    				IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
    				tableCollection = excelReader.AsDataSet().Tables;
    				fs.Close();
    			}
    			//得到每一张表
    			foreach(DataTable table in tableCollection)
    			{
    				//生成数据结构类
    				GenerateExcelDataClass(table);
    				//生成容器类
    				GenerateExcelContainer(table);
    	 			//生成二进制数据
    	 			GenerateExcelBinary(table);
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    生成数据结构类的逻辑方法
    private static void GenerateExcelDataClass(DataTable table)
    {
    	//字段名字
    	DataRow rowName = GetRowInfo(table,0);
    	//字段类型
    	DataRow rowType = GetRowInfo(table,1);
    
    	if (!Directory.Exists(DATA_CLASS_PATH))
    		Directory.CreateDirectory(DATA_CLASS_PATH);
    
    	//字符串拼接生成代码
    	string code = "public class " + table.TableName + "\n{\n";
    
    	//拼接变量
    	for (int i = 0;i < table.Columns.Count;i++)
    	{
    		code += "	public " + rowType[i].ToString() + " " + rowName[i].ToString() + ";\n";
    	}
    
    	code += "}";
    
    	File.WriteAllText(DATA_CLASS_PATH + table.TableName + ".cs", code);
    
    	//刷新Projects窗口
    	AssetDatabase.Refresh();
    }
    
    //获得指定行所在行信息
    //获得变量名所在行信息
    private static DataRow GetRowInfo(DataTable table,int row)
    {
    	return table.Rows[row];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    生成容器(字典)类
    private static void GenerateExcelContainer(DataTable table)
    {
    	int keyIndex = GetKeyIndex(table);
    	DataRow rowType = GetRowInfo(table,1);
    	//没有路径则创建路径
    	if (!Directory.Exists(DATA_CONTAINER_PATH))
    		Directory.CreateDirectory(DATA_CONTAINER_PATH);
    
    	//字符串拼接生成类
    	string code = "using System.Collections.Generic;\n";
    	code += "public class " + table.TableName + "Container" + "\n{\n";
    	code += "	public Dictionary<" + rowType[keyIndex].ToString() + ", " + table.TableName + "> dataDic = new Dictionary<" + rowType[keyIndex].ToString() + ", " + table.TableName + ">();\n";
    	code += "}";
    
    	File.WriteAllText(Data_CONTAINER_PATH + table.TableName + "Container.cs", code);
    
    	AssetDatabase.Refresh();
    }
    
    //找到主键所在的索引
    private static int GetKeyIndex(DataTable table)
    {
    	DataRow row = table.Rows[2];
    	for (int i = 0;i < table.Columns.Count; i++)
    	{
    		if (row[i].ToString() == "key")
    		{
    			return i;
    		}
    	}
    	return default(int);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    生成Excel二进制数据

    (放在StreamingAssets包中,打包出去也要读取数据(只读))

    private static void GenerateExcelBinary(DataTable table)
    {
    	//判断路径下是否存在文件夹
    	if (!Directory.Exists(DATA_BINARY_PATH))
    		Directory.CreateDirectory(DATA_BINARY_PATH);
    
    	//创建一个二进制文件进行写入
    	using(FileStream fs = new FileStream(DATA_BINARY_PATH + table.TableName + ".shawn",FileMode.OpenOrCreate,FileAccess.Write))
    	{
    		//1.优先存储我们要写多少行
    		fs.Write(BitConverter.GetBytes(table.Rows.Count - 4),0,4);
    		//2.存储主键的变量名(id)
    		string keyName = GetRowInfo(table,0)[GetKeyIndex(table)].ToString();
    		byte[] bytes = Encoding.UTF8.GetBytes(keyName);
    		//存储字符串字节数组的长度
    		fs.Write(BitConverter.GetBytes(bytes.Length),0,4);
    		//存储字符串字节数组
    		fs.Write(bytes,0,bytes.Length);
    		
    		//数据开始索引可定义为全局变量,后续可直接修改变量
    		DataRow row;		
    		//得到类型
    		DataRow rowType = GetRowInfo(table,1);
    		for (int i = BEGIN_INDEX;i < table.Rows.Count;i++)
    		{
    			row = table.Rows[i];
    			for (int j = 0;j < table.Columns.Count;j++)
    			{
    				switch(rowType[j].ToString())
    				{
    					case "int":
    						fs.Write(BitConverter.GetBytes(int.Parse(row[j].ToString())),0,4);
    						break;
    					case "float":
    					fs.Write(BitConverter.GetBytes(float.Parse(row[j].ToString())),0,4);
    						break;
    					case "bool":
    					fs.Write(BitConverter.GetBytes(int.Parse(row[j].ToString())),0,1);
    						break;
    					case "string":
    						bytes = Encoding.UTF8.GetBytes(row[j].ToString());
    						fs.Write(BitConverter.GetBytes(bytes.Length),0,4);
    						fs.Write(bytes,0,bytes.Length);
    						break;
    				}
    			}
    		}
    		fs.Close();
    	}
    	AssetDatabase.Refresh();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    使用二进制数据读取使用

    3.数据持久化——PlayerPrefs

    3.1 概述

    概念:PlayerPrefs是可以用于读取玩家数据的公共类。

    3.2 基础语法

    存储相关

    //存储三种数据类型
    PlayerPrefs.SetInt("Age",18);
    PlayerPrefs.SetFloat("Height",177.5f);
    PlayerPrefs.SetString("Name","Shawn");
    
    //调用Save会把数据存入硬盘
    PlayerPrefs.Save();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    读取相关

    //运行时,不需要Save也能读取到
    string name = PlayerPrefs.GetString("Name");
    //如果找不到对应值,函数会返回第二个参数
    int age = PlayerPrefs.GetInt("age",100);
    
    • 1
    • 2
    • 3
    • 4

    删除相关

    //删除指定键
    PlayerPrefs.DeleteKey("age");
    //删除所有信息
    PlayerPrefs.DeleteAll();
    
    • 1
    • 2
    • 3
    • 4

    3.3 PlayerPrefs存储位置

    Windows

    存储路径:HKCU\Software\[公司名称]\[产品名称] 下的注册表中
    打开方式:

    • 1.运行 regedit
    • 2.进入HKEY_CURRENT_USER->SOFTWARE
    • 3.进入Unity->UnityEditor->公司名称->产品名称

    Android
    存储路径:/data/data/包名/shared_prefs/pkg-name.xml

    IOS
    存储路径:/Libraray/Preferences/[应用ID].plist

  • 相关阅读:
    极速视觉:揭秘YOLO如何革新目标检测速度
    一起Talk Android吧(第四百二十七回:在Android中使用MQTT通信一)
    最长上升子序列模型
    27、Flink 的SQL之SELECT (窗口函数)介绍及详细示例(3)
    Leetcode—7.整数反转【中等】
    PHP毕业设计项目作品源码选题(10)校园新生自助报到系统毕业设计毕设作品开题报告
    【算法】湖心岛上的数学梦--用c#实现一元多次方程的展开式
    vue中替换全局字体
    最优化——凸优化概述
    Qt多线程及线程池的使用笔记
  • 原文地址:https://blog.csdn.net/qq_55071342/article/details/127307129