在unity游戏开发中很多时候都需要策划配置一些XML表,但是XML的读取效率并不高,相比XML读取txt的效率会高一些,这样我们可以用C#的序列化与反序列化来做这样的转化。
先在editor下将XML表读取到内存,然后序列化到txt中,然后游戏中只用读取txt然后反序列化就得到数据了。
XML格式如下:
<root>
<sample sid="1" class="www.projectName.fileName" name="名字1"/>
<sample sid="2" class="www.projectName.fileName" name="名字2"/>
</root>
要存储表里记录要用[Serializable]修饰对象,我定义了一个基类BaseTemplate
using UnityEngine;
using System.Collections;
using System;
[Serializable]
abstract public class BaseTemplate
{
public int sid;//配置编号(不可重复)
public string name;//名称
public static System.Type getTypeFromClass(string className)
{
if(className == "www.projectName.fileName")
{
return typeof(TestTemplate);
}
return null;
}
public static BaseTemplate create(System.Type t)
{
object o = System.Activator.CreateInstance(t);
return (BaseTemplate)o;
}
public abstract void postProcess();
}
getTypeFromClass方法用于获取配置表中class字段与自己定义的class对应
所有配置类都继承它,如:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
[Serializable]
public class TestTemplate : BaseTemplate
{
public static Dictionary<int, BaseTemplate> manager = new Dictionary<int, BaseTemplate>();
public override void postProcess()
{
}
}
Serializable:要使一个类可序列化,最简单的方法是使用 Serializable 属性对它进行标记。
Deserialize:反序列化,需要知道文件存储的类型,然后强制转换就好了。
我的XML文件放都在Assets/ZBuffer/XML下,序列化的二进制文件存储在Resources/Data下面(文件名同类名)。
序列化与反序列化的代码如下:
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEditor;
using UnityEngine;
public class XMLBuile
{
static List<System.Type> templateTypeList = new List<System.Type>();
static void init()
{
templateTypeList.Clear ();
templateTypeList.Add (typeof(TestTemplate));
}
//for building xml files
[MenuItem("Tool/Build/Build XML")]
static void buildXML()//序列化
{
init ();
string dir = Application.dataPath + "/ZBuffer/XML/";
DirectoryInfo dirinfo = new DirectoryInfo(dir);
FileInfo[] infos = dirinfo.GetFiles();
for(int i = 0; i < infos.Length; ++i)
{
string filename = infos[i].FullName;
if(Path.GetExtension(filename) == ".xml")
{
ReadXML (filename);
}
}
saveAllTemplateToSerialFile ();
}
static void saveAllTemplateToSerialFile()
{
for(int i = 0; i < templateTypeList.Count; ++i)
{
FileSerialize (templateTypeList[i]);
}
}
static void ReadXML(string filename)
{
Debug.Log (filename);
byte[] bytes = null;
bytes = File.ReadAllBytes (filename);
realLoad(bytes);
}
static void realLoad(byte[] bytes)
{
if (bytes != null)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load (new MemoryStream (bytes));
XmlNode rootNode = xmlDoc.FirstChild;
if (rootNode.Name == "root")
{
string className = "";
int rscCount = rootNode.ChildNodes.Count;
if(rscCount > 0)
{
for (int n = 0; n < rscCount; n++)
{
XmlNode node = rootNode.ChildNodes[n];
if(node.Name == "sample")
{
for (int m = 0; m < node.Attributes.Count; m++)
{
XmlAttribute attr = node.Attributes[m];
if(attr.Name == "class")
{
className = attr.Value;
break;
}
}
}
if(className != "")
{
break;
}
}
}
if(className == "")
{
Debug.Log("<color=red>load failure xml file format error</color>");
}
for (int n = 0; n < rscCount; n++)
{
XmlNode node = rootNode.ChildNodes[n];
if(node.Name == "sample")
{
for (int m = 0; m < node.Attributes.Count; m++)
{
XmlAttribute attr = node.Attributes[m];
if(attr.Name == "class")
{
className = attr.Value;
break;
}
}
}
System.Type type = BaseTemplate.getTypeFromClass(className);
if(type == null)
{
continue;
}
FieldInfo fi = type.GetField("manager");
Dictionary<int, BaseTemplate> manager = (Dictionary<int, BaseTemplate>)(fi.GetValue(null));
if(node.Name == "sample")
{
if(node.Attributes == null)
{
continue;
}
BaseTemplate tmpl = BaseTemplate.create(type);
for (int m = 0; m < node.Attributes.Count; m++)
{
XmlAttribute attr = node.Attributes[m];
if(attr.Name == "class")
{
continue;
}
loadAttribute(attr, type, tmpl);
}
manager[tmpl.sid] = tmpl;
tmpl.postProcess();
}
}
}
}
else
{
Debug.Log("<color=red>load failure </color>");
}
}
static void loadAttribute(XmlAttribute attr, System.Type type, BaseTemplate tmpl)
{
string attrName = attr.Name;
FieldInfo fi = type.GetField(attrName);
if(fi == null)
{
Debug.Log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ failure : " + attrName);
}
else
{
if(fi.FieldType == typeof(int))
{
fi.SetValue(tmpl, int.Parse(attr.Value));
}
else if(fi.FieldType == typeof(string))
{
fi.SetValue(tmpl, attr.Value);
}
else if(fi.FieldType == typeof(float))
{
fi.SetValue(tmpl, float.Parse(attr.Value));
}
}
}
public static bool FileSerialize (System.Type type)
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(getSerialFilePathInDataFolder(type));
FieldInfo fi = type.GetField("manager");
Dictionary<int, BaseTemplate> manager = (Dictionary<int, BaseTemplate>)(fi.GetValue(null));
if(manager == null)
{
return true;
}
bf.Serialize(file, manager);
file.Close();
return true;
}
static string getSerialFilePathInDataFolder(System.Type type)
{
string filename = type.ToString ();
return Application.dataPath + "/Resources/Data/" + filename + ".txt";
}
//反序列化
static public void loadAll()
{
init ();
loadAllTemplateFromSerialFile();
}
static bool loadAllTemplateFromSerialFile()
{
bool suc = true;
for(int i = 0; i < templateTypeList.Count; ++i)
{
bool b = load (templateTypeList[i]);
suc &= b;
}
return suc;
}
static bool load(System.Type type)
{
BinaryFormatter bf = new BinaryFormatter();
FieldInfo fi = type.GetField("manager");
Dictionary<int, BaseTemplate> manager = (Dictionary<int, BaseTemplate>)(fi.GetValue(null));
if(manager == null)
{
return true;
}
FileStream file = File.Open(getSerialFilePathInUserFolder(type), FileMode.Open);
manager = (Dictionary<int, BaseTemplate>)bf.Deserialize(file);
fi.SetValue(null, manager);
file.Close ();
return true;
}
static string getSerialFilePathInUserFolder(System.Type type)
{
string filename = type.ToString ();
return Application.persistentDataPath + "/data/" + filename + ".txt";
}
}
序列化在Editor下执行,反序列化只需调用XMLBuile.loadAll(); 就行了。
项目中看到的效果是,之前读取所有XML需要十几秒,现在2秒左右。