Unity遊戲框架設計之UI管理器

珂霖發表於2024-05-01

Unity遊戲框架設計之UI管理器

簡單介紹

在遊戲開發過程中,我們通常需要管理 UI 遊戲物件的載入、開啟和銷燬等操作,同時也需要管理遊戲資料和遊戲資料在 UI 上的顯示,因此我們需要一個 UI 管理器來統一實現上述基礎功能。

我們可以基於 MVC 模式編寫 UI 管理器,此時 UI 管理器將分為模型管理器(Model)、檢視管理器(View)和控制管理器(Controller)。

UI模型管理器

簡單介紹

UI 模型管理器主要管理 UI 上資料的設定和讀取。為了將資料顯示到 UI 上,則必須選定一個 UI 元件作為載體。對於不同的 UI 元件,則有不同的設定和讀取方法。因此引入 UIModelType 類來區分不同的 UI 元件。

下述程式碼以 UI 元件中的 TextMeshProUGUI 元件為例,分別編寫對應的 UI 元件的模型管理器。然後編寫 UI 模型管理器來統一管理所有 UI 元件中的模型,並以 UIModelType 作為區分不同 UI 的識別符號。

程式碼設計

public enum UIModelType
{
    TextMeshProUGUI
}
public class UITextMeshProGUIModelManager : SingletonMono<UITextMeshProGUIModelManager>
{
    public string GetTextModel(string sceneAssetPath, string uiName, string gameObjectName)
    {
        return UIManager.Instance.FindUIComponentByName<TextMeshProUGUI>(sceneAssetPath, uiName, gameObjectName).text;
    }

    public void SetTextModel(string sceneAssetPath, string uiName, string gameObjectName, string text)
    {
        UIManager.Instance.FindUIComponentByName<TextMeshProUGUI>(sceneAssetPath, uiName, gameObjectName).text = text;
    }
}
public class UIModelManager : SingletonMono<UIModelManager>
{
    protected override void Awake()
    {
        base.Awake();
        gameObject.AddComponent<UITextMeshProGUIModelManager>();
    }

    public string GetModel(string sceneAssetPath, string uiName, string gameObjectName, UIModelType type)
    {
        switch (type)
        {
            case UIModelType.TextMeshProGUI:
                return UITextMeshProGUIModelManager.Instance.GetTextModel(sceneAssetPath, uiName, gameObjectName);
            default:
                throw new ArgumentOutOfRangeException();
        }
    }

    public void SetModel(string sceneAssetPath, string uiName, string gameObjectName, string value, UIModelType type)
    {
        switch (type)
        {
            case UIModelType.TextMeshProGUI:
                UITextMeshProGUIModelManager.Instance.SetTextModel(sceneAssetPath, uiName, gameObjectName, value);
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
}

UI檢視管理器

簡單介紹

UI 檢視管理器主要管理 UI 的資源載入、UI 的開啟、UI 的關閉、UI 的銷燬。UI 檢視管理器依賴於場景管理器(不是 Unity 官方 API 而是自己實現的遊戲框架中的場景管理器),因為 UI 檢視管理器的操作與場景管理器的操作類似。UI 的資源載入,實際上就是將資源載入到場景中。UI 的開啟,實際上就是場景中游戲物件的啟用。UI 的關閉,實際上就是場景中游戲物件的禁用。UI 的銷燬,實際上就是場景中游戲物件的銷燬。

程式碼設計

public class UIViewManager : SingletonMono<UIViewManager>
{
    public GameObject LoadUI(string sceneAssetPath, string uiAssetPath, string uiName)
    {
        return SceneManager.Instance.LoadRootPrefab(sceneAssetPath, uiAssetPath, uiName);
    }

    public (GameObject, T) LoadUI<T>(string sceneAssetPath, string uiAssetPath, string uiName) where T : Component
    {
        GameObject prefab = SceneManager.Instance.LoadRootPrefab(sceneAssetPath, uiAssetPath, uiName);
        T component = AddUIComponent<T>(sceneAssetPath);
        return (prefab, component);
    }

    public GameObject OpenUI(string sceneAssetPath, string uiName)
    {
        return SceneManager.Instance.ActiveRootPrefab(sceneAssetPath, uiName);
    }

    public GameObject LoadAndOpenUI(string sceneAssetPath, string uiAssetPath, string uiName)
    {
        return SceneManager.Instance.LoadAndActiveRootPrefab(sceneAssetPath, uiAssetPath, uiName);
    }

    public (GameObject, T) LoadAndOpenUI<T>(string sceneAssetPath, string uiAssetPath, string uiName) where T : Component
    {
        GameObject prefab = LoadAndOpenUI(sceneAssetPath, uiAssetPath, uiName);
        T component = AddUIComponent<T>(sceneAssetPath);
        return (prefab, component);
    }

    public void CloseUI(string sceneAssetPath, string uiName)
    {
        SceneManager.Instance.InactiveRootPrefab(sceneAssetPath, uiName);
    }

    public void CloseAndDestroyUI(string sceneAssetPath, string uiName)
    {
        SceneManager.Instance.InactiveAndDestroyRootPrefab(sceneAssetPath, uiName);
    }

    private T AddUIComponent<T>(string sceneAssetPath) where T : Component
    {
        return SceneManager.Instance.AddRootGameObjectComponent<T>(sceneAssetPath);
    }
}

UI控制管理器

簡單介紹

UI 控制管理器主要用於控制 UI 事件的處理。首先我們透過 UI 控制管理器向 UI 新增 UI 事件的監聽器,然後當 UI 事件觸發時,將自動執行監聽器中的程式碼來完成對 UI 事件的處理。下面以 Button 元件的事件監聽器的繫結作為例子,編寫 UI 控制管理器。

程式碼設計

public class UIControllerManager : SingletonMono<UIControllerManager>
{
    public void AddListenerOnButtonClick(string sceneAssetPath, string uiName, string gameObjectName, Action onButtonClick)
    {
        UIManager.Instance.FindUIComponentByName<Button>(sceneAssetPath, uiName, gameObjectName).onClick.AddListener(() => onButtonClick());
    }
}

程式碼說明

(一)使用 UI 控制管理器時,必須在場景中新增 EventSystem 元件,否則 UI 事件將不會觸發。

(二)允許重複新增事件監聽器。

(三)向 UI 新增 UI 事件的監聽器後,不需要主動在 UI 被禁用或者被銷燬時刪除 UI 事件的監聽器,監聽的刪除操作由 Unity 自動完成。

程式碼設計

在 UI 管理器中,提供 UI 模型管理器、UI 檢視管理器和 UI 控制管理器的方法入口,並提供一些公用方法。

public class UIManager : SingletonMono<UIManager>
{
    protected override void Awake()
    {
        base.Awake();
        // UIViewManager
        gameObject.AddComponent<UIViewManager>();
        // UIControllerManager
        gameObject.AddComponent<UIControllerManager>();
        // UIModelManager
        gameObject.AddComponent<UIModelManager>();
    }
    
    public void SetModel(string sceneAssetPath, string uiName, string gameObjectName, string value, UIModelType type)
    {
        UIModelManager.Instance.SetModel(sceneAssetPath, uiName, gameObjectName, value, type);
    }

    public string GetModel(string sceneAssetPath, string uiName, string gameObjectName, UIModelType type)
    {
        return UIModelManager.Instance.GetModel(sceneAssetPath, uiName, gameObjectName, type);
    }

    public GameObject LoadUI(string sceneAssetPath, string uiAssetPath, string uiName)
    {
        return UIViewManager.Instance.LoadUI(sceneAssetPath, uiAssetPath, uiName);
    }

    public (GameObject, T) LoadUI<T>(string sceneAssetPath, string uiAssetPath, string uiName) where T : Component
    {
        return UIViewManager.Instance.LoadUI<T>(sceneAssetPath, uiAssetPath, uiName);
    }

    public GameObject OpenUI(string sceneAssetPath, string uiName)
    {
        return UIViewManager.Instance.OpenUI(sceneAssetPath, uiName);
    }

    public GameObject LoadAndOpenUI(string sceneAssetPath, string uiAssetPath, string uiName)
    {
        return UIViewManager.Instance.LoadAndOpenUI(sceneAssetPath, uiAssetPath, uiName);
    }

    public (GameObject, T) LoadAndOpenUI<T>(string sceneAssetPath, string uiAssetPath, string uiName) where T : Component
    {
        return UIViewManager.Instance.LoadAndOpenUI<T>(sceneAssetPath, uiAssetPath, uiName);
    }

    public void CloseUI(string sceneAssetPath, string uiName)
    {
        UIViewManager.Instance.CloseUI(sceneAssetPath, uiName);
    }

    public void CloseAndDestroyUI(string sceneAssetPath, string uiName)
    {
        UIViewManager.Instance.CloseAndDestroyUI(sceneAssetPath, uiName);
    }

	public void AddListenerOnButtonClick(string sceneAssetPath, string uiName, string gameObjectName, Action onButtonClick)
    {
        UIControllerManager.Instance.AddListenerOnButtonClick(sceneAssetPath, uiName, gameObjectName, onButtonClick);
    }

    public T FindUIComponentByName<T>(string sceneAssetPath, string uiName, string gameObjectName) where T : Component
    {
        return SceneManager.Instance.FindRootPrefabDescendantComponent<T>(sceneAssetPath, uiName, gameObjectName);
    }

    public GameObject FindUIGameObjectByName(string sceneAssetPath, string rootGameObjectName, string gameObjectName)
    {
        return SceneManager.Instance.FindRootPrefabDescendant(sceneAssetPath, rootGameObjectName, gameObjectName);
    }

    public string GetDefaultUIName(string uiAssetPath)
    {
        return SceneManager.Instance.GetDefaultPrefabName(uiAssetPath);
    }
}

後記

由於個人能力有限,文中不免存在疏漏之處,懇求大家斧正,一起交流,共同進步。

相關文章