天天看點

Gameframework架構思路

前言

俗話說得好不想懂底層代碼的程式猿,不是好程式猿(那裡來的俗話,我也不知道...),Unity引擎如何搭建的,不懂倒是沒有關系,畢竟代碼沒有開源(就算開源了,也不會去讀的,畢竟在下智商有限),但是GF代碼是E大設計基于Unity架構,用着封裝好的十八大金剛,敲着簡單的代碼,良心不會痛嘛,而且萬一想要往架構封裝新子產品,@作者去添加子產品也不太好,我們也不是這樣的人,是以看下作者設計架構時的簡單思路。
  • 如何下手?

接觸GF架構差不多一個月了,本來準備拿它馬上練手的,嘗試去寫個小遊戲,但是突然思考良久,不行!作為優秀的程式猿,連架構代碼原理都不了解,這麼可以如此匆忙的下手(手裡已經拿着省力杠杆,但還是用手去翹石頭,這樣也不太好...),是以準備把每個子產品仔仔細細了解清楚,然後把搭建GF架構的思路也明明白白給大腦安排一遍,那個時候再去使用省力杠杆,豈不是甚好?知識都是高濃度轉向低濃度的,是以隻要把腦袋緊貼着電腦螢幕就可以把知識轉移到腦子裡。在學習GF架構盡量要做到一個目标:你的架構就是我的架構,我的架構還是我的架構。

Gameframework架構思路

文字水了這麼多了,現在應該讨論一下架構應該如何下手學習?當場捕獲兩個腳本(GameFrameworkEntry和GameEntry),各位可以先從GameFrameworkEntry腳本,畢竟作者把它備注成遊戲架構入口,看看遊戲架構入口到底寫了什麼鬼?具體代碼如下:

using System;
using System.Collections.Generic;

namespace GameFramework
{
    /// <summary>
    /// 遊戲架構入口。
    /// </summary>
    public static class GameFrameworkEntry
    {
        private static readonly GameFrameworkLinkedList<GameFrameworkModule> s_GameFrameworkModules = new GameFrameworkLinkedList<GameFrameworkModule>();

        /// <summary>
        /// 所有遊戲架構子產品輪詢。
        /// </summary>
        /// <param name="elapseSeconds">邏輯流逝時間,以秒為機關。</param>
        /// <param name="realElapseSeconds">真實流逝時間,以秒為機關。</param>
        public static void Update(float elapseSeconds, float realElapseSeconds)
        {
            foreach (GameFrameworkModule module in s_GameFrameworkModules)
            {
                module.Update(elapseSeconds, realElapseSeconds);
            }
        }

        /// <summary>
        /// 關閉并清理所有遊戲架構子產品。
        /// </summary>
        public static void Shutdown()
        {
            for (LinkedListNode<GameFrameworkModule> current = s_GameFrameworkModules.Last; current != null; current = current.Previous)
            {
                current.Value.Shutdown();
            }

            s_GameFrameworkModules.Clear();
            ReferencePool.ClearAll();
            GameFrameworkLog.SetLogHelper(null);
        }

        /// <summary>
        /// 擷取遊戲架構子產品。
        /// </summary>
        /// <typeparam name="T">要擷取的遊戲架構子產品類型。</typeparam>
        /// <returns>要擷取的遊戲架構子產品。</returns>
        /// <remarks>如果要擷取的遊戲架構子產品不存在,則自動建立該遊戲架構子產品。</remarks>
        public static T GetModule<T>() where T : class
        {
            Type interfaceType = typeof(T);
            if (!interfaceType.IsInterface)
            {
                throw new GameFrameworkException(Utility.Text.Format("You must get module by interface, but '{0}' is not.", interfaceType.FullName));
            }

            if (!interfaceType.FullName.StartsWith("GameFramework."))
            {
                throw new GameFrameworkException(Utility.Text.Format("You must get a Game Framework module, but '{0}' is not.", interfaceType.FullName));
            }

            string moduleName = Utility.Text.Format("{0}.{1}", interfaceType.Namespace, interfaceType.Name.Substring(1));
            Type moduleType = Type.GetType(moduleName);
            if (moduleType == null)
            {
                throw new GameFrameworkException(Utility.Text.Format("Can not find Game Framework module type '{0}'.", moduleName));
            }

            return GetModule(moduleType) as T;
        }

        /// <summary>
        /// 擷取遊戲架構子產品。
        /// </summary>
        /// <param name="moduleType">要擷取的遊戲架構子產品類型。</param>
        /// <returns>要擷取的遊戲架構子產品。</returns>
        /// <remarks>如果要擷取的遊戲架構子產品不存在,則自動建立該遊戲架構子產品。</remarks>
        private static GameFrameworkModule GetModule(Type moduleType)
        {
            foreach (GameFrameworkModule module in s_GameFrameworkModules)
            {
                if (module.GetType() == moduleType)
                {
                    return module;
                }
            }

            return CreateModule(moduleType);
        }

        /// <summary>
        /// 建立遊戲架構子產品。
        /// </summary>
        /// <param name="moduleType">要建立的遊戲架構子產品類型。</param>
        /// <returns>要建立的遊戲架構子產品。</returns>
        private static GameFrameworkModule CreateModule(Type moduleType)
        {
            GameFrameworkModule module = (GameFrameworkModule)Activator.CreateInstance(moduleType);
            if (module == null)
            {
                throw new GameFrameworkException(Utility.Text.Format("Can not create module '{0}'.", moduleType.FullName));
            }

            LinkedListNode<GameFrameworkModule> current = s_GameFrameworkModules.First;
            while (current != null)
            {
                if (module.Priority > current.Value.Priority)
                {
                    break;
                }

                current = current.Next;
            }

            if (current != null)
            {
                s_GameFrameworkModules.AddBefore(current, module);
            }
            else
            {
                s_GameFrameworkModules.AddLast(module);
            }

            return module;
        }
    }
}
           

通過腳本可以擷取到需要的管理器,并且也有架構清理釋放函數。還有Update函數可以輪詢每個管理器的更新函數,有沒有發現這個類是靜态的,而且在動态連結庫裡的。是以大膽猜測一定是某個腳本繼承了Mono腳本裡然後去調用更新函數(雖然檢視一下Update函數所有引用就知道了),這樣動态連結庫裡的每個管理器都接入了更新方案。GameFrameworkModule清單是各位在遊戲架構入口類裡可以看到的,是以如果有什麼和遊戲相關的管理器需要擴充時,就必須要繼承GameFrameworkModule才可以,可以看一下架構有哪些類是繼承了子產品抽象類的,具體截圖如下:

Gameframework架構思路

目測十八大金鋼都在,為什麼名字有如此多重複?如果有這種疑問的童靴,可以去看一下partial限定字作用,這樣就可以知道如何做到把類代碼分開實作的。至于為什麼作者把這些管理器腳本分成這麼多份,因為管理器下面有分成一些功能,如果把代碼全部集中在一起太low,而且類代碼可能又長又寬, 導緻看起來很不清晰。像我這種菜鳥以前都是集中起來,慫什麼?幹就完事了。還有一點要知道,如果往架構添加遊戲子產品時,管理器代碼集中寫問題倒是不大。但是添加新子產品時不能引用Unity相關的庫,如果發現添加新子產品時引用了Unity相關庫,在下就錘爆在座各位的狗頭🙄。

Gameframework架構思路

這樣怎麼辦呢?動态連結庫代碼如何做到與Unity中代碼進行對接,其實每個管理器可以有很多個代理類,比如資料總管可以有很多個資源加載代理類,下載下傳管理器可以有很多下載下傳代理類,命名差不多是xxxHelper,通俗的說它們的上司就是這些管理器,各位隻需要在動态連結庫裡拟定一下接口,在Unity中具體實作接口即可,具體是什麼意思?各位參照設定管理器(SettingManager)就知道了,首先設定代理接口代碼如下:

namespace GameFramework.Setting
{
    /// <summary>
    /// 遊戲配置輔助器接口。
    /// </summary>
    public interface ISettingHelper
    {
        /// <summary>
        /// 加載遊戲配置。
        /// </summary>
        /// <returns>是否加載遊戲配置成功。</returns>
        bool Load();

        /// <summary>
        /// 儲存遊戲配置。
        /// </summary>
        /// <returns>是否儲存遊戲配置成功。</returns>
        bool Save();

        /// <summary>
        /// 檢查是否存在指定遊戲配置項。
        /// </summary>
        /// <param name="settingName">要檢查遊戲配置項的名稱。</param>
        /// <returns>指定的遊戲配置項是否存在。</returns>
        bool HasSetting(string settingName);

        /// <summary>
        /// 移除指定遊戲配置項。
        /// </summary>
        /// <param name="settingName">要移除遊戲配置項的名稱。</param>
        void RemoveSetting(string settingName);

        /// <summary>
        /// 清空所有遊戲配置項。
        /// </summary>
        void RemoveAllSettings();

        /// <summary>
        /// 從指定遊戲配置項中讀取布爾值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的布爾值。</returns>
        bool GetBool(string settingName);

        /// <summary>
        /// 從指定遊戲配置項中讀取布爾值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultValue">當指定的遊戲配置項不存在時,傳回此預設值。</param>
        /// <returns>讀取的布爾值。</returns>
        bool GetBool(string settingName, bool defaultValue);

        /// <summary>
        /// 向指定遊戲配置項寫入布爾值。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="value">要寫入的布爾值。</param>
        void SetBool(string settingName, bool value);

        /// <summary>
        /// 從指定遊戲配置項中讀取整數值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的整數值。</returns>
        int GetInt(string settingName);

        /// <summary>
        /// 從指定遊戲配置項中讀取整數值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultValue">當指定的遊戲配置項不存在時,傳回此預設值。</param>
        /// <returns>讀取的整數值。</returns>
        int GetInt(string settingName, int defaultValue);

        /// <summary>
        /// 向指定遊戲配置項寫入整數值。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="value">要寫入的整數值。</param>
        void SetInt(string settingName, int value);

        /// <summary>
        /// 從指定遊戲配置項中讀取浮點數值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的浮點數值。</returns>
        float GetFloat(string settingName);

        /// <summary>
        /// 從指定遊戲配置項中讀取浮點數值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultValue">當指定的遊戲配置項不存在時,傳回此預設值。</param>
        /// <returns>讀取的浮點數值。</returns>
        float GetFloat(string settingName, float defaultValue);

        /// <summary>
        /// 向指定遊戲配置項寫入浮點數值。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="value">要寫入的浮點數值。</param>
        void SetFloat(string settingName, float value);

        /// <summary>
        /// 從指定遊戲配置項中讀取字元串值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的字元串值。</returns>
        string GetString(string settingName);

        /// <summary>
        /// 從指定遊戲配置項中讀取字元串值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultValue">當指定的遊戲配置項不存在時,傳回此預設值。</param>
        /// <returns>讀取的字元串值。</returns>
        string GetString(string settingName, string defaultValue);

        /// <summary>
        /// 向指定遊戲配置項寫入字元串值。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="value">要寫入的字元串值。</param>
        void SetString(string settingName, string value);

        /// <summary>
        /// 從指定遊戲配置項中讀取對象。
        /// </summary>
        /// <typeparam name="T">要讀取對象的類型。</typeparam>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的對象。</returns>
        T GetObject<T>(string settingName);

        /// <summary>
        /// 從指定遊戲配置項中讀取對象。
        /// </summary>
        /// <param name="objectType">要讀取對象的類型。</param>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的對象。</returns>
        object GetObject(Type objectType, string settingName);

        /// <summary>
        /// 從指定遊戲配置項中讀取對象。
        /// </summary>
        /// <typeparam name="T">要讀取對象的類型。</typeparam>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultObj">當指定的遊戲配置項不存在時,傳回此預設對象。</param>
        /// <returns>讀取的對象。</returns>
        T GetObject<T>(string settingName, T defaultObj);

        /// <summary>
        /// 從指定遊戲配置項中讀取對象。
        /// </summary>
        /// <param name="objectType">要讀取對象的類型。</param>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultObj">當指定的遊戲配置項不存在時,傳回此預設對象。</param>
        /// <returns>讀取的對象。</returns>
        object GetObject(Type objectType, string settingName, object defaultObj);

        /// <summary>
        /// 向指定遊戲配置項寫入對象。
        /// </summary>
        /// <typeparam name="T">要寫入對象的類型。</typeparam>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="obj">要寫入的對象。</param>
        void SetObject<T>(string settingName, T obj);

        /// <summary>
        /// 向指定遊戲配置項寫入對象。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="obj">要寫入的對象。</param>
        void SetObject(string settingName, object obj);
    }
}
           

這裡設定代理接口就是在要打包成動态連結庫的解決方案裡拟定的,因為每個管理器都需要持有它們的代理類,是以設定管理器類裡一定有的參數就是ISettingHelper SettingHelper,繼續?看看Unity中如何實作ISettingHelper接口的,部分代碼如下:

public abstract class SettingHelperBase : MonoBehaviour, ISettingHelper
           

是以我們需要添加其他設定方案時(比如将本地設定儲存成byte檔案),繼承接口并且去實作即可,作者這裡預設實作的設定代理類是對Unity的PlayerPrefs的一層封裝,如此去設計GF簡直是天才之作(先把作者吹一波),這樣就可以具體實作功能的插拔,比如有天不想使用PlayerPrefs去實作設定子產品儲存資料,這樣再額外實作一個設定代理類即可。

Gameframework架構思路
  •  各大管理器元件又是幹嘛的?

故事還是從GameEntry腳本開始吧!盯着電腦螢幕半天,感覺這個腳本和上面開始說的那個腳本簡直神似???隻不過腳本在Unity解決方案裡的,多了注冊子產品的函數,用來将管理器元件添加到清單裡,缺少了更新函數(想想也對的,都是在Unity中的解決方案了,還要什麼更新函數?)。因為元件是挂載到Unity實際對象上的,是以GF架構在Awake時直接去注冊了,而動态連結庫裡是擷取管理器時,如果無法找到管理器的話就馬上建立管理器然後儲存到清單裡,還是來看看元件們是如何被注冊吧,具體代碼如下:

namespace UnityGameFramework.Runtime
{
    /// <summary>
    /// 遊戲架構元件抽象類。
    /// </summary>
    public abstract class GameFrameworkComponent : MonoBehaviour
    {
        /// <summary>
        /// 遊戲架構元件初始化。
        /// </summary>
        protected virtual void Awake()
        {
            GameEntry.RegisterComponent(this);
        }
    }
}
           

隻要管理器元件繼承了元件抽象類時,就可以在Awake時注冊到清單裡,當然如果要重寫Awake函數時也是沒有問題的 ,但是要記住在Awake開頭調用寫上以下代碼:

base.Awake();
           

可能各位會說這個就不必多說了,這個不是基礎知識嘛,那個那個...,就随便提一嘴而已,各位接下來就來分析一下各個管理器元件腳本的具體的用處是什麼?動态連結庫裡的管理器總需要有去調用和初始化參數的腳本,這個時候就需要像月老牽線一樣,天下情侶都是一對的(如果你們敢說國外有些地方都可以一夫多妻,我隻能對各位說渣男!),是以一個管理器元件對應上一個管理器,來看看設定管理器元件到底做了什麼事情,具體代碼如下:

//------------------------------------------------------------
// Game Framework
// Copyright © 2013-2020 Jiang Yin. All rights reserved.
// Homepage: https://gameframework.cn/
// Feedback: mailto:[email protected]
//------------------------------------------------------------

using System;

namespace GameFramework.Setting
{
    /// <summary>
    /// 遊戲配置管理器。
    /// </summary>
    internal sealed class SettingManager : GameFrameworkModule, ISettingManager
    {
        private ISettingHelper m_SettingHelper;

        /// <summary>
        /// 初始化遊戲配置管理器的新執行個體。
        /// </summary>
        public SettingManager()
        {
            m_SettingHelper = null;
        }

        /// <summary>
        /// 遊戲配置管理器輪詢。
        /// </summary>
        /// <param name="elapseSeconds">邏輯流逝時間,以秒為機關。</param>
        /// <param name="realElapseSeconds">真實流逝時間,以秒為機關。</param>
        internal override void Update(float elapseSeconds, float realElapseSeconds)
        {
        }

        /// <summary>
        /// 關閉并清理遊戲配置管理器。
        /// </summary>
        internal override void Shutdown()
        {
            Save();
        }

        /// <summary>
        /// 設定遊戲配置輔助器。
        /// </summary>
        /// <param name="settingHelper">遊戲配置輔助器。</param>
        public void SetSettingHelper(ISettingHelper settingHelper)
        {
            if (settingHelper == null)
            {
                throw new GameFrameworkException("Setting helper is invalid.");
            }

            m_SettingHelper = settingHelper;
        }

        /// <summary>
        /// 加載遊戲配置。
        /// </summary>
        /// <returns>是否加載遊戲配置成功。</returns>
        public bool Load()
        {
            return m_SettingHelper.Load();
        }

        /// <summary>
        /// 儲存遊戲配置。
        /// </summary>
        /// <returns>是否儲存遊戲配置成功。</returns>
        public bool Save()
        {
            return m_SettingHelper.Save();
        }

        /// <summary>
        /// 檢查是否存在指定遊戲配置項。
        /// </summary>
        /// <param name="settingName">要檢查遊戲配置項的名稱。</param>
        /// <returns>指定的遊戲配置項是否存在。</returns>
        public bool HasSetting(string settingName)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.HasSetting(settingName);
        }

        /// <summary>
        /// 移除指定遊戲配置項。
        /// </summary>
        /// <param name="settingName">要移除遊戲配置項的名稱。</param>
        public void RemoveSetting(string settingName)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            m_SettingHelper.RemoveSetting(settingName);
        }

        /// <summary>
        /// 清空所有遊戲配置項。
        /// </summary>
        public void RemoveAllSettings()
        {
            m_SettingHelper.RemoveAllSettings();
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取布爾值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的布爾值。</returns>
        public bool GetBool(string settingName)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetBool(settingName);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取布爾值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultValue">當指定的遊戲配置項不存在時,傳回此預設值。</param>
        /// <returns>讀取的布爾值。</returns>
        public bool GetBool(string settingName, bool defaultValue)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetBool(settingName, defaultValue);
        }

        /// <summary>
        /// 向指定遊戲配置項寫入布爾值。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="value">要寫入的布爾值。</param>
        public void SetBool(string settingName, bool value)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            m_SettingHelper.SetBool(settingName, value);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取整數值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的整數值。</returns>
        public int GetInt(string settingName)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetInt(settingName);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取整數值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultValue">當指定的遊戲配置項不存在時,傳回此預設值。</param>
        /// <returns>讀取的整數值。</returns>
        public int GetInt(string settingName, int defaultValue)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetInt(settingName, defaultValue);
        }

        /// <summary>
        /// 向指定遊戲配置項寫入整數值。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="value">要寫入的整數值。</param>
        public void SetInt(string settingName, int value)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            m_SettingHelper.SetInt(settingName, value);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取浮點數值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的浮點數值。</returns>
        public float GetFloat(string settingName)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetFloat(settingName);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取浮點數值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultValue">當指定的遊戲配置項不存在時,傳回此預設值。</param>
        /// <returns>讀取的浮點數值。</returns>
        public float GetFloat(string settingName, float defaultValue)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetFloat(settingName, defaultValue);
        }

        /// <summary>
        /// 向指定遊戲配置項寫入浮點數值。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="value">要寫入的浮點數值。</param>
        public void SetFloat(string settingName, float value)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            m_SettingHelper.SetFloat(settingName, value);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取字元串值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的字元串值。</returns>
        public string GetString(string settingName)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetString(settingName);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取字元串值。
        /// </summary>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultValue">當指定的遊戲配置項不存在時,傳回此預設值。</param>
        /// <returns>讀取的字元串值。</returns>
        public string GetString(string settingName, string defaultValue)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetString(settingName, defaultValue);
        }

        /// <summary>
        /// 向指定遊戲配置項寫入字元串值。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="value">要寫入的字元串值。</param>
        public void SetString(string settingName, string value)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            m_SettingHelper.SetString(settingName, value);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取對象。
        /// </summary>
        /// <typeparam name="T">要讀取對象的類型。</typeparam>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的對象。</returns>
        public T GetObject<T>(string settingName)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetObject<T>(settingName);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取對象。
        /// </summary>
        /// <param name="objectType">要讀取對象的類型。</param>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <returns>讀取的對象。</returns>
        public object GetObject(Type objectType, string settingName)
        {
            if (objectType == null)
            {
                throw new GameFrameworkException("Object type is invalid.");
            }

            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetObject(objectType, settingName);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取對象。
        /// </summary>
        /// <typeparam name="T">要讀取對象的類型。</typeparam>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultObj">當指定的遊戲配置項不存在時,傳回此預設對象。</param>
        /// <returns>讀取的對象。</returns>
        public T GetObject<T>(string settingName, T defaultObj)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetObject(settingName, defaultObj);
        }

        /// <summary>
        /// 從指定遊戲配置項中讀取對象。
        /// </summary>
        /// <param name="objectType">要讀取對象的類型。</param>
        /// <param name="settingName">要擷取遊戲配置項的名稱。</param>
        /// <param name="defaultObj">當指定的遊戲配置項不存在時,傳回此預設對象。</param>
        /// <returns>讀取的對象。</returns>
        public object GetObject(Type objectType, string settingName, object defaultObj)
        {
            if (objectType == null)
            {
                throw new GameFrameworkException("Object type is invalid.");
            }

            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            return m_SettingHelper.GetObject(objectType, settingName, defaultObj);
        }

        /// <summary>
        /// 向指定遊戲配置項寫入對象。
        /// </summary>
        /// <typeparam name="T">要寫入對象的類型。</typeparam>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="obj">要寫入的對象。</param>
        public void SetObject<T>(string settingName, T obj)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            m_SettingHelper.SetObject(settingName, obj);
        }

        /// <summary>
        /// 向指定遊戲配置項寫入對象。
        /// </summary>
        /// <param name="settingName">要寫入遊戲配置項的名稱。</param>
        /// <param name="obj">要寫入的對象。</param>
        public void SetObject(string settingName, object obj)
        {
            if (string.IsNullOrEmpty(settingName))
            {
                throw new GameFrameworkException("Setting name is invalid.");
            }

            m_SettingHelper.SetObject(settingName, obj);
        }
    }
}
           

看來元件做的事情也不少,動态連結庫裡管理器持有代理對象,而管理器元件持有着管理器對象,可能有些人會說為什麼就一定要使用動态連結庫裡去調用,就不能在Unity解決方案裡直接寫嘛?可以是可以,但是主要的代碼寫到動态連結裡,性能方面會更加好,管理器元件持有管理器去調用方法即可。給大家畫個比較簡單的GF架構圖,讓各位更好的了解設計思路。結構思路圖如下:

Gameframework架構思路

這樣需要添加新管理器時,就可以按照這個思路去設計了,武林秘籍已經傾囊傳授給各位了,接下來各位去看看十八子產品的事件是如何觸發起來的,比如設定成功以後需要有一個成功的回調函數。

  • 管理器事件觸發機制

每個管理器都需要有一定回調函數,通知它是否成功的回調,成功或失敗之後總需要幹點什麼事情,在管理器元件裡寥寥無幾的事件代碼,隻需要在Awake裡給它添加到一個多點傳播事件裡即可,以場景管理器元件(SceneComponent)作為例子,具體代碼如下:

m_SceneManager.LoadSceneSuccess += OnLoadSceneSuccess;
            m_SceneManager.LoadSceneFailure += OnLoadSceneFailure;
           

查找所有引用看一下到底那裡調用這個多點傳播事件,先給各位截個圖吧,畢竟這個算是一個轉折點了,諸君請看下圖:

Gameframework架構思路

可以看到事件的回調時放到這個函數裡了,但是具體這個函數在那裡調用呢?讓我們繼續查找一下引用,發現一個事件管理器把所有回調函數全部封裝到一個對象裡了,具體代碼段如下:

m_LoadSceneCallbacks = new LoadSceneCallbacks(LoadSceneSuccessCallback, LoadSceneFailureCallback, LoadSceneUpdateCallback, LoadSceneDependencyAssetCallback);
           

是以需要找到m_LoadSceneCallbacks對象引用即可,最後的最後!!!可以發現的是把對象交給任務池管理器了,添加到任務管理池裡面,大概是放到類似清單的資料結構裡進行輪回,先給各位看一下跳轉到的添加到任務池的代碼段。

public void LoadScene(string sceneAssetName, int priority, LoadSceneCallbacks loadSceneCallbacks, object userData)
            {
                ResourceInfo? resourceInfo = null;
                string[] dependencyAssetNames = null;

                if (!CheckAsset(sceneAssetName, out resourceInfo, out dependencyAssetNames))
                {
                    string errorMessage = Utility.Text.Format("Can not load scene '{0}'.", sceneAssetName);
                    if (loadSceneCallbacks.LoadSceneFailureCallback != null)
                    {
                        loadSceneCallbacks.LoadSceneFailureCallback(sceneAssetName, LoadResourceStatus.NotReady, errorMessage, userData);
                        return;
                    }

                    throw new GameFrameworkException(errorMessage);
                }

                LoadSceneTask mainTask = LoadSceneTask.Create(sceneAssetName, priority, resourceInfo.Value, dependencyAssetNames, loadSceneCallbacks, userData);
                foreach (string dependencyAssetName in dependencyAssetNames)
                {
                    if (!LoadDependencyAsset(dependencyAssetName, priority, mainTask, userData))
                    {
                        string errorMessage = Utility.Text.Format("Can not load dependency asset '{0}' when load scene '{1}'.", dependencyAssetName, sceneAssetName);
                        if (loadSceneCallbacks.LoadSceneFailureCallback != null)
                        {
                            loadSceneCallbacks.LoadSceneFailureCallback(sceneAssetName, LoadResourceStatus.DependencyError, errorMessage, userData);
                            return;
                        }

                        throw new GameFrameworkException(errorMessage);
                    }
                }

                m_TaskPool.AddTask(mainTask);
            }
           

至于TaskPool裡代碼确實是在Update裡執行,任務池不斷的更新,執行任務以後就把任務對象從清單裡移除,給各位看一下任務池的Update裡是如何執行的,調用截圖如下:

Gameframework架構思路

 進入任務隊列以後,實際調用的函數是通過ProcessRunningTasks,這裡又有一個概念就是任務代理類,說起來有點繁瑣,這裡就先簡單的畫龍點睛一下,具體的調用原理可以看一下在下寫的任務池原理分析,首先給各位看看ProcessRunningTasks函數到底做了那些事情吧!具體代碼如下:

private void ProcessRunningTasks(float elapseSeconds, float realElapseSeconds)
        {
            LinkedListNode<ITaskAgent<T>> current = m_WorkingAgents.First;
            while (current != null)
            {
                T task = current.Value.Task;
                if (!task.Done)
                {
                    current.Value.Update(elapseSeconds, realElapseSeconds);
                    current = current.Next;
                    continue;
                }

                LinkedListNode<ITaskAgent<T>> next = current.Next;
                current.Value.Reset();
                m_FreeAgents.Push(current.Value);
                m_WorkingAgents.Remove(current);
                ReferencePool.Release(task);
                current = next;
            }
        }
           

到這裡的current.Value.Update就是調到管理器需要執行的回調函數(m_LoadSceneCallbacks),各位是不是慢慢忘記了标題...好像這麼調着調着,代碼就講到這裡了,不要驚慌!隻需要看過任務池文章,你的思路就會漸漸清楚了(雖然寫這篇文章時,任務池文章還沒有開始寫,哈哈哈哈哈哈)。

Gameframework架構思路

這裡Update函數具體調用代碼給各位看一下,具體是在DefaultLoadResourceAgentHelper裡,如圖所示:

Gameframework架構思路

到這裡算是結束了...還有一點差點忘記了,就是調用半天了,其實回調函數裡隻是調用m_EventComponent.Fire函數,這個函數又是什麼作用?各位可以到以下傳送門:

https://blog.csdn.net/m0_37920739/article/details/104723210