Unity 修改Excel表的内容

继之前的 Unity 读取Excel表的内容 这篇我们讲讲怎么在Unity中修改Excel文件。

常见的应用场景:比如策划有一些表,例如技能表,里面的一些数值策划也不清楚到底应该配置多少合适,那么如果我们只能读取表的话,策划就需要一直重复,打开Excel文件,修改一个参数,导入Excel内容,运行测试,直到配置到了理想的数据。这明显就是个很繁琐的过程。所以我们可以在Unity中添加一个小工具,让策划可以只在Unity中操作,点击一个保存按钮,即可运行测试(甚至可以在运行中修改某个表中字段的值)调试到满意的结果在点个保存按钮,同步修改Excel文件的内容,那不就美滋滋了。

准备:除了上一篇提到的dll文件,要修改excel文件还需一个EPPlus.dll文件。链接: https://pan.baidu.com/s/1NhY_VxmO233cp3N09VErNw 提取码: qc5k

注意:要修改的excel文件必须是.xlsx后缀的,进行写入操作的时候,下标都是从1开始。代码中进行了注释。

首先,我们把之前的ItemManager类做成一个单例类,这样做的好处是可以在程序运行的时候同步修改数据。

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

public class ItemManager : ScriptableObject
{
    static ItemManager mInstance;
    public static ItemManager Instance
    {
        get
        {
            if (mInstance == null)
            {
                mInstance = Resources.Load<ItemManager>("DataAssets/Item");
            }
            return mInstance;
        }
    }
    public Item[] dataArray;
}

接着就是在Editor目录下添加一个ExcelEditorWindow.cs文件,创建我们的Excel编辑界面,具体内容见代码,添加了注释。具体的逻辑就是显示Asset中的数据(因此要先将Excel文件导出成Asset),然后选择你要修改的某行数据,修改后保存即可。

using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;

public class ExcelEditorWindow : EditorWindow
{
    List<bool> toggleValues = new List<bool>();//单选框信息

    FieldInfo[] itemFieldInfoArray;//表数据对应类的反射(注意:FieldInfo和PropertyInfo)
    int selectedIndex = -1;//当前选择的某行数据下标
    List<string> temperDataValues = new List<string>();//选择的某行数据,可修改

    Vector2 ScrollViewContentOffset = Vector2.zero;//纪录ScrollView滚动的位置

    [MenuItem("CustomEditor/ItemExcelEditor")]
    public static void GetWindow()
    {
        Rect rect = new Rect(0, 0, 800, 500);
        var window = EditorWindow.GetWindowWithRect(typeof(ExcelEditorWindow), rect, true, "物品编辑");
        window.Show();
    }

    void OnEnable()
    {
        LoadExcelData();
    }

    void OnGUI()
    {
        DrawTableDatas();

        GUILayout.Space(30);

        DrawEditArea();
    }

    //显示当前xlsx导出的asset文件的内容,并添加复选框用于选择需要修改的某行数据
    void DrawTableDatas()
    {
        EditorGUILayout.BeginHorizontal();
        ScrollViewContentOffset = EditorGUILayout.BeginScrollView(ScrollViewContentOffset, GUILayout.Width(750), GUILayout.Height(250));

        EditorGUILayout.BeginHorizontal();
        GUILayout.Space(100);
        for (int i = 0; i < itemFieldInfoArray.Length; i++)
        {
            EditorGUILayout.LabelField(itemFieldInfoArray[i].Name, GUILayout.Width(100));
        }
        EditorGUILayout.EndHorizontal();

        for (int i = 0; i < ItemManager.Instance.dataArray.Length; i++)
        {
            EditorGUILayout.BeginHorizontal();
            toggleValues.Add(false);
            if (toggleValues[i] = EditorGUILayout.Toggle(toggleValues[i], GUILayout.Width(100)))
            {
                if (selectedIndex != i)
                {
                    ChangeSelect(i);
                }
            }
            for (int j = 0; j < itemFieldInfoArray.Length; j++)
            {
                EditorGUILayout.LabelField(itemFieldInfoArray[j].GetValue(ItemManager.Instance.dataArray[i])?.ToString(), GUILayout.Width(100));
            }
            EditorGUILayout.EndHorizontal();
        }
        EditorGUILayout.EndScrollView();
        EditorGUILayout.EndHorizontal();
    }

    void DrawEditArea()
    {
        if (selectedIndex != -1)
        {
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label(new GUIContent("编辑数据:"));
            EditorGUILayout.EndHorizontal();
            GUILayout.Space(10);

            //显示title
            EditorGUILayout.BeginHorizontal();
            for (int i = 0; i < itemFieldInfoArray.Length; i++)
            {
                EditorGUILayout.LabelField(itemFieldInfoArray[i].Name, GUILayout.Width(100));
            }
            EditorGUILayout.EndHorizontal();
            GUILayout.Space(10);

            //显示数据
            EditorGUILayout.BeginHorizontal();
            for (int i = 0; i < itemFieldInfoArray.Length; i++)
            {
                temperDataValues[i] = EditorGUILayout.TextField(temperDataValues[i], GUILayout.Width(100));
            }
            EditorGUILayout.EndHorizontal();
            GUILayout.Space(10);

            //编辑按钮
            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("保存数据到Excel"))
            {
                WriteToExcel();
            }
            if (GUILayout.Button("保存测试数据"))
            {
                SaveToAsset();
            }
            EditorGUILayout.EndHorizontal();
        }
    }

    //读取Asset数据
    void LoadExcelData()
    {
        itemFieldInfoArray = typeof(Item).GetFields();
    }

    //选择某行数据
    void ChangeSelect(int index)
    {
        if (selectedIndex != index)
        {
            if (selectedIndex != -1)
            {
                toggleValues[selectedIndex] = false;
            }
            toggleValues[index] = true;
        }
        selectedIndex = index;
        GetSelectDataStringValues();
    }

    //获取选择行的数据的string value
    void GetSelectDataStringValues()
    {
        temperDataValues.Clear();
        for (int i = 0; i < itemFieldInfoArray.Length; i++)
        {
            var value = itemFieldInfoArray[i].GetValue(ItemManager.Instance.dataArray[selectedIndex]);
            if (value != null)
            {
                temperDataValues.Add(value.ToString());
            }
        }
    }

    //写入xlsx文件,注意excel文件的后缀必须为.xlsx,若为.xls则无法读取到Workbook.Worksheets
    public void WriteToExcel()
    {
        SaveToAsset();

        string path = ExcelConfig.excelsFolderPath + "Item.xlsx";
        FileInfo xlsxFile = new FileInfo(path);

        if (xlsxFile.Exists)
        {
            //通过ExcelPackage打开文件
            using (ExcelPackage package = new ExcelPackage(xlsxFile))
            {
                //修改excel的第一个sheet,下标从1开始
                ExcelWorksheet worksheet = package.Workbook.Worksheets[1];

                for (int i = 0; i < temperDataValues.Count; i++)
                {
                    //修复selectedIndex + 2行,i + 1列数据,下标从1开始,第一行因为是描述信息所以是selectedIndex + 2
                    worksheet.Cells[selectedIndex + 2, i + 1].Value = temperDataValues[i];
                }

                package.Save();
                Debug.Log("WriteToExcel Success");
            }
        }
    }

    //修改的数据保存到Asset文件中
    void SaveToAsset()
    {
        ItemManager.Instance.dataArray[selectedIndex] = GetNewItem();
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log("SaveToAsset Success");
    }

    Item GetNewItem()
    {
        Item item = new Item();
        for (int i = 0; i < itemFieldInfoArray.Length; i++)
        {
            var v = ConvertObject(temperDataValues[i], itemFieldInfoArray[i].FieldType);
            itemFieldInfoArray[i].SetValue(item, v);
        }
        return item;
    }

    //类型转换
    object ConvertObject(object obj, Type type)
    {
        if (type == null)
        {
            return obj;
        }
        if (obj == null)
        {
            return type.IsValueType ? Activator.CreateInstance(type) : null;
        }

        Type underlyingType = Nullable.GetUnderlyingType(type);
        if (type.IsAssignableFrom(obj.GetType())) 
        {
            // 如果待转换对象的类型与目标类型兼容,则无需转换
            return obj;
        }
        else if ((underlyingType ?? type).IsEnum) 
        {
            // 如果待转换的对象的基类型为枚举
            if (underlyingType != null && string.IsNullOrEmpty(obj.ToString())) 
            {
                // 如果目标类型为可空枚举,并且待转换对象为null 则直接返回null值
                return null;
            }
            else
            {
                return Enum.Parse(underlyingType ?? type, obj.ToString());
            }
        }
        else if (typeof(IConvertible).IsAssignableFrom(underlyingType ?? type))
        {
            // 如果目标类型的基类型实现了IConvertible,则直接转换
            try
            {
                return Convert.ChangeType(obj, underlyingType ?? type, null);
            }
            catch
            {
                return underlyingType == null ? Activator.CreateInstance(type) : null;
            }
        }
        else if (type.IsAssignableFrom(typeof(Vector3)))
        {
            //按照自己自定义的格式解析,例如:
            //string v = obj.ToString();
            //v = v.Replace("(", "").Replace(")", "");
            //var strValues = v.Split(',');
            //return new Vector3(Convert.ToSingle(strValues[0]), Convert.ToSingle(strValues[1]), Convert.ToSingle(strValues[2]));
        }
        else
        {
            TypeConverter converter = TypeDescriptor.GetConverter(type);
            if (converter.CanConvertFrom(obj.GetType()))
            {
                return converter.ConvertFrom(obj);
            }
            ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
            if (constructor != null)
            {
                object o = constructor.Invoke(null);
                PropertyInfo[] propertys = type.GetProperties();
                Type oldType = obj.GetType();
                foreach (PropertyInfo property in propertys)
                {
                    PropertyInfo p = oldType.GetProperty(property.Name);
                    if (property.CanWrite && p != null && p.CanRead)
                    {
                        property.SetValue(o, ConvertObject(p.GetValue(obj, null), property.PropertyType), null);
                    }
                }
                return o;
            }
        }
        return obj;
    }
}

点击CustomEditor->ItemExcelEditor后,界面如下。

发布了71 篇原创文章 · 获赞 160 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/wangjiangrong/article/details/100743781
今日推荐