Unity构建读取XML简单架构的方法与注意事项
2016-03-26 00:02
627 查看
讲道理这是本宅第一次写原创技术文章,且文中全部内容均为本人拙劣的技术方法实现,若有各种疑问和错误,欢迎及时指正。
绝大部分有点规模的游戏都需要读取XML,且XML数量并不少,如果每次需要数据时就读一次那就太浪费资源了,而且性能也不好,所以我自己动手简单写了一个管理读取XML简单架构。
大致思路:首先建立一个读取XML的父类,继承的子类即所要读XML的实体类。使用WWW类获取XML路径,使用www.text得到XML内容,经过处理后把所得的东西存入一个Dictionary<Type,Object>中,以后需要用到XML中数据的时候,直接从Dictionary中调数据即可,而不需要重复的读XML了。
思路大致如此,下面贴点代码详细说明一下吧。
这里特别说明一下,我使用的Dictionary是<type,Object>方式存储的,key就是当前要存入字典中value对象的Type,使用typeof(ClassName)或者Object.getType()都可以得到。而value就是DatabaseConfig的对象。
下面还有一个判断是否已有T类型的key,如果有,就把它对应的value(即T类型的对象)返回去,实体类通过操作这个对象就可以获取Player的各种字段值了。
最下面写了一个功能函数,是在实际使用时调用的,就是通过判断id来获取player对象。
由于我个人水平极低,在我写代码的时候遇到如下一些低级问题,仅献给和我一样的初学者们,希望你们少走弯路。
1、GetPlayerByID函数找不到players
这是我第一个遇到的问题,而实际出问题的地方却并非多高端。players是Player类中的一个List,我最开始并没有把Player player1写在Read()函数外,而出了方法体player1找不到了,当然也就不可能在其他位置调用了。
2、存入players的数据为null
3、存入players的数据出错
上面两个问题也是困扰我半宿,都是围绕这个List展开的。我最开始的写法是这样的:tmpplayer1.players.Add(tmpplayer1);Debug时看不出什么问题,但是实际存在players里的东西就不一样了,因为foreach循环体里每次都要new tmpplayer(),代表着每次循环所使用的tmpplayers都不是同一个,可以理解为tmpplayer1、tmpplayer2,对象不是同一个,其players的自然也不是同一个,所以List总是只保存了最后一次存入的数据(因为foreach结束了不再newtmpplayer了),之前写入的东西都被后一个覆盖掉了。
其实上面说这么多,原因就是在Read()函数体中操作的数据是没有被保存的,出了这个函数,如果没有保存或return就再也找不到了,包括players。所以我在函数外声明了一个player对象专门用于保存函数体内操作后的数据,而后还要把这个对象存入Dictionary中,这样数据就不会丢了。
这里使用了streamingAssets文件夹作为存放XML的路径,大家可以搜索streamingAssets有好多介绍。最下面就是用协程加载找到XML并调用Read()方法读取XML内容,上面已经讲过了,最后把这个对象存到Dictionary里。
所以需要加载其他XML的时候,只要写一个继承于DatabaseConfig的子类就可以了,然后把AddConfig<ClassName>放进Start里,在Unity一运行的时候,就把这个XML读好放进Dictionary里了。需要增加或减少XML只需要管理Start里的条目就可以了。虽然整体逻辑我自己想起来也很费脑子,但是以后再增删XML可就省事多了。
最后附一段测试代码
绝大部分有点规模的游戏都需要读取XML,且XML数量并不少,如果每次需要数据时就读一次那就太浪费资源了,而且性能也不好,所以我自己动手简单写了一个管理读取XML简单架构。
大致思路:首先建立一个读取XML的父类,继承的子类即所要读XML的实体类。使用WWW类获取XML路径,使用www.text得到XML内容,经过处理后把所得的东西存入一个Dictionary<Type,Object>中,以后需要用到XML中数据的时候,直接从Dictionary中调数据即可,而不需要重复的读XML了。
思路大致如此,下面贴点代码详细说明一下吧。
<span style="font-size:14px;"><?xml version="1.0" encoding="UTF-8" standalone="yes"?> <PlayerConfig> <Player name="伊沢ライオン" id="1001" HP="100" MP="50" ATK="10" DFN="10" > <skill>Q</skill> <skill>W</skill> <skill>E</skill> <skill>R</skill> </Player> </PlayerConfig></span>↑↑↑↑↑这个是示例XML,我们就以读取这个为例。
<span style="font-size:14px;">using UnityEngine; using System.Collections; using System.Collections.Generic; public class Player { public string name; public int id; public int hp; public int mp; public int atk; public int dfn; public List<Player> players=new List<Player>(); public List<Skill> skill=new List<Skill>(); } public class Skill { public string skillName; }</span>↑↑↑↑↑↑这个是Player类,存数据的
<span style="font-size:14px;">using UnityEngine; using System.Collections; using System.Collections.Generic; public class DatabaseConfig { //判断是否为空 public virtual bool Read(string text) { if (string.IsNullOrEmpty(text)) { return false; } return true; } } public class DefaultConfig { //单例模式 private static DefaultConfig instance; public static DefaultConfig getInstance() { if (instance == null) { instance = new DefaultConfig(); return instance; } else { return instance; } } public Dictionary<System.Type, DatabaseConfig> configDic = new Dictionary<System.Type, DatabaseConfig>(); public T GetConfigByType<T>() where T : DatabaseConfig { //查找字典是否已有T类型的数据源 if (configDic.ContainsKey(typeof(T))) { return configDic[typeof(T)] as T; } return null; } } </span><span style="font-size:18px;"> </span>↑↑↑↑↑↑这段是读取XML的父类和声明Dictionary的部分。其中父类只有一个Read()方法判断是否为空,因为在子类中还要override。
这里特别说明一下,我使用的Dictionary是<type,Object>方式存储的,key就是当前要存入字典中value对象的Type,使用typeof(ClassName)或者Object.getType()都可以得到。而value就是DatabaseConfig的对象。
下面还有一个判断是否已有T类型的key,如果有,就把它对应的value(即T类型的对象)返回去,实体类通过操作这个对象就可以获取Player的各种字段值了。
<span style="font-size:14px;">using UnityEngine; using System.Collections; using System.Xml; using System.Collections.Generic; public class PlayerConfig : DatabaseConfig{ Player player1 = new Player(); public override bool Read(string text){ if (base.Read(text) == false){ return false; } XmlDocument xmldoc = new XmlDocument(); xmldoc.LoadXml(text); XmlElement rootElem = xmldoc.DocumentElement; //读取根节点下面子节点的元素 XmlNodeList xmllist1 = rootElem.ChildNodes; //遍历所有根节点下面子节点的属性 foreach (XmlElement tempel1 in xmllist1){ Player tmpplayer1 = new Player(); tmpplayer1.name = tempel1.GetAttribute("name"); tmpplayer1.id = int.Parse(tempel1.GetAttribute("id")); //将当前Player记录到players链表下 player1.players.Add(tmpplayer1); } return true; }</span>
<span style="font-size:14px;"> #region public Player GetPlayerByID(int id) { if ( id<= 0) { return null; } foreach (var player in player1.players) { if (player.id == id) { return player; } } return null; } #endregion }</span><span style="font-size:18px;"> </span>↑↑↑↑↑↑这个就是继承于父类的一个实际读取XML的子类。中间一大部分都是C#中读取XML的代码,我就不多介绍了,可以搜索C#读取XML了解。我这里只获取了一个id、一个name做测试,其他字段都可以通过相同方法获取。
最下面写了一个功能函数,是在实际使用时调用的,就是通过判断id来获取player对象。
由于我个人水平极低,在我写代码的时候遇到如下一些低级问题,仅献给和我一样的初学者们,希望你们少走弯路。
1、GetPlayerByID函数找不到players
这是我第一个遇到的问题,而实际出问题的地方却并非多高端。players是Player类中的一个List,我最开始并没有把Player player1写在Read()函数外,而出了方法体player1找不到了,当然也就不可能在其他位置调用了。
2、存入players的数据为null
3、存入players的数据出错
上面两个问题也是困扰我半宿,都是围绕这个List展开的。我最开始的写法是这样的:tmpplayer1.players.Add(tmpplayer1);Debug时看不出什么问题,但是实际存在players里的东西就不一样了,因为foreach循环体里每次都要new tmpplayer(),代表着每次循环所使用的tmpplayers都不是同一个,可以理解为tmpplayer1、tmpplayer2,对象不是同一个,其players的自然也不是同一个,所以List总是只保存了最后一次存入的数据(因为foreach结束了不再newtmpplayer了),之前写入的东西都被后一个覆盖掉了。
其实上面说这么多,原因就是在Read()函数体中操作的数据是没有被保存的,出了这个函数,如果没有保存或return就再也找不到了,包括players。所以我在函数外声明了一个player对象专门用于保存函数体内操作后的数据,而后还要把这个对象存入Dictionary中,这样数据就不会丢了。
<span style="font-size:14px;">using UnityEngine; using System.Collections; public class LoadManager : MonoBehaviour { //游戏一开始就加载所有的xml void Start () { //需要添加新的配置表,只需添加addconfig方法,加上读取的xml名即可 addConfig<PlayerConfig>(); // addConfig<XXX>(); } void addConfig<T>() where T : DatabaseConfig, new() { T config = new T(); StartCoroutine(loadXML(config)); // Debug.Log(config); } public string getPlatForm(DatabaseConfig configPath) { string path = Application.streamingAssetsPath+ configPath.ToString() + ".xml; return path; } IEnumerator loadXML(DatabaseConfig tempConfig) { string path = getPlatForm(tempConfig); WWW www = new WWW(path); yield return www; Debug.Log(www.text); tempConfig.Read(www.text); DefaultConfig.getInstance().configDic.Add(tempConfig.GetType(), tempConfig); } }</span>
这里使用了streamingAssets文件夹作为存放XML的路径,大家可以搜索streamingAssets有好多介绍。最下面就是用协程加载找到XML并调用Read()方法读取XML内容,上面已经讲过了,最后把这个对象存到Dictionary里。
所以需要加载其他XML的时候,只要写一个继承于DatabaseConfig的子类就可以了,然后把AddConfig<ClassName>放进Start里,在Unity一运行的时候,就把这个XML读好放进Dictionary里了。需要增加或减少XML只需要管理Start里的条目就可以了。虽然整体逻辑我自己想起来也很费脑子,但是以后再增删XML可就省事多了。
最后附一段测试代码
void testDemo() {
if(<pre name="code" class="csharp" style="font-size:18px;">PlayerConfig playercon= DefaultConfig.getInstance().GetConfigByType<PlayerConfig>()<span style="font-family: Arial, Helvetica, sans-serif;">){</span>
<pre name="code" class="csharp"> Player currentPlayer = playercon.GetPlayerByID(1001); Debug.Log(currentPlayer.name);
}如果Dictionary里有PlayerConfig的key,那么下面就是获取Player的对象,查找ID为1001角色,如果没有错误,最后Debug里输出的值就是“伊沢ライオン”啦!
相关文章推荐
- Thinking in Unity3D:材质系统概览
- Thinking in Unity3D:材质系统概览
- warning: mysql-community-libs-5.7.11-1.el7.x86_64.rpm: Header V3 DSA/SHA1 Signature, key ID 5072e1f5
- unity3D:Unity中的优化技术
- Android Studio 导入unity 项目
- unity3d,声音播放
- unity3d网格(mesh)编程的研究
- Unity3D -- Shader
- unity工具IGamesTools之批量生成帧动画
- Unity中的优化技术
- Unity3D研究院之LZMA压缩文件与解压文件
- Unity 图片序列帧动画的三种方法(个人总结,若有其它方法欢迎交流)
- Xcode7.3编译unity导出工程出现 Error "unknown type name __declspec" after Xcode 7.3 upgrade
- Xcode7.3编译unity导出工程出现no simulator只能选择devices解决办法
- Unity常用脚本函数
- Unity 每次启动都进入默认启动界面
- Unity发布时出现“The type or namespace name `UnityEditor' could not be found.” 错误
- Unity3D内存释放
- Unity3D EditorWindow一些小心得
- Unity 游戏对象消失 enable,destroy与active的区别