【Unity】Excel配置工具

1、功能介绍

通过Excel表配置表数据,一键生成对应Excel配置表的数据结构类、数据容器类、已经二进制数据文件,加载二进制数据文件获取所有表数据

需要使用Excel读取的dll包

2、关键代码

2.1 ExcelTool类

实现一键生成Excel配置表的数据结构类、数据容器类、已经二进制数据文件

using Excel;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;

public class ExcelTool
{
    /// <summary>
    /// excel文件存放路径
    /// </summary>
    public static string EXCEL_PATH = Application.dataPath + "/ArtRes/Excel/";

    /// <summary>
    /// 数据结构类脚本存储路径
    /// </summary>
    public static string DATA_CLASS_PATH = Application.dataPath + "/Scripts/ExcelData/DataClass/";

    /// <summary>
    /// 容器类脚本存储路径
    /// </summary>
    public static string DATA_CONTAINER_PATH = Application.dataPath + "/Scripts/ExcelData/Container/";

    /// <summary>
    /// 二进制数据存储路径
    /// </summary>
    //public static string DATA_BINARY_PATH = Application.streamingAssetsPath + "/Bianry/";

    /// <summary>
    /// 真正内容开始行号
    /// </summary>
    public static int BEGIN_INDEX = 4;

    [MenuItem("GameTool/GenerateExcelInfo")]
    private static void GenerateExcelInfo()
    {
        //加载指定路径中的所有Excel文件 用于生成对应的3个文件
        DirectoryInfo dInfo = Directory.CreateDirectory(EXCEL_PATH);
        //得到指定路径中的所有文件信息 相当于就是得到所有的Exce1表
        FileInfo[] files = dInfo.GetFiles();
        //数据容器
        DataTableCollection tableCollection;
        for (int i = 0; i < files.Length; i++)
        {
            //如果不是Excel文件就不要处理
            if (files[i].Extension != ".xlsx" && files[i].Extension != ".xls")
                continue;
            //打开一个excel文件得到其中所有表的数据
            using(FileStream fs = files[i].Open(FileMode.Open, FileAccess.Read))
            {
                IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
                tableCollection = excelReader.AsDataSet().Tables;
                fs.Close();
            }
            //遍历文件中的所有表的信息
            foreach (DataTable table in tableCollection)
            {
                Debug.Log(table.TableName);
                //生成数据结构类
                GenerateExcelDataClass(table);
                //生成容器类
                GenerateExcelContainer(table);
                //生成2进制数据
                GenerateExcelBinary(table);
            }
        }
    }

    /// <summary>
    /// 生成Excel表对应的数据结构类
    /// </summary>
    /// <param name="table"></param>
    private static void GenerateExcelDataClass(DataTable table)
    {
        //字段名行
        DataRow rowName = GetVariableNameRow(table);
        //字段类型行
        DataRow rowType = GetVariableTypeRow(table);

        //判断路径是否存在 没有的话 就创建文件夹
        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 += "\tpublic " + rowType[i].ToString() + " " + rowName[i].ToString() + ";\n";
        }

        str += "}\n";

        //把拼接好的字符串存进占地文件中
        File.WriteAllText(DATA_CLASS_PATH + table.TableName + ".cs", str);

        //刷新Project窗口
        AssetDatabase.Refresh();
    }

    /// <summary>
    /// 生成Excel表对应的数据容器类
    /// </summary>
    /// <param name="table"></param>
    private static void GenerateExcelContainer(DataTable table)
    {
        //得到主键索引
        int keyIndex = GetKeyIndex(table);
        //得到字段类型行
        DataRow rowType = GetVariableTypeRow(table);
        //没有路径就创建路径
        if (!Directory.Exists(DATA_CONTAINER_PATH)) 
            Directory.CreateDirectory (DATA_CONTAINER_PATH);

        string str = "using System.Collections.Generic;\n\n";

        str += "public class " + table.TableName + "Container" + "\n{\n";

        str += "\tpublic 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();

    }

    /// <summary>
    /// 生成Excel 2进制数据
    /// </summary>
    /// <param name="table"></param>
    private static void GenerateExcelBinary(DataTable table)
    {
        //没有路径创建路径
        if(!Directory.Exists(BinaryDataMgr.DATA_BINARY_PATH)) 
            Directory.CreateDirectory(BinaryDataMgr.DATA_BINARY_PATH);

        //创建一个2进制文件进行写入
        using(FileStream fs = new FileStream(BinaryDataMgr.DATA_BINARY_PATH + table.TableName + ".zhou", FileMode.OpenOrCreate, FileAccess.Write))
        {
            //存储具体的Excel对应的2进制信息
            //1.先要存储需要写的行数
            //-4 因为前面4行是配置规则 不是需要记录的数据内容
            fs.Write(BitConverter.GetBytes(table.Rows.Count - 4), 0, 4);
            //2.存储主键的变量名
            string keyName = GetVariableNameRow(table)[GetKeyIndex(table)].ToString();
            byte[] bytes = Encoding.UTF8.GetBytes(keyName);
            //存储字符串字节数据的长度
            fs.Write(BitConverter.GetBytes(keyName.Length), 0, 4);
            //存储字符串字节数组
            fs.Write(bytes, 0, bytes.Length);

            //遍历所有内容的行 进行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();

        }

        AssetDatabase.Refresh();
    }

    /// <summary>
    /// 获取变量名所在行
    /// </summary>
    /// <param name="table"></param>
    /// <returns></returns>
    private static DataRow GetVariableNameRow(DataTable table)
    {
        return table.Rows[0];
    }

    /// <summary>
    /// 获取变量类型所在行
    /// </summary>
    /// <param name="table"></param>
    /// <returns></returns>
    private static DataRow GetVariableTypeRow(DataTable table)
    {
        return table.Rows[1];
    }

    /// <summary>
    /// 获取主键索引
    /// </summary>
    /// <param name="table"></param>
    /// <returns></returns>
    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;
    }

}

2.2 BinaryDataMgr

保存数据,读取数据

读取Excel表生成的二进制文件,加载到内容中以供使用

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using UnityEngine;

/// <summary>
/// 2进制数据管理器
/// </summary>
public class BinaryDataMgr
{
    private static BinaryDataMgr instance = new BinaryDataMgr();
    public static BinaryDataMgr Instance => instance;
    private BinaryDataMgr() { }

    /// <summary>
    /// 用于存储所有Excel表数据的容器
    /// </summary>
    private Dictionary<string, object> tableDic = new Dictionary<string, object>();

    /// <summary>
    /// 数据存储路径
    /// </summary>
    private static string SAVE_PATH = Application.persistentDataPath + "/Data/";
    /// <summary>
    /// 二进制数据存储路径
    /// </summary>
    public static string DATA_BINARY_PATH = Application.streamingAssetsPath + "/Bianry/";

    public void InitData()
    {
        //加载自己的表到内存
        //LoadTable<TowerInfoContainer, TowerInfo>();
        //LoadTable<PlayerInfo1Container, PlayerInfo1>();
        //LoadTable<TestInfoContainer, TestInfo>();
    }

    /// <summary>
    /// 加载Excel表的2进制数据到内存中
    /// </summary>
    /// <typeparam name="T">容器类名</typeparam>
    /// <typeparam name="K">数据结构体类名</typeparam>
    public void LoadTable<T, K>()
    {
        //读取Excel表对应的2进制文件  来进行解析
        using (FileStream fs = File.Open(DATA_BINARY_PATH + typeof(K).Name + ".zhou", 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);
            //得到数据结构类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 dicObj = contaninerType.GetField("dataDic").GetValue(contaninerObj);

                //得到容器字段对象
                MethodInfo mInfo = dicObj.GetType().GetMethod("Add");
                //得到数据结构类对象中 指定主键字段的值
                object keyValue = classType.GetField(keyName).GetValue(dataObj);
                mInfo.Invoke(dicObj, new object[] { keyValue, dataObj });
            }

            //把读取完的表记录下来
            tableDic.Add(typeof(T).Name, contaninerObj);
            fs.Close();

        }
    }

    /// <summary>
    /// 得到一张表的信息
    /// </summary>
    /// <typeparam name="T">容器类名</typeparam>
    /// <returns></returns>
    public T GetTable<T>() where T : class
    {
        string tableName = typeof(T).Name;
        if(tableDic.ContainsKey(tableName))
        {
            return tableDic[tableName] as T;
        }
        return null;
    }

    /// <summary>
    /// 存储类对象数据
    /// </summary>
    /// <param name="data"></param>
    /// <param name="fileName"></param>
    public void Save(object data, string fileName)
    {
        if(!Directory.Exists(SAVE_PATH))
        {
            Directory.CreateDirectory(SAVE_PATH);
        }

        using(FileStream fs = new FileStream(SAVE_PATH + fileName + ".zhou", FileMode.OpenOrCreate, FileAccess.Write))
        {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, data);

            fs.Flush();
            fs.Close();
        }
    }

    /// <summary>
    /// 读取2进制数据转换成对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="fileName"></param>
    /// <returns></returns>
    public T Load<T>(string fileName) where T : class
    {
        if(!File.Exists(SAVE_PATH + fileName + ".zhou"))
        {
            return default(T);
        }

        T data;
        using(FileStream fs = new FileStream(SAVE_PATH + fileName + ".zhou", FileMode.Open, FileAccess.Read))
        {
            BinaryFormatter bf = new BinaryFormatter();
            data = bf.Deserialize(fs) as T;
            fs.Close();
        }
        return data;
    }

}

3、使用步骤

3.1 根据规则创建Excel配置表

创建的Excel表需要放置到ArtRes/Excel路径下

Excel配置规则

  •     第一行:字段名
  •     第二行:字段类型(字段类型一定不要配置错误,字段类型目前只支持int float bool string)如果想要在添加类型,需要ExcelTool的GenerateExcelBianary和BianryDataMgr的LoadTable方法当中对应添加读写的逻辑
  •     第三行:主键是哪一个字段 需要通过key来标识主键
  •     第四行:描述信息(只是给别人看,不会有别的作用)
  •     第五行~第n行:就是具体数据信息
  •     下方的表名决定类数据结构类,容器类,2进制文件的文件名

3.2 点击按钮GenerateExcelInfo

生成所有Excel表的数据结构类、数据结构容器类和二进制数据文件

 

3.3 完善BinaryDataMgr的InitData()方法,将自己创建的表进行加载

    public void InitData()
    {
        //加载自己的表到内存
        //LoadTable<TowerInfoContainer, TowerInfo>();
        //LoadTable<PlayerInfo1Container, PlayerInfo1>();
        //LoadTable<TestInfoContainer, TestInfo>();
        LoadTable<WeaponItemContainer, WeaponItem>();
    }

3.4 初始化数据,加载数据到内存

BinaryDataMgr.Instance.InitData()        加载二进制数据到内存当中

BinaryDataMgr.Instance.GetTable<TowerInfoContainer>()        获取需要使用的表的数据

        BinaryDataMgr.Instance.InitData();
        WeaponItemContainer data = BinaryDataMgr.Instance.GetTable<WeaponItemContainer>();

        foreach (var item in data.dataDic.Values)
        {
            Debug.Log($"id:{item.id}-name:{item.name}-des:{item.des}-money:{item.money}-ATK:{item.atk}");
        }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/749617.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Centos7源码方式安装sqle及开发相关

官方文档-源码安装 操作系统&#xff1a;centos:7.9,everything (DVD版应该也可以) (在ubuntu22.04装了两天之后乖乖开了一个新Centos7虚拟机) 镜像&#xff1a;清华大学开源软件镜像站 centos/7.9.2009 安装git sudo yum update -y sudo yum install -y git git --version安…

Sonia索尼娅:填补心理健康护理缺口的创新人工智能治疗师应用APP

聊天机器人可以取代人类治疗师吗&#xff1f;一些初创公司和患者声称他们可以。但这并不是完全确定的科学。 一项引人注目的研究发现&#xff0c;高达80%的使用OpenAI的ChatGPT寻求心理健康建议的人认为&#xff0c;这项技术可作为传统治疗的理想替代方案。与此同时&#xff0…

Android高级面试_2_IPC相关

Android 高级面试-3&#xff1a;语言相关 1、Java 相关 1.1 缓存相关 问题&#xff1a;LruCache 的原理&#xff1f; 问题&#xff1a;DiskLruCache 的原理&#xff1f; LruCache 用来实现基于内存的缓存&#xff0c;LRU 就是最近最少使用的意思&#xff0c;LruCache 基于L…

国外8年联培访学迎来逆袭|国家最高科学技术奖薛其坤成长史

国家最高科技奖花落薛其坤&#xff0c;他是该奖项史上最年轻得主。在追踪其成长史的过程中&#xff0c;知识人网小编注意到&#xff1a;薛其坤的学习研究开局并不顺利&#xff0c;直至到日本做联合培养博士研究生&#xff0c;他才真正迎来了自己学术生涯的重要转折点。后来到美…

面试相关-接口测试常问的问题

1.为什么要做接口测试 (1)现在大多系统都是前后端分离的项目,前端和后端的进度可能不一样,那为了尽早的进入测试,前端界面没有开发完成的情况下,只要后端的接口开发完了,就可以提前做接口测试了; (2)基于安全考虑,只依赖前端进行限制,已经完全不满足系统的安全性…

ELK日志集成

https://www.bilibili.com/video/BV1x94y1674x/?buvidXY705117E90F73A790429C9CFBD5F70F22168&vd_source939ea718db29535a3847d861e5fe37ef

Aigtek:为何要使用电压放大器

电压放大器在现代电子技术中起到了至关重要的作用。它是一种电子设备&#xff0c;用于将输入信号的电压增大到所需的输出电压水平。电压放大器的使用有以下几个方面的原因和优势。 电压放大器可以提高信号的强度和质量。许多实际应用中的输入信号往往很微弱&#xff0c;比如来自…

“管式加热炉简单控制系统和串级控制系统设计与Matlab仿真”,高分资源,匠心制作,下载可用。强烈推荐!!!

“管式加热炉简单控制系统和串级控制系统设计与Matlab仿真”毕业设计&#xff0c;高分资源&#xff0c;匠心制作&#xff0c;下载可用。强烈推荐&#xff01;&#xff01;&#xff01; 1.控制目标 加热炉的任务是把原油加热到一定温度&#xff0c;以保证下道工艺的顺利进行。…

windows安装mysql8.0.35保姆级教程

一、下载mysql安装包 点击mysql安装包下载链接&#xff1a;https://downloads.mysql.com/archives/community/ 选择window版本&#xff0c;点击下载按钮&#xff0c;如下所示&#xff1a; 二、解压安装包并新建my.ini文件 将下面内容复制到新建的my.ini文件里面 [mysqld] #…

阿里云oss存储

文章目录 准备阿里云的OSS控制台创建bucket获取AccessKey java使用oss导入依赖官网demo修改参数运行demo代码 封装工具类Oss下载如何保证指定时间段内可以访问私有权限的图片文件&#xff1f; 准备阿里云的OSS 控制台 访问阿里云官网&#xff0c;登录以后&#xff0c;右上角有…

大众点评根据关键词搜索采集店铺信息

大众点评根据关键词搜索采集店铺信息&#xff0c;包括店铺名称、大中小分类、省市区划分、人均价格、评价数量、团购数量、全部团购名称、全部团购链接&#xff08;团购信息还可解析出每个团购的价格&#xff09; ​​​

【代码安全】如何通过实现代码加密与魔改Python,防止代码泄露、恶意窃取

如何通过实现代码加密与魔改Python&#xff0c;防止代码泄露、恶意窃取 文章目录 如何通过实现代码加密与魔改Python&#xff0c;防止代码泄露、恶意窃取前言概述代码运行演示Step 0: 正常代码运行Step 1: 代码加密Step 2: 加密代码在魔改环境运行Step 3: 加密代码在正常环境运…

matlab编辑稀疏单位方阵

创建 10001000 稀疏单位方阵&#xff0c;并查看稀疏模式。 &#xff08;1&#xff09; I speye(1000); spy(I)&#xff08;2&#xff09; S speye(400,800); spy(S)此命令等同于 speye([400 800])。

【Python】易错题 [1]

目录 一、选择&#xff1a; 1.列表的复制​编辑 2.函数 二、填空 一、选择&#xff1a; 1.列表的复制 在Python中&#xff0c;列表是可变的数据类型。当将一个列表赋值给另一个变量时&#xff0c;实际上是将这个变量的引用指向原始列表。&#xff08;指针&#xff09;因此&…

直播怎么录制视频?直播视频,3种录制方法

“今晚我最喜欢的游戏博主要进行直播&#xff0c;但我可能还要加班。怎么办&#xff0c;不想错过直播的内容&#xff01;电脑怎么才能进行直播录制视频啊&#xff1f;谁能教教我&#xff1f;” 在数字化的今天&#xff0c;直播已经成为人们获取信息和娱乐的重要途径。有时&…

Adobe Acrobat编辑器最新版下载安装 Adobe Acrobat版本齐全!

功能强大&#xff0c;Adobe Acrobat无疑是PDF文档处理领域的翘楚。这款软件集多种PDF文档处理功能于一身&#xff0c;不仅使得用户可以轻松地编辑PDF文档&#xff0c;更能轻松应对转换和合并等多种需求。 在编辑功能上&#xff0c;Adobe Acrobat的表现尤为出色。无论是添加文字…

IDEA 插件推荐【一】

好使的插件可以让工作事倍功半。下面就推荐一些常用的IDEA插件&#xff0c;如果你有其他好使的插件&#xff0c;欢迎评论区留言分享出来~ 1.Key Promoter X Key Promoter X 插件&#xff0c;IDEA 快捷键提示工具。 在每次我们使用鼠标进行 IDEA 的某个操作&#xff0c;Key Pr…

​​植物大战僵尸杂交版直装版v2.1 安卓版:全新策略塔防体验

《植物大战僵尸杂交版直装版》v2.1是由B站UP主“潜艇伟伟迷”精心制作的同人游戏&#xff0c;为策略塔防手游带来了全新的活力。游戏中引入了众多创新的杂交植物&#xff0c;例如结合了向日葵的阳光生成能力和豌豆射手的攻击特性的向日葵豌豆射手&#xff0c;以及拥有寒冰豌豆射…

CesiumJS【Basic】- #019 加载glb/gltf文件(Entity方式)

文章目录 加载glb/gltf文件(Entity方式)1 目标2 代码实现3 资源文件加载glb/gltf文件(Entity方式) 1 目标 使用Entity方式加载glb/gltf文件 2 代码实现 import * as Cesium from "cesium";const viewer = new Cesium.Viewer

武汉高校大学智能制造实验室数字孪生可视化系统平台建设项目验收

随着科技的不断进步和工业4.0的深入推进&#xff0c;智能制造已成为现代工业发展的重要方向。在这一背景下&#xff0c;武汉高校大学智能制造实验室积极响应国家号召&#xff0c;致力于将先进的数字孪生技术与智能制造教育相结合&#xff0c;打造了一个集教学、实训、科研于一体…