您的位置:首页 > 移动开发 > Unity3D

Unity 实现转策划Execl文档,并根据Xml配置文件生成代码

2017-06-28 23:39 489 查看
本工具主要还是依赖别人提供的一个解析Excel文件的工具。

下载链接:http://www.xuanyusong.com/archives/2429

工具实现的过程是将Excel表中的数据根据xml配置文件读取出来后,然后又根据xml文件写入一个自定义的文件中。游戏中就还是根据xml配置读取数据。

首先来看一下xml配置文件吧:

<?xml version="1.0" encoding="gb2312" standalone="yes" ?>
<metalib tagsetversion="1" name="" version="1">
<table name="test">
<column name="id" type="int" count ="1" descript ="id"/>
<column name="name" type="string" count ="1" descript ="name"/>
<column name="sex" type="int" count ="1" descript ="sex"/>
<column name="age" type="int" count ="1" descript ="age"/>
<column name="descrip" type="string" count ="1" descript ="descrip"/>
</table>
<table name="test1">
<column name="id" type="int" count ="1" descript ="id"/>
<column name="name" type="string" count ="3" descript ="name"/>
<column name="sex" type="int" count ="1" descript ="sex"/>
<column name="age" type="int" count ="1" descript ="age"/>
<column name="descrip" type="string" count ="1" descript ="descrip"/>
</table>
<table name="test2">
<column name="id" type="int" count ="1" descript ="id"/>
<column name="name" type="string" count ="1" descript ="name"/>
<column name="sex" type="int" count ="3" descript ="sex"/>
<column name="age" type="int" count ="1" descript ="age"/>
<column name="descrip" type="string" count ="1" descript ="descrip"/>
</table>
</metalib>

这其中每一个table就对应一个Excel表,table标签中就是表的字段。
下面就是整个工具的全部代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using Excel;
using System.Data;
using System.Xml;
using System.Threading;
using UObject = UnityEngine.Object;

public class ExeclRead : EditorWindow
{
private static string m_TableXmlData = "GameData/Data.xml";
private static string m_CsCodePath = "GameData/GameDataClass.cs";
private static string m_ChangedTableDataPath = "GameData";
private static string m_ChangedTableDataPathExtend = ".tdr";
private static string m_GameDataParentClassName = "GameDataParent";
private static string m_GameDataInitFunName = "SetData";

private static string m_ApplictionDataPath = "";
private static bool m_IsShowBar = false;
private static float m_BarValue = 0.0f;
private static string m_BarTitle = "";
private static string m_BarMessage = "";

private static bool m_IsRefreshAsset = false;
[MenuItem("Tools/GameData/GenerateCode")]
public static void GenerateCode()
{
m_ApplictionDataPath = Application.dataPath;
new Thread(GenerateCodeTherad).Start();
}

[MenuItem("Tools/GameData/Execl")]
public static void ChangeExcel()
{
m_ApplictionDataPath = Application.dataPath;
UObject[] excelTables = Selection.GetFiltered(typeof(UObject), SelectionMode.DeepAssets);
foreach (var table in excelTables)
{
string tableName = AssetDatabase.GetAssetPath(table);
if (tableName.IndexOf(".xls") != -1 || tableName.IndexOf(".xlsx") != -1)
{
string tableCompeletPath = GetAssetPath(tableName);
ChangeTable(tableCompeletPath);
}
}
}

[MenuItem("Tools/GameData/Test")]
public static void GameDataTest() {
m_ApplictionDataPath = Application.dataPath;
UObject[] excelTables = Selection.GetFiltered(typeof(UObject), SelectionMode.DeepAssets);
string xmlPath = m_ApplictionDataPath + "/" + m_TableXmlData;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlPath);
XmlNodeList tableNodes = xmlDoc.GetElementsByTagName("table");
foreach (var table in excelTables)
{
string tableName = AssetDatabase.GetAssetPath(table);
if (tableName.IndexOf(".tdr") != -1)
{
string tableCompeletPath = GetAssetPath(tableName);
FileStream fileStream = File.Open(tableCompeletPath, FileMode.Open, FileAccess.Read);
XmlNode targetTable = null;
foreach (XmlNode fildtable in tableNodes)
{
if (fildtable.Attributes["name"].Value == table.name)
{
targetTable = fildtable;
break;
}
}
if (targetTable == null) continue;
XmlNodeList tableFilds = targetTable.ChildNodes;
while (fileStream.Position < fileStream.Length) {
foreach (XmlNode fild in tableFilds)
{
string name = fild.Attributes["name"].Value;
string type = fild.Attributes["type"].Value;
int cout = int.Parse(fild.Attributes["count"].Value);
for (int k = 0; k < cout; k++)
{
switch (type)
{
case "float":
byte[] fvalue = new byte[4];
fileStream.Read(fvalue, 0, 4);
float fvalu = BitConverter.ToSingle(fvalue,0);
Debug.Log(fvalu);
break;
case "int":
byte[] ivalue = new byte[4];
fileStream.Read(ivalue, 0, 4);
int inva = BitConverter.ToInt32(ivalue,0);
Debug.Log(inva);
break;
case "string":
byte[] blength = new byte[4];
fileStream.Read(blength, 0, 4);
int length= BitConverter.ToInt32(blength,0);
byte[] bvalue = new byte[length];
fileStream.Read(bvalue, 0, bvalue.Length);
string temp = Encoding.UTF8.GetString(bvalue);
Debug.Log(temp);
break;
}
}
}
}
fileStream.Close();
}
}
}

/// <summary>
/// 开始创建数据表对应的Class
/// </summary>
static void GenerateCodeTherad() {
string xmlPath = m_ApplictionDataPath + "/" + m_TableXmlData;
if (!File.Exists(xmlPath)) return;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlPath);
XmlNodeList tableNodes = xmlDoc.GetElementsByTagName("table");
int allCount = tableNodes.Count;
int currentCounut = 0;
StringBuilder allCodeContent = new StringBuilder( "using System;\r\n using System.Collections; \r\n");
//写入父类
allCodeContent.Append(GenerParentClassCode(m_GameDataParentClassName, m_GameDataInitFunName));
foreach (XmlNode table in tableNodes)
{
string tableName = table.Attributes["name"].Value;
ShowBar("Generate Code", "generate code " + tableName, currentCounut / allCount);
allCodeContent.Append(StartGenerateCode(table));
}
string fileName = m_ApplictionDataPath + "/" + m_CsCodePath;
if (File.Exists(fileName))
{
File.Delete(fileName);
}
FileStream csCodeFilStream = File.Open(fileName, FileMode.CreateNew, FileAccess.ReadWrite);
byte[] csCodeContent = Encoding.UTF8.GetBytes(allCodeContent.ToString());
csCodeFilStream.Write(csCodeContent, 0, csCodeContent.Length);
csCodeFilStream.Close();
HiddenBar();
RefreshAsset();
}

/// <summary>
/// 生成数据表对应的Class的父类
/// </summary>
/// <param name="className"></param>
/// <param name="dataInitFunName"></param>
/// <returns></returns>
static string GenerParentClassCode(string className, string dataInitFunName) {
if (string.IsNullOrEmpty(className) || string.IsNullOrEmpty(dataInitFunName)) return "";
CsCodeBuild parent = new CsCodeBuild(className);
CodeFunction codeFun = new CodeFunction();
codeFun.name = dataInitFunName;
codeFun.parms = new Dictionary<string, string>();
codeFun.parms.Add("data", "Hashtable");
codeFun.isVirtual = true;
parent.AddCodeFunction(codeFun);
return parent.codeContent;
}

/// <summary>
/// 根据数据表创建对应的Class
/// </summary>
/// <param name="tableNode"></param>
/// <returns></returns>
public static string StartGenerateCode(XmlNode tableNode) {
if (tableNode == null) return "";
XmlAttrib
e567
uteCollection attrs = tableNode.Attributes;
string className = attrs["name"].Value;
CsCodeBuild codeBuulder = new CsCodeBuild(className);
codeBuulder.AddExtendClass(m_GameDataParentClassName);
XmlNodeList fildLists = tableNode.ChildNodes;
StringBuilder functionContent = new StringBuilder("");
foreach (XmlNode fild in fildLists) {
int count = int.Parse(fild.Attributes["count"].Value);
string type = fild.Attributes["type"].Value;
string name = fild.Attributes["name"].Value;
CodeFild codeFild = new CodeFild();
codeFild.name = name;
codeFild.type = type;
codeFild.descript = fild.Attributes["descript"].Value;
codeFild.isArray = count > 1;
codeFild.arrayCount = count;
codeBuulder.AddCodeFild(codeFild);
functionContent.Append(GenerateFunctionContent(name,type,count));
}
Dictionary<string, string> parms = new Dictionary<string, string>();
parms.Add("data", "Hashtable");
CodeFunction codeFun = new CodeFunction();
codeFun.name = m_GameDataInitFunName;
codeFun.content = functionContent.ToString();
codeFun.parms = parms;
codeFun.isOverried = true;
codeBuulder.AddCodeFunction(codeFun);
return codeBuulder.codeContent;
}

/// <summary>
/// 生成对应的列的读取方式
/// </summary>
/// <param name="name"></param>
/// <param name="type"></param>
/// <param name="count"></param>
/// <returns></returns>
static string GenerateFunctionContent(string name, string type, int count) {
StringBuilder funcContent = new StringBuilder();
funcContent.Append("\t\t\t");
funcContent.Append(name);
if (count > 1)
{
funcContent.Append( " = data[\"" );
funcContent.Append( name );
funcContent.Append("\"] as ");
funcContent.Append( type );
funcContent.Append("[];\r\n");
}
else
{
switch (type)
{
case "float":
funcContent.Append( " = Convert.ToSingle(data[\"");
funcContent.Append( name );
funcContent.Append("\"]);\r\n");
break;
case "int":
funcContent.Append(" = Convert.ToInt32(data[\"");
funcContent.Append(name);
funcContent.Append("\"]);\r\n");
break;
default:
funcContent.Append(" = data[\"" );
funcContent.Append( name );
funcContent.Append( "\"] as ");
funcContent.Append(type);
funcContent.Append(";\r\n");
break;
}
}

return funcContent.ToString();
}

public static void ChangeTable( string tableCompeletPath) {
string xmlPath = m_ApplictionDataPath + "/" + m_TableXmlData;
if (string.IsNullOrEmpty(tableCompeletPath)) return;
if (!File.Exists(tableCompeletPath)) return;
FileStream stream = File.Open(tableCompeletPath, FileMode.Open,FileAccess.Read);
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
DataSet result = excelReader.AsDataSet();
int columns = result.Tables[0].Columns.Count;
int rows = result.Tables[0].Rows.Count;

FileInfo fileInfo = new FileInfo(tableCompeletPath);
string tableName = fileInfo.Name.Substring(0, fileInfo.Name.IndexOf("."));
string fileName = fileInfo.DirectoryName+"/" + tableName + m_ChangedTableDataPathExtend;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlPath);
XmlNodeList tableNodes = xmlDoc.GetElementsByTagName("table");
XmlNode targetTable = null;
foreach (XmlNode table in tableNodes) {
if (table.Attributes["name"].Value == tableName) {
targetTable = table;
break;
}
}
if (targetTable == null) {
Debug.LogError("dont find table " + tableName);
return;
}
if (File.Exists(fileName))
{
File.Delete(fileName);
}

FileStream gameDataStream = File.Open(fileName, FileMode.CreateNew, FileAccess.ReadWrite);
XmlNodeList fildLists = targetTable.ChildNodes;
for (int i = 1; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
XmlNode fild = fildLists[j];
string type = fild.Attributes["type"].Value;
int cout = int.Parse(fild.Attributes["count"].Value);
for (int k = 0; k < cout; k++) {
byte[] tempData = null;
string nvalue = result.Tables[0].Rows[i][j + k].ToString();
switch (type)
{
case "float":
float fvalue = float.Parse(nvalue);
tempData = BitConverter.GetBytes(fvalue);
// Debug.Log("write: " + fvalue + " length:" + tempData.Length);
break;
case "int":
int ivalue = int.Parse(nvalue);
tempData = BitConverter.GetBytes(ivalue);
//Debug.Log("write: " + ivalue + " length:" + tempData.Length);
break;
case "string":
tempData = Encoding.UTF8.GetBytes(nvalue);
int length = tempData.Length;
byte[] lengthData = BitConverter.GetBytes(length);
gameDataStream.Write(lengthData, 0, lengthData.Length);
//Debug.Log("write: " + nvalue + " length:" + tempData.Length);
break;
}
if (tempData == null) continue;
gameDataStream.Write(tempData, 0, tempData.Length);
}
j += (cout - 1);
}
}
gameDataStream.Close();
}

static bool IsStandType(string typeName)
{
bool isStandType = false;
switch (typeName) {
case "float":
isStandType = true;
break;
case "int":
isStandType = true;
break;
case "string":
isStandType = true;
break;
}
return isStandType;
}

public static string GetAssetPath(string path) {
if (string.IsNullOrEmpty(path)) return null;
string compeletPath = Application.dataPath;
compeletPath = compeletPath.Substring(0, compeletPath.IndexOf("Assets"))+path;
return compeletPath;
}

public static void ShowBar(string barTitle,string barMessage,float barValue) {
m_IsShowBar = true;
m_BarTitle = barTitle;
m_BarMessage = barMessage;
m_BarValue = barValue;
}

public static void HiddenBar() {
m_IsShowBar = false;
}

public static void RefreshAsset() {
m_IsRefreshAsset = true;
}

void OnGUI()
{
if (m_IsShowBar) {
EditorUtility.DisplayProgressBar(m_BarTitle,m_BarMessage, m_BarValue);
}

if (m_IsRefreshAsset) {
AssetDatabase.Refresh();
m_IsRefreshAsset = false;
}
}
}

public class CodeFild
{
public string name = "";
public string type = "";
public bool isArray = false;
public string descript = null;
public int arrayCount = 0;
public bool isStatic = false;
}

public class CodeFunction
{
public string name = "";
public Dictionary<string, string> parms = null;
public string content = "";
public string returnType = "void";
public bool isStatic = false;
public bool isOverried = false;
public bool isVirtual = false;
}

public class CsCodeBuild{

private string m_ClassName = "";
private string m_CodeContent = "";
private string m_SpaceNmae = "";
private string m_ExtendClassName = "";
public string codeContent {
get {
m_CodeContent = GenerCodeContent();
return m_CodeContent;
}
}

public string className {
get {
return m_ClassName;
}
}
private List<string> m_UseSpaceName = new List<string>();
private List<CodeFild> m_CodeFilds = new List<CodeFild>();
private List<CodeFunction> m_CodeFunctions = new List<CodeFunction>();
public CsCodeBuild(string className)
{
if (!string.IsNullOrEmpty(className)) {
m_ClassName = GenerateClassName(className);
}
}

public static string GenerateClassName(string className) {
if (string.IsNullOrEmpty(className)) return null;
string tempClassName = "";
string upClassName = className.ToUpper();
string fistChar = upClassName.Substring(0, 1);
tempClassName = fistChar + className.Substring(1);
return tempClassName;
}

public void AddUseSpaceName(string spaceName) {
m_UseSpaceName.Add(spaceName);
}

public void AddNameSpace(string spaceName) {
m_SpaceNmae = spaceName;
}

public void AddExtendClass(string extendClassName) {
m_ExtendClassName = extendClassName;
}
public void AddCodeFild(CodeFild codeFild) {
if (codeFild == null) return;
m_CodeFilds.Add(codeFild);
}

public void AddCodeFunction(CodeFunction codeFunction) {
if (codeFunction == null) return;
m_CodeFunctions.Add(codeFunction);
}

string GenerCodeContent() {
StringBuilder content = new StringBuilder("");
//写入引用命名空间
foreach (string spaceName in m_UseSpaceName) {
content.Append("using ");
content.Append(spaceName);
content.Append(";\r\n");
}
//写入命名空间
if (!string.IsNullOrEmpty(m_SpaceNmae)) {
content.Append ( "namespace ");
content.Append( m_SpaceNmae);
content.Append(" { \r\n");
}
content.Append("public class ");
content.Append(m_ClassName);
//写入父类
if (!string.IsNullOrEmpty(m_ExtendClassName)) {
content.Append(" : ");
content.Append(m_ExtendClassName);
}
content.Append(" {\r\n");
//写入属性
foreach (var fild in m_CodeFilds) {
content.Append(GenerateFild(fild));
}
//写入方法
foreach (var function in m_CodeFunctions) {
content.Append(GenerateFunctionCode(function));
}

if (!string.IsNullOrEmpty(m_SpaceNmae))
{
content.Append("\t\t } \r\n");
}

content.Append( "\t } \r\n \r\n");
return content.ToString();
}
string GenerateFild(CodeFild codeFild) {
if (codeFild == null) return "";
StringBuilder fildCode = new StringBuilder();
fildCode.Append("\t\t public ");
fildCode.Append(codeFild.type);
fildCode.Append(" ");
if (codeFild.isArray)
{
fildCode.Append("[] ");
}
fildCode.Append(codeFild.name);
fildCode.Append("; ");
if (!string.IsNullOrEmpty(codeFild.descript))
{
fildCode.Append("//");
fildCode.Append(codeFild.descript);
}
fildCode.Append("\r\n");
return fildCode.ToString();
}

string GenerateFunctionCode(CodeFunction codeFun) {
if (codeFun == null) return "";
StringBuilder functionCode = new StringBuilder();
functionCode.Append("\t\t public ");
if (codeFun.isStatic)
{
functionCode.Append("static ");
}
if (codeFun.isOverried) {
functionCode.Append("override ");
}
if (codeFun.isVirtual) {
functionCode.Append("virtual ");
}

functionCode.Append(codeFun.returnType);
functionCode.Append(" ");
functionCode.Append(codeFun.name);
functionCode.Append("(");
if (codeFun.parms != null)
{
foreach (var parm in codeFun.parms)
{
functionCode.Append(parm.Value );
functionCode.Append(" ");
functionCode.Append(parm.Key);
}
}
functionCode.Append(") { \r\n");
functionCode.Append(codeFun.content);
functionCode.Append(" \t\t\t } \r\n");
return functionCode.ToString();
}
}

代码有点多。不足的地方还请各位大神指出。
其中生成cs代码的部分还不是特别的完善。

下面就是一个读取的类:

using System.Collections;
using System.Collections.Generic;
using System.Xml;
using UnityEngine;
using System.IO;
using System;
using System.Text;
using CObject = System.Object;
public class TableDataMannager {
private static TableDataMannager s_Instance = null;

private static readonly string s_XmlFilePath = "/GameData/Data.xml";
private static readonly string s_TableParentDir = "/GameData/";

public static TableDataMannager instance {
get {
if (s_Instance == null) {
s_Instance = new TableDataMannager();
}
return s_Instance;
}
}

private Dictionary<string, CObject> readTableData = new Dictionary<string, CObject>();

private XmlNodeList m_XmlTable = null;
public XmlNodeList xmlTable {
get {
if (m_XmlTable == null) {
string xmlPath = Application.dataPath + s_XmlFilePath;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlPath);
m_XmlTable = xmlDoc.GetElementsByTagName("table");
}
return m_XmlTable;
}
}

public List<T> GetTableData<T>(string tableName) where T : GameDataParent ,new(){
CObject temptable;
tableName = tableName.ToLower();
if (readTableData.TryGetValue(tableName, out temptable)) {
return temptable as List<T>;
}
List<T> table = new List<T>();
string tdrFile = Application.dataPath+ s_TableParentDir + tableName + ".tdr";
XmlNodeList tableNodes = xmlTable;
if (tableNodes == null) { return null; }
XmlNode targetTable = null;
for (int i=0;i<tableNodes.Count;i++)
{
if (tableNodes[i].Attributes["name"].Value.ToLower() == tableName)
{
targetTable = tableNodes[i];
break;
}
}
if (targetTable == null) return table;
FileStream fileStream = File.Open(tdrFile, FileMode.Open, FileAccess.Read);
BufferedStream bufferReader = new BufferedStream(fileStream);
XmlNodeList tableFilds = targetTable.ChildNodes;
while (bufferReader.Position < bufferReader.Length)
{
T clown = new T();
Hashtable data = new Hashtable();
for (int i=0;i<tableFilds.Count;i++)
{
string name = tableFilds[i].Attributes["name"].Value;
string type = tableFilds[i].Attributes["type"].Value;
int cout = int.Parse(tableFilds[i].Attributes["count"].Value);
switch (type)
{
case "float":
if (cout > 1)
{
data[name] = ReadFloatArray(bufferReader, cout);
}
else {
data[name] = ReadSingleFloat(bufferReader);
}
break;
case "int":
if (cout > 1)
{
data[name] = ReadIntArray(bufferReader, cout);
}
else {
data[name] = ReadSingInt(bufferReader);
}
break;
case "string":
if (cout > 1)
{
data[name] = ReadStringArray(bufferReader,cout);
}
else {
data[name] = ReadSingString(bufferReader);
}
break;
}
}
clown.SetData(data);
table.Add(clown);
}
fileStream.Close();
readTableData.Add(tableName, table );
return table;
}

float ReadSingleFloat(BufferedStream bufferReader) {
byte[] fvalue = new byte[4];
bufferReader.Read(fvalue, 0, 4);
return BitConverter.ToSingle(fvalue, 0);
}

float[] ReadFloatArray(BufferedStream bufferReader, int count) {
float[] data = new float[count];
for (int i = 0; i < count; i++) {
byte[] fvalue = new byte[4];
bufferReader.Read(fvalue, 0, 4);
data[i]=BitConverter.ToSingle(fvalue, 0);
}
return data;
}

int ReadSingInt(BufferedStream bufferReader) {
byte[] ivalue = new byte[4];
bufferReader.Read(ivalue, 0, 4);
return BitConverter.ToInt32(ivalue, 0);
}

int[] ReadIntArray(BufferedStream bufferReader, int count) {
int[] data = new int[count];
for (int i = 0; i < count; i++)
{
byte[] fvalue = new byte[4];
bufferReader.Read(fvalue, 0, 4);
data[i] = BitConverter.ToInt32(fvalue, 0);
}
return data;
}

string ReadSingString(BufferedStream bufferReader) {
byte[] blength = new byte[4];
bufferReader.Read(blength, 0, 4);
int length = BitConverter.ToInt32(blength, 0);
byte[] bvalue = new byte[length];
bufferReader.Read(bvalue, 0, bvalue.Length);
return Encoding.UTF8.GetString(bvalue);
}

string[] ReadStringArray(BufferedStream bufferReader, int count) {
string[] data = new string[count];
for (int i = 0; i < count; i++)
{
byte[] blength = new byte[4];
bufferReader.Read(blength, 0, 4);
int length = BitConverter.ToInt32(blength, 0);
byte[] bvalue = new byte[length];
bufferReader.Read(bvalue, 0, bvalue.Length);
data[i] = Encoding.UTF8.GetString(bvalue);
}
return data;
}
}

整个的读取速度还是挺快的,经测试,10多万行也就一秒左右。(对我来说还是挺满意的)。有不足的地方还请各路大神多度指教。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐