解析流程: 1. 配置Excel 2. 读取解析Excel 3. 整理解析到的数据 4. 自动创建对应的C#类,继承ScriptableObject,声明变量,保存.cs文件 5. 自动创建ScriptableObject的Asset文件,并赋值,保存Asset文件 6. 在游戏直接使用ScriptableObject类 优点: 1. 每个Excel对应一个类,使用灵活,对Excel限制少 2. 自动创建C#类,不需要对每个Excel手动写代码,Excel修改后重新生成类即可 3. 自动创建ScriptableObject的Asset文件,自动对类字段赋值 4. 数据值直接写到ScriptableObject里,方便查看,可以手动修改调整,不需要每次改动都在Excel里操作 5. 在游戏内直接调用ScriptableObject类,不需要额外操作 具体操作: 1. 配置Excel 2. 读取Excel 3. 自动生成C#类和Asset文件 4. 编写操作面板窗口 5. 操作成功,游戏内使用数据 表格数据类基类 自动创建的C#类: 自动创建的ScriptableObject对应Asset文件 测试用管理类 测试游戏内使用
#if UNITY_EDITOR using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; using Excel; using System.Reflection; using System; //Excel中间数据 public class ExcelMediumData { //Excel名字 public string excelName; //Dictionary<字段名称, 字段类型>,记录类的所有字段及其类型 public Dictionary<string, string> propertyNameTypeDic; //List<一行数据>,List<Dictionary<字段名称, 一行的每个单元格字段值>> //记录类的所有字段值,按行记录 public List<Dictionary<string, string>> allItemValueRowList; } public static class ExcelDataReader { //Excel第2行对应字段名称 const int excelNameRow = 2; //Excel第4行对应字段类型 const int excelTypeRow = 4; //Excel第5行及以后对应字段值 const int excelDataRow = 5; //Excel读取路径 //public static string excelFilePath = Application.dataPath + "/Excel"; public static string excelFilePath = Application.dataPath.Replace("Assets", "Excel"); //自动生成C#类文件路径 static string excelCodePath = Application.dataPath + "/Script/Excel/AutoCreateCSCode"; //自动生成Asset文件路径 static string excelAssetPath = "Assets/Resources/ExcelAsset"; #region --- Read Excel --- //创建Excel对应的C#类 public static void ReadAllExcelToCode() { //读取所有Excel文件 //指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。 string[] excelFileFullPaths = Directory.GetFiles(excelFilePath, "*.xlsx"); if (excelFileFullPaths == null || excelFileFullPaths.Length == 0) { Debug.Log("Excel file count == 0"); return; } //遍历所有Excel,创建C#类 for (int i = 0; i < excelFileFullPaths.Length; i++) { ReadOneExcelToCode(excelFileFullPaths[i]); } } //创建Excel对应的C#类 public static void ReadOneExcelToCode(string excelFileFullPath) { //解析Excel获取中间数据 ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFileFullPath); if (excelMediumData != null) { //根据数据生成C#脚本 string classCodeStr = ExcelCodeCreater.CreateCodeStrByExcelData(excelMediumData); if (!string.IsNullOrEmpty(classCodeStr)) { //写文件,生成CSharp.cs if (WriteCodeStrToSave(excelCodePath, excelMediumData.excelName + "ExcelData", classCodeStr)) { Debug.Log("<color=green>Auto Create Excel Scripts Success : </color>" + excelMediumData.excelName); return; } } } //生成失败 Debug.LogError("Auto Create Excel Scripts Fail : " + (excelMediumData == null ? "" : excelMediumData.excelName)); } #endregion #region --- Create Asset --- //创建Excel对应的Asset数据文件 public static void CreateAllExcelAsset() { //读取所有Excel文件 //指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。 string[] excelFileFullPaths = Directory.GetFiles(excelFilePath, "*.xlsx"); if (excelFileFullPaths == null || excelFileFullPaths.Length == 0) { Debug.Log("Excel file count == 0"); return; } //遍历所有Excel,创建Asset for (int i = 0; i < excelFileFullPaths.Length; i++) { CreateOneExcelAsset(excelFileFullPaths[i]); } } //创建Excel对应的Asset数据文件 public static void CreateOneExcelAsset(string excelFileFullPath) { //解析Excel获取中间数据 ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFileFullPath); if (excelMediumData != null) { ////获取当前程序集 //Assembly assembly = Assembly.GetExecutingAssembly(); ////创建类的实例,返回为 object 类型,需要强制类型转换,assembly.CreateInstance("类的完全限定名(即包括命名空间)"); //object class0bj = assembly.CreateInstance(excelMediumData.excelName + "Assignment",true); //必须遍历所有程序集来获得类型。当前在Assembly-CSharp-Editor中,目标类型在Assembly-CSharp中,不同程序将无法获取类型 Type type = null; foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { //查找目标类型 Type tempType = asm.GetType(excelMediumData.excelName + "AssetAssignment"); if (tempType != null) { type = tempType; break; } } if (type != null) { //反射获取方法 MethodInfo methodInfo = type.GetMethod("CreateAsset"); if (methodInfo != null) { methodInfo.Invoke(null, new object[] { excelMediumData.allItemValueRowList, excelAssetPath }); //创建Asset文件成功 Debug.Log("<color=green>Auto Create Excel Asset Success : </color>" + excelMediumData.excelName); return; } } } //创建Asset文件失败 Debug.LogError("Auto Create Excel Asset Fail : " + (excelMediumData == null ? "" : excelMediumData.excelName)); } #endregion #region --- private --- //解析Excel,创建中间数据 private static ExcelMediumData CreateClassCodeByExcelPath(string excelFileFullPath) { if (string.IsNullOrEmpty(excelFileFullPath)) return null; excelFileFullPath = excelFileFullPath.Replace("\", "/"); FileStream stream = File.Open(excelFileFullPath, FileMode.Open, FileAccess.Read); if (stream == null) return null; //解析Excel IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream); //无效Excel if (excelReader == null || !excelReader.IsValid) { Debug.Log("Invalid excel : " + excelFileFullPath); return null; } //<数据名称,数据类型> KeyValuePair<string, string>[] propertyNameTypes = null; //List<KeyValuePair<数据名称, 单元格数据值>[]>,所有数据值,按行记录 List<Dictionary<string, string>> allItemValueRowList = new List<Dictionary<string, string>>(); //每行数据数量 int propertyCount = 0; //当前遍历行,从1开始 int curRowIndex = 1; //开始读取,按行遍历 while (excelReader.Read()) { if (excelReader.FieldCount == 0) continue; //读取一行的数据 string[] datas = new string[excelReader.FieldCount]; for (int j = 0; j < excelReader.FieldCount; ++j) { //赋值一行的每一个单元格数据 datas[j] = excelReader.GetString(j); } //空行/行第一个单元格为空,视为无效数据 if (datas.Length == 0 || string.IsNullOrEmpty(datas[0])) { curRowIndex++; continue; } //数据行 if (curRowIndex >= excelDataRow) { //数据无效 if (propertyCount <= 0) return null; Dictionary<string, string> itemDic = new Dictionary<string, string>(propertyCount); //遍历一行里的每个单元格数据 for (int j = 0; j < propertyCount; j++) { //判断长度 if (j < datas.Length) itemDic[propertyNameTypes[j].Key] = datas[j]; else itemDic[propertyNameTypes[j].Key] = null; } allItemValueRowList.Add(itemDic); } //数据名称行 else if (curRowIndex == excelNameRow) { //以数据名称确定每行的数据数量 propertyCount = datas.Length; if (propertyCount <= 0) return null; //记录数据名称 propertyNameTypes = new KeyValuePair<string, string>[propertyCount]; for (int i = 0; i < propertyCount; i++) { propertyNameTypes[i] = new KeyValuePair<string, string>(datas[i], null); } } //数据类型行 else if (curRowIndex == excelTypeRow) { //数据类型数量少于指定数量,数据无效 if (propertyCount <= 0 || datas.Length < propertyCount) return null; //记录数据名称及类型 for (int i = 0; i < propertyCount; i++) { propertyNameTypes[i] = new KeyValuePair<string, string>(propertyNameTypes[i].Key, datas[i]); } } curRowIndex++; } if (propertyNameTypes.Length == 0 || allItemValueRowList.Count == 0) return null; ExcelMediumData excelMediumData = new ExcelMediumData(); //类名 excelMediumData.excelName = excelReader.Name; //Dictionary<数据名称,数据类型> excelMediumData.propertyNameTypeDic = new Dictionary<string, string>(); //转换存储格式 for (int i = 0; i < propertyCount; i++) { //数据名重复,数据无效 if (excelMediumData.propertyNameTypeDic.ContainsKey(propertyNameTypes[i].Key)) return null; excelMediumData.propertyNameTypeDic.Add(propertyNameTypes[i].Key, propertyNameTypes[i].Value); } excelMediumData.allItemValueRowList = allItemValueRowList; return excelMediumData; } //写文件 private static bool WriteCodeStrToSave(string writeFilePath, string codeFileName, string classCodeStr) { if (string.IsNullOrEmpty(codeFileName) || string.IsNullOrEmpty(classCodeStr)) return false; //检查导出路径 if (!Directory.Exists(writeFilePath)) Directory.CreateDirectory(writeFilePath); //写文件,生成CS类文件 StreamWriter sw = new StreamWriter(writeFilePath + "/" + codeFileName + ".cs"); sw.WriteLine(classCodeStr); sw.Close(); // UnityEditor.AssetDatabase.Refresh(); return true; } #endregion } #endif
#if UNITY_EDITOR using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Text; public class ExcelCodeCreater { #region --- Create Code --- //创建代码,生成数据C#类 public static string CreateCodeStrByExcelData(ExcelMediumData excelMediumData) { if (excelMediumData == null) return null; //Excel名字 string excelName = excelMediumData.excelName; if (string.IsNullOrEmpty(excelName)) return null; //Dictionary<字段名称, 字段类型> Dictionary<string, string> propertyNameTypeDic = excelMediumData.propertyNameTypeDic; if (propertyNameTypeDic == null || propertyNameTypeDic.Count == 0) return null; //List<一行数据>,List<Dictionary<字段名称, 一行的每个单元格字段值>> List<Dictionary<string, string>> allItemValueRowList = excelMediumData.allItemValueRowList; if (allItemValueRowList == null || allItemValueRowList.Count == 0) return null; //行数据类名 string itemClassName = excelName + "ExcelItem"; //整体数据类名 string dataClassName = excelName + "ExcelData"; //生成类 StringBuilder classSource = new StringBuilder(); classSource.Append("/*Auto Create, Don't Edit !!!*/n"); classSource.Append("n"); //添加引用 classSource.Append("using UnityEngine;n"); classSource.Append("using System.Collections.Generic;n"); classSource.Append("using System;n"); classSource.Append("using System.IO;n"); classSource.Append("n"); //生成行数据类,记录每行数据 classSource.Append(CreateExcelRowItemClass(itemClassName, propertyNameTypeDic)); classSource.Append("n"); //生成整体数据类,记录整个Excel的所有行数据 classSource.Append(CreateExcelDataClass(dataClassName, itemClassName)); classSource.Append("n"); //生成Asset操作类,用于自动创建Excel对应的Asset文件并赋值 classSource.Append(CreateExcelAssetClass(excelMediumData)); classSource.Append("n"); return classSource.ToString(); } //---------- //生成行数据类 private static StringBuilder CreateExcelRowItemClass(string itemClassName, Dictionary<string, string> propertyNameTypeDic) { //生成Excel行数据类 StringBuilder classSource = new StringBuilder(); classSource.Append("[Serializable]n"); classSource.Append("public class " + itemClassName + " : ExcelItemBasen"); classSource.Append("{n"); //声明所有字段 foreach (var item in propertyNameTypeDic) { classSource.Append(CreateCodeProperty(item.Key, item.Value)); } classSource.Append("}n"); return classSource; } //声明行数据类字段 private static string CreateCodeProperty(string name, string type) { if (string.IsNullOrEmpty(name)) return null; if (name == "id") return null; //判断字段类型 if (type == "int" || type == "Int" || type == "INT") type = "int"; else if (type == "float" || type == "Float" || type == "FLOAT") type = "float"; else if (type == "bool" || type == "Bool" || type == "BOOL") type = "bool"; else type = "string"; //声明 string propertyStr = "tpublic " + type + " " + name + ";n"; return propertyStr; } //---------- //生成数据类 private static StringBuilder CreateExcelDataClass(string dataClassName, string itemClassName) { StringBuilder classSource = new StringBuilder(); classSource.Append("[CreateAssetMenu(fileName = "" + dataClassName + "", menuName = "Excel To ScriptableObject/Create " + dataClassName + "", order = 1)]n"); classSource.Append("public class " + dataClassName + " : ExcelDataBase<" + itemClassName + ">n"); classSource.Append("{n"); //声明字段,行数据类数组 //classSource.Append("tpublic " + itemClassName + "[] items;n"); classSource.Append("}n"); return classSource; } //---------- //生成Asset操作类 private static StringBuilder CreateExcelAssetClass(ExcelMediumData excelMediumData) { if (excelMediumData == null) return null; string excelName = excelMediumData.excelName; if (string.IsNullOrEmpty(excelName)) return null; Dictionary<string, string> propertyNameTypeDic = excelMediumData.propertyNameTypeDic; if (propertyNameTypeDic == null || propertyNameTypeDic.Count == 0) return null; List<Dictionary<string, string>> allItemValueRowList = excelMediumData.allItemValueRowList; if (allItemValueRowList == null || allItemValueRowList.Count == 0) return null; string itemClassName = excelName + "ExcelItem"; string dataClassName = excelName + "ExcelData"; StringBuilder classSource = new StringBuilder(); classSource.Append("#if UNITY_EDITORn"); //类名 classSource.Append("public class " + excelName + "AssetAssignmentn"); classSource.Append("{n"); //方法名 classSource.Append("tpublic static bool CreateAsset(List<Dictionary<string, string>> allItemValueRowList, string excelAssetPath)n"); //方法体,若有需要可加入try/catch classSource.Append("t{n"); classSource.Append("ttif (allItemValueRowList == null || allItemValueRowList.Count == 0)n"); classSource.Append("tttreturn false;n"); classSource.Append("ttint rowCount = allItemValueRowList.Count;n"); classSource.Append("tt" + itemClassName + "[] items = new " + itemClassName + "[rowCount];n"); classSource.Append("ttfor (int i = 0; i < items.Length; i++)n"); classSource.Append("tt{n"); classSource.Append("tttitems[i] = new " + itemClassName + "();n"); foreach (var item in propertyNameTypeDic) { classSource.Append("tttitems[i]." + item.Key + " = "); classSource.Append(AssignmentCodeProperty("allItemValueRowList[i]["" + item.Key + ""]", propertyNameTypeDic[item.Key])); classSource.Append(";n"); } classSource.Append("tt}n"); classSource.Append("tt" + dataClassName + " excelDataAsset = ScriptableObject.CreateInstance<" + dataClassName + ">();n"); classSource.Append("ttexcelDataAsset.items = items;n"); classSource.Append("ttif (!Directory.Exists(excelAssetPath))n"); classSource.Append("tttDirectory.CreateDirectory(excelAssetPath);n"); classSource.Append("ttstring pullPath = excelAssetPath + "/" + typeof(" + dataClassName + ").Name + ".asset";n"); classSource.Append("ttUnityEditor.AssetDatabase.DeleteAsset(pullPath);n"); classSource.Append("ttUnityEditor.AssetDatabase.CreateAsset(excelDataAsset, pullPath);n"); classSource.Append("ttUnityEditor.AssetDatabase.Refresh();n"); classSource.Append("ttreturn true;n"); classSource.Append("t}n"); // classSource.Append("}n"); classSource.Append("#endifn"); return classSource; } //声明Asset操作类字段 private static string AssignmentCodeProperty(string stringValue, string type) { //判断类型 if (type == "int" || type == "Int" || type == "INT") { return "Convert.ToInt32(" + stringValue + ")"; } else if (type == "float" || type == "Float" || type == "FLOAT") { return "Convert.ToSingle(" + stringValue + ")"; } else if (type == "bool" || type == "Bool" || type == "BOOL") { return "Convert.ToBoolean(" + stringValue + ")"; } else return stringValue; } #endregion } #endif
#if UNITY_EDITOR using UnityEngine; using UnityEditor; using System.IO; using System.Collections.Generic; using System.Linq; public class BuildExcelEditor : Editor { } public class BuildExcelWindow : EditorWindow { //[MenuItem("MyTools/Excel/Build Script")] //public static void CreateExcelCode() //{ // ExcelDataReader.ReadAllExcelToCode(); //} //[MenuItem("MyTools/Excel/Build Asset")] //public static void CreateExcelAssset() //{ // ExcelDataReader.CreateAllExcelAsset(); //} [MenuItem("MyTools/Excel/Build Window")] public static void ShowExcelWindow() { //显示操作窗口方式一 //BuildExcelWindow buildExcelWindow = GetWindow<BuildExcelWindow>(); //buildExcelWindow.Show(); //显示操作窗口方式二 EditorWindow.GetWindow(typeof(BuildExcelWindow)); } private string showNotify; private Vector2 scrollPosition = Vector2.zero; private List<string> fileNameList = new List<string>(); private List<string> filePathList = new List<string>(); private void Awake() { titleContent.text = "Excel数据读取"; } private void OnEnable() { showNotify = ""; GetExcelFile(); } private void OnDisable() { showNotify = ""; fileNameList.Clear(); filePathList.Clear(); } private void OnGUI() { scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.Width(position.width), GUILayout.Height(position.height)); //自动创建C#脚本 GUILayout.Space(10); GUILayout.Label("Excel To Script"); for (int i = 0; i < fileNameList.Count; i++) { if (GUILayout.Button(fileNameList[i], GUILayout.Width(200), GUILayout.Height(30))) { SelectExcelToCodeByIndex(i); } } if (GUILayout.Button("All Excel", GUILayout.Width(200), GUILayout.Height(30))) { SelectExcelToCodeByIndex(-1); } //自动创建Asset文件 GUILayout.Space(20); GUILayout.Label("Script To Asset"); for (int i = 0; i < fileNameList.Count; i++) { if (GUILayout.Button(fileNameList[i], GUILayout.Width(200), GUILayout.Height(30))) { SelectCodeToAssetByIndex(i); } } if (GUILayout.Button("All Excel", GUILayout.Width(200), GUILayout.Height(30))) { SelectCodeToAssetByIndex(-1); } // GUILayout.Space(20); GUILayout.Label(showNotify); // GUILayout.EndScrollView(); //this.Repaint(); } //读取指定路径下的Excel文件名 private void GetExcelFile() { fileNameList.Clear(); filePathList.Clear(); if (!Directory.Exists(ExcelDataReader.excelFilePath)) { showNotify = "无效路径:" + ExcelDataReader.excelFilePath; return; } string[] excelFileFullPaths = Directory.GetFiles(ExcelDataReader.excelFilePath, "*.xlsx"); if (excelFileFullPaths == null || excelFileFullPaths.Length == 0) { showNotify = ExcelDataReader.excelFilePath + "路径下没有找到Excel文件"; return; } filePathList.AddRange(excelFileFullPaths); for (int i = 0; i < filePathList.Count; i++) { string fileName = filePathList[i].Split('/').LastOrDefault(); fileName = filePathList[i].Split('\').LastOrDefault(); fileNameList.Add(fileName); } showNotify = "找到Excel文件:" + fileNameList.Count + "个"; } //自动创建C#脚本 private void SelectExcelToCodeByIndex(int index) { if (index >= 0 && index < filePathList.Count) { string fullPath = filePathList[index]; ExcelDataReader.ReadOneExcelToCode(fullPath); } else { ExcelDataReader.ReadAllExcelToCode(); } } //自动创建Asset文件 private void SelectCodeToAssetByIndex(int index) { if (index >= 0 && index < filePathList.Count) { string fullPath = filePathList[index]; ExcelDataReader.CreateOneExcelAsset(fullPath); } else { ExcelDataReader.CreateAllExcelAsset(); } } } #endif
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; public class ExcelDataBase<T> : ScriptableObject where T : ExcelItemBase { public T[] items; public T GetExcelItem(string targetId) { if(items != null && items.Length > 0) { return items.FirstOrDefault(item => item.id == targetId); } return null; } } public class ExcelItemBase { public string id; }
/*Auto Create, Don't Edit !!!*/ using UnityEngine; using System.Collections.Generic; using System; using System.IO; [Serializable] public class RoleParameExcelItem : ExcelItemBase { public int attack; public int maxHealth; public int energySpeed; public int maxEnergy; public float mass; public int maxSpeed; public int acceleration; public float critProbability; public float critValue; public float angryTime; public float angrySpeed; public float bounceTime; public float resist; public int skillLevel; } [CreateAssetMenu(fileName = "RoleParameExcelData", menuName = "Excel To ScriptableObject/Create RoleParameExcelData", order = 1)] public class RoleParameExcelData : ExcelDataBase<RoleParameExcelItem> { } #if UNITY_EDITOR public class RoleParameAssetAssignment { public static bool CreateAsset(List<Dictionary<string, string>> allItemValueRowList, string excelAssetPath) { if (allItemValueRowList == null || allItemValueRowList.Count == 0) return false; int rowCount = allItemValueRowList.Count; RoleParameExcelItem[] items = new RoleParameExcelItem[rowCount]; for (int i = 0; i < items.Length; i++) { items[i] = new RoleParameExcelItem(); items[i].id = allItemValueRowList[i]["id"]; items[i].attack = Convert.ToInt32(allItemValueRowList[i]["attack"]); items[i].maxHealth = Convert.ToInt32(allItemValueRowList[i]["maxHealth"]); items[i].energySpeed = Convert.ToInt32(allItemValueRowList[i]["energySpeed"]); items[i].maxEnergy = Convert.ToInt32(allItemValueRowList[i]["maxEnergy"]); items[i].mass = Convert.ToSingle(allItemValueRowList[i]["mass"]); items[i].maxSpeed = Convert.ToInt32(allItemValueRowList[i]["maxSpeed"]); items[i].acceleration = Convert.ToInt32(allItemValueRowList[i]["acceleration"]); items[i].critProbability = Convert.ToSingle(allItemValueRowList[i]["critProbability"]); items[i].critValue = Convert.ToSingle(allItemValueRowList[i]["critValue"]); items[i].angryTime = Convert.ToSingle(allItemValueRowList[i]["angryTime"]); items[i].angrySpeed = Convert.ToSingle(allItemValueRowList[i]["angrySpeed"]); items[i].bounceTime = Convert.ToSingle(allItemValueRowList[i]["bounceTime"]); items[i].resist = Convert.ToSingle(allItemValueRowList[i]["resist"]); items[i].skillLevel = Convert.ToInt32(allItemValueRowList[i]["skillLevel"]); } RoleParameExcelData excelDataAsset = ScriptableObject.CreateInstance<RoleParameExcelData>(); excelDataAsset.items = items; if (!Directory.Exists(excelAssetPath)) Directory.CreateDirectory(excelAssetPath); string pullPath = excelAssetPath + "/" + typeof(RoleParameExcelData).Name + ".asset"; UnityEditor.AssetDatabase.DeleteAsset(pullPath); UnityEditor.AssetDatabase.CreateAsset(excelDataAsset, pullPath); UnityEditor.AssetDatabase.Refresh(); return true; } } #endif
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public class ExcelManager : CSSingleton<ExcelManager> { Dictionary<Type, ExcelDataBase<ExcelItemBase>> excelDataDic = new Dictionary<Type, ExcelDataBase<ExcelItemBase>>(); public void Init() { } public T GetExcelData<T, V>() where T : ExcelDataBase<V> where V : ExcelItemBase { Type type = typeof(T); if (excelDataDic.ContainsKey(type)) return excelDataDic[type] as T; T excelData = LoadManager.ResourcesLoad<T>(ConstConfig.excelAssetResourcesPath, type.Name); if (excelData != null) excelDataDic.Add(type, excelData as ExcelDataBase<ExcelItemBase>); return excelData; } public V GetExcelItem<T, V>(string targetId) where T : ExcelDataBase<V> where V : ExcelItemBase { Type type = typeof(T); T excelData = GetExcelData<T, V>(); if (excelData != null) return excelData.GetExcelItem(targetId); return null; } }
public class ExcelTest : MonoBehaviour { void Start() { RoleParameExcelData roleParameExcelData = Resources.Load<RoleParameExcelData>("ExcelAsset/RoleParameExcelData"); if(roleParameExcelData != null) { for (int i = 0; i < roleParameExcelData.items.Length; i++) { Debug.Log(roleParameExcelData.items[i].id); } } } }
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算