[编程工具]Unity配表导出工具TableExporter1.1

news2024/11/25 17:32:50

[ 目录 ]

  • 0. 前言
  • 1. 属性拓展优化
    • (1)反射获取转化函数 TryParse
    • (2)反射获取EmptyReplace
    • (3)属性类型
    • (4)属性拓展
  • 2. 模板处理
    • (1)替换内容
    • (2)属性段
    • (3)模板特殊符号定义
    • (4)模板
  • 3. 面板优化
  • 4. 结束咯

0. 前言

之前就在做的一个 unity 中使用的 Excel 导出工具,继续完善了一下。
这次改了挺多内容的,一方面是使用了反射然后再优化了代码,属性拓展更加简单,另一方面是优化了模板处理和窗口显示。相关的代码以及Demo已经打包为Unity包,连接如下。上一个版本有比较详细的使用方法介绍,链接也放在下面可供参考

链接:https://pan.baidu.com/s/1AdsaUDOW4e4D-beWUaqhXA?pwd=wsad 
提取码:wsad
前一个版本的文章:https://blog.csdn.net/Blue_carrot_/article/details/130954127

1. 属性拓展优化

(1)反射获取转化函数 TryParse

~ TryParse
比如“int.TryParse”,是用于尝试将字符串转化为int类型,这对于数据导出工具来说还是很重要的,可以用于做数据的校验,另外在游戏运行时也可以作为将数据转化为对应类型。

~ 获取TryParse
作为一个unityEditor相关插件,与其他独立的导出工具不同的是,可以直接通过反射获取到程序内正在使用的类。这种情况下,我们可以将写在表格中的类型,用反射来获取到该类型的转化函数TryParse,通过这个函数来校验数据以及导出后的数据处理。

比如,“Color”类型,我们需要从有个方法来校验数据是否正确,另外导出代码时能够写入转化的方法。Unity中已经提供了ColorUtility.TryParseHtmlString来转16进制的颜色,比如“#FF00FF”。我们可以把每个属性对应的函数都列举到一个类中,方便我们去反射调用。如下:

using UnityEngine;
namespace Exporter
{
    public static partial class DataTable
    {
    	public delegate bool TryParseFunc<T>(string text, out T result);
    	
        public static TryParseFunc<int> TryParse_int = int.TryParse;
        public static TryParseFunc<float> TryParse_float = float.TryParse;
        public static TryParseFunc<double> TryParse_double = double.TryParse;
        public static TryParseFunc<bool> TryParse_bool = bool.TryParse;
        public static TryParseFunc<Color> TryParse_Color = ColorUtility.TryParseHtmlString;
    }
}

这里用到的是delegate委托,不用担心委托会有额外的开销,委托在新建的时候会有额外一点开销,但执行的时候几乎和原本的函数是一致的。比起反射到各个不同的类中的不同函数,使用委托不用考虑程序集的问题,这样显然会方便很多。那么反射获取函数可以这样处理.

string TryParseDelegateFieldName = "TryParse_" + typeName;
FieldInfo tryParseFuncFieldInfo = typeof(DataTable).GetField(TryParseDelegateFieldName);
Delegate tryParseFunc = tryParseFuncFieldInfo?.GetValue(null) as Delegate;

~ 使用TryParse
这个时候因为已经获取到了TryParse的委托,那么只要调用Invoke去使用就可以了

object[] tryParseParameters = new object[2] { "", tryParseOutParameter };
(bool)tryParseFunc.Method.Invoke(tryParseFunc.Target, tryParseParameters);

(2)反射获取EmptyReplace

EmptyReplace其实只是为了当这个表格没有配置时,我期望能够有个默认的值,比如说bool类型,希望没有填值的时候能为“False”。基本反射获取也和上边的一样。这里就只放一下定义

using UnityEngine;
namespace Exporter
{
    public static partial class EmptyReplace
    {
        public static string Default_int = "0";
        public static string Default_float = "0";
        public static string Default_double = "0";
        public static string Default_bool = "FALSE";
        public static string Default_Color = "#FFFFFF";
    }
}

(3)属性类型

那么对于一个属性而言PropertyInfo,可以抽象如下

 internal class PropertyInfo
 {
  		private string typeName
  	 	public bool Check(string str)
  	 	public void ReplaceParse()
 }
  • typeName:属性类型名,在表中可区分哪个类型
  • Check:类型的校验方式,用于检查数据是否正确
  • ReplaceParse:这个主要是为了生成代码做处理,告知运行是如何转化数据为是个属性

这个两个方法基本在获取到TryPrase的时候就可以解决了。就不多赘述了,完整的代码如下,可供参考。

using System;
using System.Reflection;
using System.Text;

namespace Exporter
{
    internal class PropertyInfo
    {
        private string typeName;
        private bool isStringProperty;
        private Delegate tryParseFunc;
        public string emptyReplace;
        private object tryParseOutParameter;
        private object[] tryParseParameters;
        private string TryParseDelegateFieldName => "TryParse_" + typeName;
        private string EmptyReplaceFieldName => "Default_" + typeName;
        public string EmptyReplace => emptyReplace;

        public bool Init(string typeName)
        {
            this.typeName = typeName;
            this.isStringProperty = typeName == "string";

            if (isStringProperty)
            {
                emptyReplace = "";
                return true;
            }
            else
            {
                FieldInfo tryParseFuncFieldInfo = typeof(DataTable).GetField(TryParseDelegateFieldName);
                tryParseFunc = tryParseFuncFieldInfo?.GetValue(null) as Delegate;

                FieldInfo emptyReplaceInfo = typeof(EmptyReplace).GetField(EmptyReplaceFieldName);
                object value = emptyReplaceInfo?.GetValue(null);
                emptyReplace = value != null ? value as string : "";

                tryParseParameters = new object[2] { "", tryParseOutParameter };

                return tryParseFunc != null;
            }
        }

        public bool Check(string str)
        {
            if (isStringProperty)
            {
                return true;
            }
            else
            {
                tryParseParameters[0] = str;
                return (bool)tryParseFunc.Method.Invoke(tryParseFunc.Target, tryParseParameters);
            }
        }

        public void ReplaceParse(StringBuilder template, bool isArray, bool isOutputKey, string propertyName)
        {
            // 获取值
            string getValueFromKey = "DataTable.GetStringFromKey";
            string value = Config.Inst.PropertyParseValue;
            if (isOutputKey)
            {
                value = getValueFromKey + "(" + value + ")";
            }

            // 获取函数
            string TryParseDelegateName = "DataTable." + TryParseDelegateFieldName;
            string parse = "";
            if (isStringProperty && isArray)
            {
                // 字符数组
                parse = string.Format("DataTable.SplitStrng({0})", value);
            }
            else if (isStringProperty && !isArray)
            {
                // 字符
                parse = value;
            }
            else if (!isStringProperty && isArray)
            {
                // 其他属性数组
                parse = "DataTable.ParseArr<{0}>({1}, {2})";
                parse = string.Format(parse, typeName, value, TryParseDelegateName);
            }
            else if (!isStringProperty && !isArray)
            {
                // 其他属性
                parse = "DataTable.Parse<{0}>({1}, {2})";
                parse = string.Format(parse, typeName, value, TryParseDelegateName);
            }
            template.Replace("{parse}", parse);
        }
    }
}

(4)属性拓展

那在此之后,我们去拓展一个属性,只需要创建一个对应的委托即可!

比如Vector2Int,我们只需要在DataTable中写入转化函数即可。(这里是需要前置一个V,比如“V1,1000”,主要是为了区别数字与字符串。如果没有前置字符,Excel在常规时会认为他是个数,比如"1,1000",会被识别为数字11000,为了防止到时候失误,所以加了个V)

public static TryParseFunc<Vector2Int> TryParse_Vector2Int = (string text, out Vector2Int result) =>
{
    if (!text.StartsWith("V"))
    {
        result = Vector2Int.zero;
        return false;
    }

    string[] temp = text.Substring(1, text.Length - 1).Split(",");
    int x, y;
    if (temp.Length == 2 &&
        int.TryParse(temp[0], out x) &&
        int.TryParse(temp[1], out y))
    {
        result = new Vector2Int(x, y);
        return true;
    }
    else
    {
        result = Vector2Int.zero;
        return false;
    }
};

2. 模板处理

代码模板独立成一个文本Template_code.txt,这样比起在UnIty中会更容易更改,另外重新更改了替换方式,尽量将能改的内容都写在样本中。

(1)替换内容

模板最常用的就是替换啦,这里我们可以设定“ {sheetName}“ 和“{fileName}”这两个字符,这样可以用于替换文件名和表格名。

(2)属性段

对于一个属性,要替换的内容有类型,名字之类的,但因为属性数量不同,所以单纯的替换就比较麻烦,这里加入属性段的概念,[properties][propertiesEnd]用于标记属性段,此时标记的部分就会按照属性来依次重复,然后对应的去替换属性的相关值

(3)模板特殊符号定义

那么,我们约定模板内的特殊符号以及其作用。

内容作用
{fileName}替换文件名
{sheetName}替换表格名
{parse}替换属性转化方式
{type}替换属性类型
{name}替换属性名字
{note}替换属性注释
{setting}替换属性设置
[properties]标记属性段的开始
[properties]标记属性段的结束

(4)模板

估计单讲没什么感觉,直接看模板估计就知道这么设定的作用了。

using System.Collections.Generic;
using UnityEngine;
using Exporter;

namespace GDT
{
    /// <summary>
    /// {sheetName} data,{fileName}.xlsx
    /// </summary>
    public class DR{sheetName}: IDataRow
    {
        
[properties]
        /// <summary>
        /// {note}  {setting}
        /// </summary>
        public {type} {name} { get; protected set; }

[propertiesEnd]
        
        public void ParseDataRow(string input)
        {
            string[] text = input.Split('\t');
            int index = 0;
[properties]
            {name} = {parse};
[propertiesEnd]
        }
        
        private void AvoidJIT()
        {
            new Dictionary<int, DR{sheetName}>();
        }
    }
}

3. 面板优化

主要是之前只弹出提示窗口显示有限,而且显示不完,所以特地额外弄了个窗口来显示这个内容。然后加了点富文本,方便显示。具体就不说怎么弄了,项目里面都有。关于Editor相关的内容以后再写文章记录一下吧。现在就简单放一下图吧。

在这里插入图片描述

4. 结束咯

这个处理的主要内容就讲完咯,下回有空再见吧。以后的可能会完善的内容有,关联额外检查、公式导出、Json导出等。不过等用到了再去弄吧,做这个东西还挺花时间的。然后,使用上有问题的话,可以再Q我.

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python如何放大界面——实用技巧

Python如何放大界面——实用技巧 在Python中&#xff0c;很多时候我们需要放大界面来更清楚的展示内容。这篇文章将介绍Python放大界面的方法。 放大界面的原理 在Python中&#xff0c;放大界面的原理实际上就是改变窗口的大小。我们可以通过改变窗口的尺寸实现放大效果。 …

LuatOS-Air AT应用指南--RNDIS

简介 RNDIS是指Remote NDIS&#xff0c;基于USB实现RNDIS实际上就是TCP/IP over USB&#xff0c;就是在USB设备上跑TCP/IP&#xff0c;让USB设备看上去像一块网卡。从而使Windows /Linux可以通过 USB 设备连接网络。 Window系统 window系统支持RNDIS直接用usb连接就可以使用&a…

2023/6/14总结

JS的学习&#xff1a; JavaScript是一种运行在客户端&#xff08;浏览器&#xff09;的编程语言&#xff0c;实现人机交互的效果 主要作用&#xff1a; 网页特效表单验证数据交互 JS的组成 ECMAScript 规定了js基础的语法核心知识 Web APIs DOM:操作文档&#xff0c;对页面…

简单的TCP网络程序·线程池(后端服务器)

目录 版本四&#xff1a;线程池 注意事项 文件&#xff1a;Task.hpp -- 任务单独为一个文件 组件&#xff1a;日志修改 新函数&#xff1a;vprintf() 可变参数的提取逻辑 vfprintf()的工作原理 初始化一个va_list 日志准备 获取时间小知识 日志初版 日志启动测试 …

DAY23:二叉树(十三)二叉树的最近公共祖先+二叉搜索树的最近公共祖先

文章目录 236.二叉树的最近公共祖先思路完整版后序遍历的进一步理解为什么左为空右不为空的时候return right这个逻辑是否包含p/q本身就是公共祖先的情况 235.二叉搜索树的最近公共祖先思路关于遍历顺序 递归法最开始的写法debug测试修改版 迭代法最开始的写法为什么最开始这种…

chatgpt赋能python:Python收费介绍

Python收费介绍 什么是Python? Python是一种高级的、解释性、面向对象、纯粹的动态语言&#xff0c;多用于快速应用程序开发、脚本编写、系统管理任务等。它有一个简单直观优美的语法&#xff0c;非常容易学习。 Python的收费形式 Python语言本身是免费的&#xff0c;任何…

chatgpt赋能python:Python如何操作Word文档

Python如何操作Word文档 简介 Python是一种高级编程语言&#xff0c;具有易于学习和使用、高效、可移植性强等优点。相信许多Python开发者都遇到过需要使用Python操作Word文档的情况。本文旨在介绍如何使用Python操作Word文档&#xff0c;使开发者能够方便地实现自己的需求。…

chatgpt赋能python:Python怎么改为中文?

Python怎么改为中文&#xff1f; Python是一种高级编程语言&#xff0c;具有易读性、简单性和可扩展性的特点。它广泛应用于Web开发、数据分析、人工智能等领域。如何将Python改为中文&#xff1f;下面将为您详细介绍。 为什么要将Python改为中文&#xff1f; Python的英文是由…

[读论文][谷歌-12s生成] Speed is all your need

论文简要总结 刚读了下speed is all you need这个论文, https://arxiv.org/pdf/2304.11267.pdf 只是用的SD1.4没有对网络进行改造。 只做了4个改动 1 是对norm采用了groupnorm (GPU shader加速) 2 采用了GELU (GPU shader加速) 3 采用了两种attention优化&#xff0c;是partiti…

C语言之指针详解(2)

目录 本章重点 1. 字符指针 2. 数组指针 3. 指针数组 4. 数组传参和指针传参 5. 函数指针 6. 函数指针数组 7. 指向函数指针数组的指针 8. 回调函数 9. 指针和数组面试题的解析 数组指针 数组指针的定义 数组指针是指针&#xff1f;还是数组&#xff1f; 答案是&…

数据结构-队列

数据结构之队列 队列的概念顺序队列循环队列 顺序循环队列的ADT定义1、简单结构体定义2、初始化3、队列的清空4、计算队列的长度5、判断队列是否为空6、插入新的元素7、元素的删除8、遍历输出队列内的所有元素 链队列的ADT定义1、链队列简单结构体定义2、初始化链队列3、判断链…

chatgpt赋能python:Python怎么断行-让代码更易读

Python怎么断行 - 让代码更易读 大多数Python程序员都知道&#xff0c;代码可读性非常重要。好的代码应该易于阅读和理解&#xff0c;而不是让人困惑和痛苦。 然而&#xff0c;我们经常会发现一些Python代码在一行中拥挤着多个表达式、长变量名混杂其中&#xff0c;让人感到相…

数组:为什么数组都从0开始编号?

提到数组&#xff0c;我想你肯定不陌生&#xff0c;甚至还会自信地说&#xff0c;它很简单啊。 是的&#xff0c;在每一种编程语言中&#xff0c;基本都会有数组这种数据类型。不过&#xff0c;它不仅仅是一种编程语言中的数据类型&#xff0c;还是一种最基础的数据结构。尽管…

word中使用通配符批量将参考文献设置为上角标

目录 一、word中的通配符匹配规则 二、匹配单个参考文献 三、匹配多个参考文献 四、操作方式 &#xff08;1&#xff09;打开word中的替换功能 &#xff08;2&#xff09;输入要查找的内容 &#xff08;3&#xff09;选择替换格式 &#xff08;4&#xff09;点击替换 一…

Linux调试工具GDB(1)

文章目录 前言一、GDB概念二、GDB具体使用方法三、GDB断点类型总结 前言 本篇文章我们来介绍一下Linux中的调试利器GDB工具&#xff0c;在Linux的调试中GDB可以帮助我们来解决非常多的问题。 一、GDB概念 GDB是一个功能强大的调试工具&#xff0c;可以用于分析程序崩溃&…

temporal简介

文章目录 前言一、temporal是什么&#xff1f;二、使用步骤1.执行以下命令以启动预构建映像以及所有依赖项。2.运行示例 总结 前言 这两天在国外的网站发现了一个新的golang的微服务框架&#xff0c;感觉挺不错&#xff0c;分亨出来&#xff0c;大家一起看看。 一、temporal是…

python:并发编程(四)

前言 本文将和大家一起探讨python的多进程并发编程&#xff0c;使用内置基本库multiprocessing来实现并发&#xff0c;先通过官方来简单使用这个模块。先打好基础&#xff0c;能够有个基本的用法与认知&#xff0c;后续文章&#xff0c;我们再进行详细使用。 本文为python并发…

【数据库原理与应用 - 第四章】关系数据库规范化理论

目录 一、关系模式规范化理论 1、关系模式规范化概念 2、关系模式应满足的基本要求 3、关系规范化的意义 &#xff08;1&#xff09;一个好的数据库应遵循的标准 &#xff08;2&#xff09;规范化的意义 二、函数依赖 1、函数依赖的概念 &#xff08;1&#xff09;平凡…

Vue--》Vue3打造可扩展的项目管理系统后台的完整指南(五)

今天开始使用 vue3 ts 搭建一个项目管理的后台&#xff0c;因为文章会将项目的每一个地方代码的书写都会讲解到&#xff0c;所以本项目会分成好几篇文章进行讲解&#xff0c;我会在最后一篇文章中会将项目代码开源到我的GithHub上&#xff0c;大家可以自行去进行下载运行&…

学生成绩管理系统(逻辑清楚-简单实用)

1、需求分析 1.1、需求分析概述 需求分析是我们在软件开发中的重要环节&#xff0c;是软件开发的第一步也是最基础的环节&#xff0c;这将决定我们所实现的目标以及系统的各个组成部分、各部分的任务职能、以及使用到的数据结构、各个部门之间的组成关系和数据流程&#xff0…