Unity構建讀取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裏輸出的值就是“伊沢ライオン”啦!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章