【Unity】利用二进制数据持久化 【练习学习项目/有不足之处欢迎斧正/侵删】
1.为编辑器菜单栏添加新的选项入口
通过Unity提供的MenuItem特性在菜单栏添加选项按钮
特性名:MenuItem
命名空间:UnityEditor
要求:一定是静态方法;新建的这个菜单栏按钮 必须有至少一个斜杠 不然会报错 它不支持只有一个菜单栏入口 ;这个特性可以用在任意的类当中
[MenuItem("GameTool/Test")]private static void Test(){Directory.CreateDirectory(Application.dataPath + "/测试文件夹");AssetDatabase.Refresh();}
同时,通过以上方式,可以调用后自动刷新窗口
类名:AssetDatabase
命名空间:UnityEditor
方法:Refresh
补充:Editor文件夹
Editor文件夹可以放在项目的任何文件夹下,可以有多个,放在其中的内容,项目打包时不会被打包到项目中。一般编辑器相关代码都可以放在该文件夹中
同时不能再非editor的文件夹下的脚本调用editor文件夹下的脚本
2.导入ExcelDll包
Excel表的本质:Excel表本质上也是一堆数据,有自己的存储读取规则
DLL文件用来解析Excel文件的
Dll文件:库文件,可以理解为它是许多代码的集合,将相关代码集合在库文件中可以方便迁移和使用,方便用户直接使用
(建议放在editor文件夹下)
3.Excel数据读取
利用FileStream读取文件流,再利用IExcelDataReader类,从流中读取Excel数据,使用DataSet数据集合类,将Excel数据转存进其中方便读取
using (FileStream fs = File.Open(Application.dataPath + "/ArtRes/Excel/PlayerInfo.xlsx", FileMode.Open, FileAccess.Read )){//通过文件流获取Excel数据IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);//将excel表中的数据转换为DataSet数据类型 方便获取其中的内容DataSet result = excelReader.AsDataSet();//得到Excel文件中的所有表信息for (int i = 0; i < result.Tables.Count; i++){Debug.Log("表名:" + result.Tables[i].TableName);Debug.Log("行数:" + result.Tables[i].Rows.Count);Debug.Log("列数:" + result.Tables[i].Columns.Count);}fs.Close();}
获取Excel表中单元格的信息
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;for (int j = 0; j < table.Rows.Count; j++){//得到每一行的信息row = table.Rows[j];for (int k = 0; k < table.Columns.Count; k++){Debug.Log(row[k].ToString());}}}fs.Close();}
DataTable 数据表类:表示Excel文件中的一个表
DataRow 数据行类:表示某张表中的一行数据
可以根据表中数据来动态的生成相关数据:数据结构类;容器类;2进制数据
为什么不直接读取Excel表而要把它转成2进制数据?
提升读取效率和数据安全性
4.制定配置表的相关规则
数据结构类
容器:放一个字典,存储整张表的数据,对应数据结构类的类型,key利用唯一ID来设置(保证主键不一样)
二进制数据
5.读取Excel目录下所有的excel文件
声明文件存放的路径
public static string EXCEL_PATH = Application.dataPath + "/ArtRes/Excel/";
加载指定路径中的所有Excel文件,用于生成对应的3个文件
DirectoryInfo dInfo = Directory.CreateDirectory(EXCEL_PATH);//得到指定路径中的所有文件信息 相当于就是得到所有的Excel表FileInfo[] files = dInfo.GetFiles();
遍历数组中所有的内容,其中会有meta文件,之后操作中无需对其进行处理
for (int i = 0; i < files.Length; i++){if (files[i].Extension != ".xlsx" &&files[i].Extension != ".xls")continue;
}
files[i].Extension 记录的是文件的后缀名
设置数据类容器
DataTableCollection tableConllection;
打开一个Excel文件得到其中的所有表的数据
using (FileStream fs = files[i].Open(FileMode.Open, FileAccess.Read)){IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);tableConllection = excelReader.AsDataSet().Tables;fs.Close();}
遍历文件中的所有表的信息
foreach (DataTable table in tableConllection){Debug.Log(table.TableName);}
6.生成数据结构类
创建生成Excel表对应的数据结构类
private static void GenerateExcelDataClass(DataTable table){}
获取变量名所在行
private static DataRow GetVariableNameRow(DataTable table){return table.Rows[0];}
获取变量类型所在行
private static DataRow GetVariableTypeRow(DataTable table){return table.Rows[1];}
回到生成Excel表对应的数据结构类
private static void GenerateExcelDataClass(DataTable table){//字段名行DataRow rowName = GetVariableNameRow(table);//字段类型行DataRow rowType = GetVariableTypeRow(table);}
设置数据结构类脚本存储位置路径
public static string DATA_CLASS_PATH = Application.dataPath + "/Scripts/ExcelData/DataClass/";
生成对应的数据结构类脚本(通过代码进行字符串拼接 然后存进文件)
//判断路径是否存在 没有的话 就创建文件夹if (!Directory.Exists(DATA_CLASS_PATH))Directory.CreateDirectory(DATA_CLASS_PATH);string str = "public class " + table.TableName + "\n{\n";//变量进行字符串拼接for (int i = 0; i < table.Columns.Count; i++){str += " public " + rowType[i].ToString() + " " + rowName[i].ToString() + ";\n";}str += "}";
把拼接好的字符串存到指定文件中去
File.WriteAllText(DATA_CLASS_PATH + table.TableName + ".cs", str);
最后刷新Project窗口
AssetDatabase.Refresh();
7.生成容器类
利用字典,表示一张表的数据
创建Excel表对应的数据容器类
private static void GenerateExcelContainer(DataTable table){}
获取主键索引
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 0;}
获取变量类型所在行
private static DataRow GetVariableTypeRow(DataTable table){return table.Rows[1];}
得到主键索引
int keyIndex = GetKeyIndex(table);
得到字段类型行
DataRow rowType = GetVariableTypeRow(table);
设置容器类脚本存储位置路径
public static string DATA_CONTAINER_PATH = Application.dataPath + "/Scripts/ExcelData/Container/";
具体容器类内部逻辑
//没有路径创建路径if (!Directory.Exists(DATA_CONTAINER_PATH))Directory.CreateDirectory(DATA_CONTAINER_PATH);string str = "using System.Collections.Generic;\n";str += "public class " + table.TableName + "Container" + "\n{\n";str += " ";str += "public Dictionary<" + rowType[keyIndex].ToString() + ", " + table.TableName + ">";str += "dataDic = new " + "Dictionary<" + rowType[keyIndex].ToString() + ", " + table.TableName + ">();\n";str += "}";File.WriteAllText(DATA_CONTAINER_PATH + table.TableName + "Container.cs", str);
刷新Project窗口
AssetDatabase.Refresh();
8.生成二进制数据
生成excel 2进制数据
private static void GenerateExcelBinary(DataTable table){}
可以利用StreamingAssets在非编辑模式下,是只读状态
2进制数据存储位置路径
public static string DATA_BINARY_PATH = Application.streamingAssetsPath + "/Binary/";
创建一个2进制文件进行写入
FileStream fs = new FileStream(DATA_BINARY_PATH + table.TableName + ".yuan", FileMode.OpenOrCreate, FileAccess.Write)
存储具体的excel对应的2进制信息
存储我们需要写多少行的数据,方便读取
fs.Write(BitConverter.GetBytes(table.Rows.Count - 4), 0, 4);
存储主键的变量名
string keyName = GetVariableNameRow(table)[GetKeyIndex(table)].ToString();byte[] bytes = Encoding.UTF8.GetBytes(keyName);
存储字符串字节数组的长度
fs.Write(BitConverter.GetBytes(bytes.Length), 0, 4);
存储字符串字节数组
fs.Write(bytes, 0, bytes.Length);
得到类型行 根据类型来决定应该如何写入数据
DataRow rowType = GetVariableTypeRow(table);
遍历所有内容的行 进行2进制的写入
DataRow row;DataRow rowType = GetVariableTypeRow(table);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(bool.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();
最后别忘了刷新Project窗口
AssetDatabase.Refresh();
9.Excel数据文件的使用
创建用于存储所有Excel表数据的容器
private Dictionary<string, object> tableDic = new Dictionary<string, object>();
数据存储的位置
private static string SAVE_PATH = Application.persistentDataPath + "/Data/";
读取excel表对应的2进制文件,根据结构体类名进行判断
using (FileStream fs = File.Open(DATA_BINARY_PATH + typeof(K).Name + ".tang", FileMode.Open, FileAccess.Read)){}
记录读取信息
byte[] bytes = new byte[fs.Length];fs.Read(bytes, 0, bytes.Length);fs.Close();//用于记录当前读取了多少字节了int index = 0;//读取多少行数据int count = BitConverter.ToInt32(bytes, index);index += 4;//读取主键的名字int keyNameLength = BitConverter.ToInt32(bytes, index);index += 4;string keyName = Encoding.UTF8.GetString(bytes, index, keyNameLength);index += keyNameLength;
创建容器类对象(通过反射)
Type contaninerType = typeof(T);object contaninerObj = Activator.CreateInstance(contaninerType);
(Activator根据type创建对象)
得到数据结构类的Type
Type classType = typeof(K);
通过反射来得到数据结构类所有字段的信息
FieldInfo[] infos = classType.GetFields();
读取每一行的信息
for (int i = 0; i < count; i++){//实例化一个数据结构类 对象object dataObj = Activator.CreateInstance(classType);foreach (FieldInfo info in infos){if( info.FieldType == typeof(int) ){//相当于就是把2进制数据转为int 然后赋值给了对应的字段info.SetValue(dataObj, BitConverter.ToInt32(bytes, index));index += 4;}else if (info.FieldType == typeof(float)){info.SetValue(dataObj, BitConverter.ToSingle(bytes, index));index += 4;}else if (info.FieldType == typeof(bool)){info.SetValue(dataObj, BitConverter.ToBoolean(bytes, index));index += 1;}else if (info.FieldType == typeof(string)){//读取字符串字节数组的长度int length = BitConverter.ToInt32(bytes, index);index += 4;info.SetValue(dataObj, Encoding.UTF8.GetString(bytes, index, length));index += length;}}
每次读取完一行的数据,便把这个数据添加到容器对象中
object dicObject = contaninerType.GetField("dataDic").GetValue(contaninerObj);//通过字典对象得到其中的 Add方法MethodInfo mInfo = dicObject.GetType().GetMethod("Add");//得到数据结构类对象中 指定主键字段的值object keyValue = classType.GetField(keyName).GetValue(dataObj);mInfo.Invoke(dicObject, new object[] { keyValue, dataObj });
把读取完的表记录下来
tableDic.Add(typeof(T).Name, contaninerObj);fs.Close();
10.工具包导出
书写相关帮助文档
创建测试工程、
BinaryDataMgr.Instance.InitData();TowerInfoContainer data = BinaryDataMgr.Instance.GetTable<TowerInfoContainer>();
相关文章:
【Unity】利用二进制数据持久化 【练习学习项目/有不足之处欢迎斧正/侵删】
1.为编辑器菜单栏添加新的选项入口 通过Unity提供的MenuItem特性在菜单栏添加选项按钮 特性名:MenuItem 命名空间:UnityEditor 要求:一定是静态方法;新建的这个菜单栏按钮 必须有至少一个斜杠 不然会报错 它不支持只有一个菜单…...
做伦敦银要等怎样的价格与行情?
对于不同的伦敦银投资者来说,合适的入市价格和好的行情机会,标准可能并不一样,因为不同人有不同的交易策略、风险偏好和盈利目标。对于喜欢做趋势跟踪的投资者来说,一波明显而持续的上涨或下跌趋势,可能就是最好的行情…...
SpringBoot多数据源切换 多数据源事务解决方案 二
https://zhuanlan.zhihu.com/p/612825647?utm_id0 https://blog.csdn.net/guzhangyu12345/article/details/108559810 SpringBoot多数据源事务解决方案 https://blog.csdn.net/u013407099/article/details/124526396多数据源切换下保证事务解决方案 https://blog.csdn.net/re…...
ElasticSearch 搜索推荐
Term Suggester "suggest_mode":"missing" missing 默认选项,不返回精准匹配到的分词结果 "suggest_mode":"popular" popular 大于等于搜索词频率的返回 "suggest_mode":"always", 不做任何限制&qu…...
Linux纯命令行查看文本文件
处理超大文本文件时,你可能希望避免一次性加载整个文件,这可能会耗尽内存资源。以下是一些在命令行中查看大文本文件的方法,它们适用于Linux和Unix系统,包括Mac OS X,而在Windows中,你可以使用类似的工具或…...
解决前端项目中Node.js版本不一致导致的依赖安装错误
解决前端项目中Node.js版本不一致导致的依赖安装错误 🌟 前言 欢迎来到我的小天地,这里是我记录技术点滴、分享学习心得的地方。📚 🛠️ 技能清单 编程语言:Java、C、C、Python、Go、前端技术:Jquery、Vue…...
IIoT 与 IoT 之间的区别
物联网世界充满了各式各样的首字母缩写词,从LPWAN到MQTT,再到广为人知的IoT。然而,这仅仅是冰山一角,物联网领域还有更多的变化等待我们去探索,其中就包括IIoT,即工业物联网。那么,你可能会问&a…...
spring boot3token拦截器链的设计与实现
⛰️个人主页: 蒾酒 🔥系列专栏:《spring boot实战》 🌊山高路远,行路漫漫,终有归途。 目录 写在前面 流程分析 需要清楚的 实现步骤 1.定义拦截器 2.创建拦截器链配置类 3.配置拦截器链顺序 4.配置拦截…...
LeetCode543题:二叉树的直径(python3)
代码思路: 先递归调用左儿子和右儿子求得它们为根的子树的深度 L和 R ,则该节点为根的子树的深度即为max(L,R)1。该节点的 dnode值为LR1 递归搜索每个节点并设一个全局变量 ans记录 dnode的最大值,最后返回 ans-1 即为树的直径。 # Definit…...
zabbix 7.0编译部署教程
zabbix 7.0编译部署教程 2024-03-08 16:50乐维社区 zabbix7.0 alpha版本、beta版本已经陆续发布,Zabbix7.0 LTS版本发布时间也越来越近。据了解,新的版本在性能提升、架构优化等新功能方面有非常亮眼的表现,不少小伙伴对此也已经跃跃欲试。心…...
Oracle Linux 8.9 安装 Python 3.11.8 和 Miniconda
Oracle Linux 8.9 安装 Python 3.11.8 和 Miniconda 1. 安装 Python 3.11.82. 安装 Miniconda 1. 安装 Python 3.11.8 Update system, sudo dnf update -yInstall Library, sudo dnf install curl gcc openssl-devel bzip2-devel libffi-devel zlib-devel wget make git -yI…...
Docker 配置阿里云镜像加速器
一、首先需要创建一个阿里云账号 二、登录阿里云账号 三、进入控制台 四、搜索容器镜像服务,并选择 五、选择镜像工具中的镜像加速 六 、配置镜像源 注意:有/etc/docker文件夹的直接从第二个命令开始...
[Linux][CentOs][Mysql]基于Linux-CentOs7.9系统安装并配置开机自启Mysql-8.0.28数据库
目录 一、准备工作:获取安装包和相应工具 (一)所需安装包 (二)安装包下载链接 (三)在服务器上创建文件夹并上传安装包 二、安装MySql (一)删除系统自带的mariadb …...
实用指南!2024年度计划怎么写?工作学习必备!
在新的一年开始之际,制定一份详细而实用的年度计划对于实现个人和职业目标至关重要。无论是在工作、学习还是生活中,一个明确的计划可以帮助你更好地组织时间、资源和精力,从而更有效地实现目标。下面是一个关于如何写年度计划的指南…...
js的事件有哪些?
鼠标事件: 鼠标事件 触发条件 onclick 鼠标点击左键触发 oncontextmenu 鼠标点击右键触发 ondblclick 鼠标双击触发 onmouseover 鼠标经过触发 onmouseout 鼠标离开触发 onmousemove 鼠标移动触发 onmouseup 鼠标弹起触发 onmousedown 鼠标按下触发 键盘事…...
Mock.js 基本语法与应用笔记
🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…...
vue从零到一创建项目?
创建一个Vue项目通常需要经过以下步骤,从零开始构建一个基本的Vue项目: 步骤一:安装Node.js和npm 下载安装Node.js: 在Node.js官网下载适合你操作系统的Node.js安装包,并按照提示进行安装。安装完Node.js后ÿ…...
安装PyTorch详细过程
安装anaconda 登录anaconda的官网下载,anaconda是一个集成的工具软件不需要我们再次下载。anaconda官网 跳转到这个页面如果你的Python版本正好是3.8版,那便可以直接根据系统去选择自己相应的下载版本就可以了。 但是如果你的Python版本号不是当前页面…...
使用Rust开发小型搜索引擎
一、概述 用Rust创建搜索引擎是探索该语言在性能和安全性方面具有优势的绝佳方式。 这个项目将索引和搜索概念转移到Rust的生态系统中,由于Rust独特的语法和范式,这是一个挑战,但也是有益的。 二、构建搜索引擎 步骤1,创建项目…...
2024.3.13
1.顺序表去重 代码: //顺序表去重 void dele(seq_p L) {if(LNULL){printf("入参为空,请检查\n");return;}for(int i0;i<L->len-1;i){for(int ji1;j<L->len;j){if(L->data[i]L->data[j]){dele_data(L,L->data[j]);j--;}…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
