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

本文介绍了在Unity中构建一个简单的XML数据管理架构,以提高游戏性能和资源利用效率。作者通过创建一个读取XML的父类,利用Dictionary存储处理后的数据,避免重复读取XML。文章详细讨论了实现过程中的常见问题,如变量作用域、数据覆盖等,并提供了代码示例,帮助初学者理解和避免这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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



绝大部分有点规模的游戏都需要读取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里输出的值就是“伊沢ライオン”啦!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值