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

Unity构建读取XML简单架构的方法与注意事项

2016-03-26 00:02 627 查看
讲道理这是本宅第一次写原创技术文章,且文中全部内容均为本人拙劣的技术方法实现,若有各种疑问和错误,欢迎及时指正。

绝大部分有点规模的游戏都需要读取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里输出的值就是“伊沢ライオン”啦!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: