天天看點

SRPG遊戲開發(二十一)第六章 基本架構 - 六 配置檔案(Config Files)第六章 基本架構(Framework)

傳回總目錄

第六章 基本架構(Framework)

  • 第六章 基本架構(Framework)
    • 六 配置檔案(Config Files)
      • 1 設定(Setting)
      • 2 管理配置檔案
      • 3 讀取配置檔案(Load Config)
      • 4 添加一些Config抽象

六 配置檔案(Config Files)

配置檔案的讀取,就是要讀取文本,或者反序列化。

而配置檔案的類型也是多種多樣,比如xml、txt、csv、json、protobuf等。

當然,讀取它們幾乎是相同的工作。

我們這裡建立所有配置檔案的基類

ConfigFile

namespace DR.Book.SRPG_Dev.Framework
{
    /// <summary>
    /// 所有Config的基類。
    /// 參考其它繼承它的類,可以添加任何類型檔案。
    /// 比如protobuf,csv,excel等檔案。
    /// </summary>
    public abstract class ConfigFile
    {
        // TODO
    }
}
           

1 設定(Setting)

讀取方式,我們也設定成可設定的,那麼就需要一個辨別。

/// <summary>
        /// 檔案讀取方式
        /// </summary>
        [Flags]
        public enum LoadType
        {
            /// <summary>
            /// Unity Resources檔案夾
            /// </summary>
            Resources = ,

            /// <summary>
            /// 本地WWW
            /// </summary>
            WWW = ,

            /// <summary>
            /// 需要與其它方式配合,不能單獨使用
            /// </summary>
            AssetBundle = ,

            /// <summary>
            /// 網絡WWW
            /// </summary>
            WWWInternet = ,
        }
           

而無論在本地還是網絡,都需要一個路徑。而我們準備一個相對路徑。而且還要注意,比如Resources讀取是不需要擴充名的。有些檔案可以直接讀取text,但我們決定讀取

byte[]

,這是由于某些二進制檔案還是需要這樣我們自己解析。

protected struct Info
        {
            /// <summary>
            /// 相對路徑,不要以'/'或'\'開始。
            /// 如果LoadType包含AssetBundle,這裡指AssetBundle相對路徑。
            /// </summary>
            public string relative { get; set; }

            /// <summary>
            /// 名稱與擴充名,Resources與AssetBundle不需要擴充名。
            /// 如果LoadType包含AssetBundle,這裡指AssetBundle名字。
            /// Resources支援的文本類型,請檢視官方文檔。
            /// </summary>
            public string name { get; set; }

            /// <summary>
            /// 讀取方式
            /// </summary>
            public LoadType loadType { get; set; }

            /// <summary>
            /// 如果LoadType包含AssetBundle,AssetBundle内的路徑
            /// </summary>
            public string pathInAssetBundle { get; set; }

            /// <summary>
            /// 擷取相對路徑+檔案名稱+擴充名
            /// </summary>
            public string relativePath
            {
                get { return Path.Combine(relative, name); }
            }
        }

        private Info m_Info;

        protected ConfigFile()
        {
            ConstructInfo(ref m_Info);
        }

        /// <summary>
        /// 初始化Info構造器
        /// </summary>
        /// <param name="info"></param>
        protected abstract void ConstructInfo(ref Info info);
           

這樣,我們繼承它之後,可以修改info的資訊,在真正讀取它的時候,每個配置檔案都分别讀取。

當然讀取

byte[]

之後,還要進行格式化。否則不會填充我們的類。

/// <summary>
        /// Format資料
        /// </summary>
        /// <param name="type"></param>
        /// <param name="bytes"></param>
        /// <param name="config"></param>
        protected abstract void Format(Type type, byte[] bytes, ref ConfigFile config);
           

繼承它的類,必須重寫這個方法,這樣每種類型配置檔案,分别重寫不同的格式化方法。

2 管理配置檔案

這些配置檔案的存儲,這裡直接使用了一個靜态字典存儲。讀取方法放在另一個類裡。

當然你也可以删除它,自己建立一個Manager,或者為Config類單獨寫一個特殊的單例,或其他方法管理它們。

/// <summary>
        /// 儲存所有Config檔案
        /// </summary>
        private static readonly Dictionary<Type, ConfigFile> s_ConfigDict = new Dictionary<Type, ConfigFile>();

        /// <summary>
        /// 擷取Config
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T Get<T>() where T : ConfigFile
        {
            Type type = typeof(T);
            ConfigFile config;
            GetInternal(type, out config);
            return config as T;
        }

        /// <summary>
        /// 丢棄Config, 如果要再次使用,需要重新讀取檔案
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public static void Release<T>() where T : ConfigFile
        {
            s_ConfigDict.Remove(typeof(T));
        }

        /// <summary>
        /// 丢棄所有Config,再次使用,需要重新讀取檔案
        /// </summary>
        public static void ReleaseAll()
        {
            s_ConfigDict.Clear();
        }
           

其中

GetInternal

方法是擷取配置檔案的實作。

裡面隻是簡單的對字典的操作,如果字典中沒有就從

ConfigLoader

中讀取并格式化資料,并添加進字典。

ConfigLoader

是我們建立的專門用于讀取

byte[]

的類。

3 讀取配置檔案(Load Config)

讀取配置檔案的

byte[]

方法,放在一個單獨的類中

ConfigLoader

namespace DR.Book.SRPG_Dev.Framework
{
    /// <summary>
    /// 讀取Config的方法。
    /// </summary>
    public static class ConfigLoader
    {
        // TODO
    }
}
           

我們将根據讀取方式去讀取檔案的

byte[]

根據讀取方式,讀取檔案如下:

  1. Resources
byte[] bytes = Resources.Load<TextAsset>(path).bytes;

// //如果是從AssetBundle讀取。
//AssetBundle bundle = Resources.Load<AssetBundle>(path);
//byte[] bytes = bundle.LoadAsset<TextAsset>(pathInAssetBundle).bytes;
//bundle.Unload(false);
           
  1. WWW
byte[] bytes;
using(WWW www = new WWW(path))
{
    while (!www.isDone)
    {
    }

    if(!string.IsNullOrEmpty(www.error))
    {
        Debug.LogErrorFormat(
            "WWW: {0}, Path: {1}.", 
            www.error, 
            path
            );
        www.Dispose();
        return null;
    }

    bytes = www.bytes;

    // //如果是從AssetBundle讀取
    //AssetBundle bundle = www.assetBundle;
    //bytes = bundle.LoadAsset<TextAsset>(pathInAssetBundle).bytes;
    //bundle.Unload(false);
}
           
  • 在使用Resources和AssetBundle時,要注意檔案格式,并不是所有格式都支援。
  • 在使用WWW時的字首,網絡要有

    http://

    ,而安卓包内要加

    jar:file://

    ,其他要加

    file://

    。具體檢視Unity文檔。
  • 在網絡讀取中,需要修改源碼,最好使用異步和逾時來控制。
  • 根據平台,本地也可以用System.IO流讀取。

4 添加一些Config抽象

我們來添加一些常見的Config類型。它們的差別不大。

首先是Json:

namespace DR.Book.SRPG_Dev.Framework
{
    public abstract class JsonConfigFile : ConfigFile
    {
        protected override void ConstructInfo(ref Info info)
        {
            info.relative = "Json/";
            info.name = "JsonConfig.json";
            info.loadType = LoadType.WWW;
            info.pathInAssetBundle = null;
        }

        protected sealed override void Format(Type type, byte[] bytes, ref ConfigFile config)
        {
            string json = Encoding.UTF8.GetString(bytes).Trim();
            FormatBuffer(json); 
        }

        /// <summary>
        /// Bytes轉換成json後執行
        /// </summary>
        /// <param name="buffer"></param>
        protected virtual void FormatBuffer(string buffer)
        {
            JsonUtility.FromJsonOverwrite(buffer, this);
        }
    }
}
           

其次是Xml:

namespace DR.Book.SRPG_Dev.Framework
{
    public abstract class XmlConfigFile : ConfigFile
    {
        protected override void ConstructInfo(ref Info info)
        {
            info.relative = "Xml/";
            info.name = "XmlConfig.xml";
            info.loadType = LoadType.WWW;
            info.pathInAssetBundle = null;
        }

        protected sealed override void Format(Type type, byte[] bytes, ref ConfigFile config)
        {
            XmlConfigFile buffer;
            using (MemoryStream ms = new MemoryStream(bytes))
            {
                XmlSerializer xs = new XmlSerializer(type);
                buffer = xs.Deserialize(ms) as XmlConfigFile;
            }

            if (buffer != null)
            {
                config = FormatBuffer(buffer);
            }
        }

        /// <summary>
        /// 反序列化完成後執行
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        protected abstract XmlConfigFile FormatBuffer(XmlConfigFile buffer);
    }
}

namespace DR.Book.SRPG_Dev.Framework
{
    public abstract class XmlDocConfigFile : ConfigFile
    {
        protected override void ConstructInfo(ref Info info)
        {
            info.relative = "Xml/";
            info.name = "XmlDocConfig.xml";
            info.loadType = LoadType.WWW;
            info.pathInAssetBundle = null;
        }

        protected sealed override void Format(Type type, byte[] bytes, ref ConfigFile config)
        {
            string xml = Encoding.UTF8.GetString(bytes).Trim();
            XmlDocument buffer = new XmlDocument();
            buffer.LoadXml(xml);
            FormatBuffer(buffer);
        }

        /// <summary>
        /// Bytes轉換成XmlDocument後執行
        /// </summary>
        /// <param name="buffer"></param>
        protected abstract void FormatBuffer(XmlDocument buffer);
    }
}
           

最後是Txt:

namespace DR.Book.SRPG_Dev.Framework
{
    public abstract class TxtConfigFile : ConfigFile
    {
        protected override void ConstructInfo(ref Info info)
        {
            info.relative = "Txt/";
            info.name = "TxtConfig.txt";
            info.loadType = LoadType.WWW;
            info.pathInAssetBundle = null;
        }

        protected sealed override void Format(Type type, byte[] bytes, ref ConfigFile config)
        {
            string text = Encoding.UTF8.GetString(bytes).Trim();
            FormatBuffer(text);
        }

        /// <summary>
        /// Bytes轉換成text後執行
        /// </summary>
        /// <param name="buffer"></param>
        protected abstract void FormatBuffer(string buffer);
    }
}
           

你可以參考這些自己寫其他類型,比如protobuf,csv等。

繼續閱讀