Unity项目中常见TXT、XML与JSON等格式文本读取解析
开发游戏项目过程中,你会发现各式各样的配置表需要去读取解析供使用如商品、道具、活动、任务等等。一般情况下,一个游戏项目中尽量统一去使用一种格式的文件当配置表,本人当前所参与的项目中,以TXT文件当配置表为主。另外的特殊情况下,也会运用XML。本篇文章主要介绍Unity游戏项目中关于TXT、XML文件的读取解析。另外,也会简单介绍游戏中常用一些特殊的“数据格式”来配置表中特殊字段的特殊值如JSON格式,Bit位,时间戳,数组等。
1.TXT
根据TXT文件存储的位置与系统提供的的API的不同,大概有以下4种方式来读取解析。
(1)将存放于项目工程某一文件夹下的TXT文件直接拖到某一脚本的Inspector属性面板中赋值引用。该TXT变量类型为TextAsset。
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ///
/// txt文件 /// public TextAsset testTxtFile; ///
/// 读取TXT /// private void ReadTxt() { string content = testTxtFile.text; //获取到文本内容 string [] allLines = content.Split( 'n' ); //根据换行符“划分”出多个“行文本” for ( int i = 0; i < allLines.Length; i++) //读取解析所有行 { //解析出一行中的各列 string [] LineContent = allLines[i].Split( new string []{ " " },StringSplitOptions.RemoveEmptyEntries); for ( int j = 0; j < LineContent.Length; j++) { Debug.Log(LineContent[j]); } } } |
运行结果如下图
(2)将TXT文件放在Resources文件夹下,通过Resources.Load(“XXX”) AsTextAsset实现。
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | ///
/// 读取TXT /// private void ReadTxtFromResources() { TextAsset textAsset = Resources.Load( "test" ) as TextAsset; string content = textAsset.text; //获取到文本内容 string [] allLines = content.Split( 'n' ); //根据换行符“划分”出多个“行文本” for ( int i = 0; i < allLines.Length; i++) //读取解析所有行 { //解析出一行中的各列 string [] LineContent = allLines[i].Split( new string []{ " " },StringSplitOptions.RemoveEmptyEntries); for ( int j = 0; j < LineContent.Length; j++) { Debug.Log(LineContent[j]); } } } |
运行结果与上述一样,这里略过。
(3)通过协程与WWW读取解析存储于本地又或者服务端的TXT文件。
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | /// /// 读取TXT /// IEnumerator ReadTxtFromWWW() { yield return www; if (www.error != null ) { Debug.Log( "www加载出错!" ); } else if (www.isDone) { string content = www.text; //获取到文本内容 string [] allLines = content.Split( 'n' ); //根据换行符“划分”出多个“行文本” for ( int i = 0; i < allLines.Length; i++) //读取解析所有行 { //解析出一行中的各列 string [] LineContent =allLines[i].Split( new string [] { " " },StringSplitOptions.RemoveEmptyEntries); for ( int j = 0; j < LineContent.Length; j++) { Debug.Log(LineContent[j]); } } } } //Use this for initialization voidStart () { StartCoroutine( "ReadTxtFromWWW" ); //通过协程开启www加载读取解析TXT文档 } |
运行结果如上,这里略过。
(4)通过文件流的方式即StreamReader读取解析TXT文件
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | /// /// 读取TXT /// private void ReadTxtFromStream() { StreamReader sr = new StreamReader( "Assets/test.txt" ); string content = sr.ReadToEnd(); //获取到文本内容 string [] allLines = content.Split( 'n' ); //根据换行符“划分”出多个“行文本” for ( int i = 0; i < allLines.Length; i++) //读取解析所有行 { //解析出一行中的各列 string [] LineContent = allLines[i].Split( new string [] { " " },StringSplitOptions.RemoveEmptyEntries); for ( int j = 0; j < LineContent.Length; j++) { Debug.Log(LineContent[j]); } } } |
特别注意的是,StreamReader sr= new StreamReader("Assets/test.txt")的路径是“根目录”[默认]+“Asset/test.txt”。运行结果相同,略过截图。
2.XML
首先,封装一个Student.cs学生信息类,属性有ID、名字、年龄与性别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | /// /// 封装学生的信息 /// public class Student{ /// ///ID /// public string Id; /// /// 名字 /// public string Name; /// /// 年龄 /// public int Age; /// /// 性别 /// public string Sex; } |
另外,新建一个存储学生信息的test.xml文件
1 2 3 4 5 6 7 8 9 10 11 |
< root > < student id = "1" value = "张三" > < age id = "1" value = "18" >
|
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | /// /// 学生信息集合 /// public List StudentList = new List(); /// /// 读取XML文档 /// private void ReadXML() { XmlDocument Doc = new XmlDocument(); //创建XMLDocument文档 Doc.Load(Application.dataPath + "/XML/test.xml" ); //加载XML文件 XmlNodeList NodeList = Doc.SelectSingleNode( "root" ).ChildNodes; //获取到root节点下的所有子节点 foreach (XmlNode xn in NodeList) { XmlElement xe = (XmlElement)xn; Student curStudent = new Student(); curStudent.Id = xe.GetAttribute( "id" ); curStudent.Name = xe.GetAttribute( "value" ); XmlNodeList childNodeList = xn.ChildNodes; //获取student节点下的所有子节点 foreach (XmlNode xn2 in childNodeList) { XmlElement xe2 = (XmlElement)xn2; if (xe2.GetAttribute( "id" ).Equals( "1" )) { curStudent.Age = int .Parse(xe2.GetAttribute( "value" )); continue ; } if (xe2.GetAttribute( "id" ).Equals( "2" )) { curStudent.Sex = xe2.GetAttribute( "value" ); continue ; } } StudentList.Add(curStudent); } } void Start() { ReadXML(); //读取XML文件 for ( int i = 0; i < StudentList.Count; i++) { Debug.Log( "学生信息:" + " id:" +StudentList[i].Id+ "name:" +StudentList[i].Name+ " age:" +StudentList[i].Age+ "sex:" +StudentList[i].Sex); } } |
运行结果如下:
值得注意的是,在设计存储数据信息的XML文档结构时,需要考虑到我们要存储数据的特点。一份设计比较好的XML文档,有既方便阅读又方便读取解析的优点。
3.数据格式
在一个游戏中,活动系统是必不可少而且重要的组成部分。因此,一定会有一张关于活动的TXT文件配置表。活动一般都会有这么几个属性即字段:开始/结束时间(时间戳格式即TimeStamp:2017-01-18 00:00:00);是否可重复参加、是否在前端展示、是否已开启(3个Bit位格式:2的0次方位0/1表示是否可重复参加,2的1次方位表示是否在前端展示、2的2次方表示是否已开启。比方说,如果3个Bit位转化成10进制值为6时,2进制表示时为110,则说明该活动已经开启,在前端展示但不可重复参加。把一张配置表中非是即否的多个字段“合成一个字段值来表示”,简单且减少内存);活动的档次即子活动,每个档次子活动对应的奖励(JSON格式:{"paramArray":["12-14","18-20","21-24"],"awardArray":[26001,26001,26001]}paramArray的长度表示该活动有多少档子活动,每个数组元素值表示该档次子活动的一些设置条件如12-14表示该活动每天下午12:00至14:00开启等等它的值可以任何形式,只要对应解析出来使用即可;awardArray则与前面paramArray对应,表示相应档次子活动的奖励礼包的ID);该活动开放的渠道(数组格式:[baidu,tencent,360] 表示该活动只在发布到百度、腾讯以及360的包启用)。针对配置表中这些特殊数据格式字段的解析与使用,需要写特殊的API来解析与使用。
(1)时间戳格式:2017-01-18 00:00:00 读取解析时为字符串,当需要用来比较时间大小时,可以通过API System.DateTime.Parse(string timeStamp)转成System.DateTime再根据相关DataTime比较时间大小的API进行比较。
(2)Bit位格式:值得注意的是,int型占4个字节共32个Bit位可使用,因此,int型数值最多只能够“联合”32个bool类型的“字段”。读取解析时为int型,而使用时,需要获得特定Bit位的值0或1时,需要写这样的工具类来处理。
举例说明:
创建一个解析int型值得Bit位值的脚本BigFlag.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | using UnityEngine; usingSystem.Collections; /* * 处理BitFlag位 */ public class BitFlag{ /// /// 待处理的目标值 /// private int targetValue; /// /// 存储目标值得所有Bit位的值的数组 /// private BitArray statusArray = new BitArray( new int [1]); /// /// 以数组的形式保存targetValue /// private int [] nowStatus = new int [1]; /// /// 构造函数 /// /// public BitFlag( int targetValue) { this .targetValue = targetValue; } /// /// 设定特定Bit位的值 /// ///第XXX个Bit位,值从0开始 ///该Bit被设置的目标值 public void SetSpecificBitValue( int index, bool value) { statusArray.Set(index, value); targetValue = GetIntFromBitArray(); } public int GetIntFromBitArray() { statusArray.CopyTo(nowStatus, 0); return nowStatus[0]; } /// /// 获取特定Bit位的值 /// ///第XXX个Bit位,值从0开始 /// public bool GetSpecificBitValue( int index) { if (nowStatus[0] != targetValue) { nowStatus[0] = targetValue; statusArray = new BitArray(nowStatus); } return statusArray.Get(index); } } |
用的时候,只要new一个BitFlag对象,并调用相应的API即可。另外,关于Bit位的相关知识,读者可参考http://blog.csdn.net/soonsnipe/article/details/4260039 这篇文章。
(3)JSON格式:{"paramArray":["12-14","18-20","21-24"],"awardArray":[26001,26001,26001]}读取解析时,需要写特定API解析成List;使用时,直接用List即可。
举例说明:
JSON格式数据解析需要借助第三方工具脚本SimpleJson.cs封装出一个API即private void ParseJSONData(ref List jsonInfoList,string jsonStr)供外界使用。SimpleJson工具已压缩上传至附件,解压导入工程即可使用。
接着进一步封装出的解析JSON数据的API如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | /// /// 解析出JSON格式中的数据到List中 /// ///存储JSON数据的List ///JSON数据(字符串) private void ParseJSONData( ref List jsonInfoList, stringjsonStr) { JsonObject jsonObj = (JsonObject)SimpleJson.SimpleJson.DeserializeObject(jsonStr); if (jsonObj.ContainsKey( "paramArray" ) &&jsonObj.ContainsKey( "awardArray" )) { JsonArray jsonArray1 = jsonObj[ "paramArray" ] as JsonArray; JsonArray jsonArray2 = jsonObj[ "awardArray" ] as JsonArray; if ((jsonArray1 != null ) && (jsonArray2 != null )) { for ( int i = 0; i < jsonArray1.Count; i++) { JsonInfo paramInfo = new JsonInfo(); paramInfo.Value1= jsonArray1[i].ToString(); paramInfo.Value2 = int .Parse(jsonArray2[i].ToString()); jsonInfoList.Add(paramInfo); } } else { Debug.LogError( "错误:解析成JsonArray出错" ); } } } |
根据上述封装出的API就可以解析JSON数据啦!
(4)数组格式:[baidu,tencent,360] 读取解析时,需要写特定API解析成Array使用时,直接用Array即可。
举例说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /// /// 从数组格式([1,2,3]或{1,2,3})中解析出数组元素至Array中 /// ///用来存储解析出来的数组元素的数组 ///待解析的数组格式的数据 private void GetValuesFromArray( ref string [] values, string arrayStr) { arrayStr = arrayStr.Substring(1); //剔除首字母 arrayStr = arrayStr.Substring(0, arrayStr.Length - 1); //剔除尾字母 values = arrayStr.Split( ',' ); //获得各个数组元素 } |
总结:本篇文章只是介绍了游戏项目中常用配置表文件格式TXT与XML使用方法,另外介绍一些特殊字段的特殊格式的数据的解析与使用方法。下篇文章,我会进一步介绍一个游戏项目中关于所有配置表的读取解析的控制管理器实现(包含策划如何填表与使用的工具;程序如何编写一个通用的配置表读取解析的工具等内容)。本文若有不足之处,请指正!