ZMUIL1——UI管理系统
UI管理系统要做的工作
UI管理系统不仅要实现诸如控制UI窗口加载显隐等常见功能,还要实现独立于MonoBehaviour
之外的UI对象生命周期函数
WindowBehaviour
基类
WindowBehaviour
声明窗口的基础属性,如gameObject
、IsVisable
等,并声明生命周期函数,如OnAwake
等
使本框架下的窗口对象在拥有类似于MonoBehaviour
的基础属性和生命周期函数的同时,我们可以自己控制UI窗口的生命周期函数的执行
WindowBase
基类(继承WindowBehaviour
)
将本框架下的UI窗口的部分共同功能抽象到该基类内,后续所有的Window脚本都继承于它
WindowBase
会管理常用的UI控件,窗口的遮罩对象和所有UI控件的父节点对象。
提供窗口初始化方法、显隐窗口方法、显隐遮罩方法,以及常用UI控件的监听事件添加与移除方法等
UIMoudle
整个UI模块的核心,管理场景上的UI摄像机,UI根节点,UI配置文件,以及所有加载出来的UI窗口
对外,UIMoudle
提供窗口的加载,显隐,销毁等接口
在以上功能中,UIModule
会在合适的时机执行WindowBehaviour
的不同的生命周期函数
后续还要在UIModule
实现单遮调节遮罩逻辑,堆栈系统逻辑等
一、WindowBehaviour
WindowBehaviour
是所有Window窗口类的顶层类,为所有Window逻辑类提供基础属性和行为
UI窗口的基类WindowBase
就会继承WindowBehaviour
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using System;using UnityEngine;public abstract class WindowBehaviour { public GameObject gameObject { get ; set ; } public Transform transform { get ; set ; } public Canvas Canvas { get ; set ; } public string Name { get ; set ; } public bool Visible { get ; set ; } public bool PopStack { get ; set ; } public Action<WindowBase> PopStackListener { get ; set ; } public virtual void OnAwake () { } public virtual void OnShow () { } public virtual void OnHide () { } public virtual void OnDestroy () { } public virtual void SetVisible (bool visible ) { } }
其中:
WindowBehaviour
实现的生命周期函数由UIModule
在合适的时机调用
PopStack
及PopStackListener
是堆栈系统会调用的属性
二、WindowBase
WindowBase
是所有Window逻辑类的基类,继承WindowBehaviour
当使用自动化系统为UI窗口对象生成Window逻辑类时,Window逻辑类会自动继承WindowBase
WindowBase
将所有UI窗口的部分共同功能抽象到该基类内,包括:
管理常用的需要监听用户输入的UI控件,并对外提供监听事件管理接口
重写WindowBehaviour
的生命周期函数,在其中执行UI窗口生成和销毁的必要逻辑,例如窗口初始化,执行显隐动画,清除监听事件等
管理遮罩的CanvasGroup
,对外提供显隐遮罩的方法,供UIModule
的遮罩系统调用
管理UI窗口通用显隐动画方法
提供隐藏窗口方法(主要是派生类调用)和设置窗口可见性方法(UIModule
内调用)
1.窗口基本成员
WindowBase
会自行管理常用的UI控件button
、toggle
和 inputField
,UI控件会在添加监听事件时添加到管理列表内
注:调用添加监听事件的语句不需要由开发者编写,它们会在后续的自动化系统里由自动生成的脚本生成,也就是说,我们无需自己将UI控件加入到列表内
WindowBase
还会管理所有窗口的遮罩对象,UI控件父节点对象,以及窗口上自带的CanvasGroup
注:在本框架下,UI窗口对象默认都采用下图的结构:
UIMask是遮罩,UIContent是所有UI控件的父节点,采用这种结构的原因是为了方便后续遮罩系统管理遮罩显隐
本框架还默认窗口对象上挂载CanvasGroup
,它用来替代SetActive
来控制窗口显隐,因此需要管理它,原因在高性能系统里解释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private List<Button> mAllButtonList = new List<Button>(); private List<Toggle> mToggleList = new List<Toggle>(); private List<InputField> mInputList = new List<InputField>(); private CanvasGroup mUIMask; private CanvasGroup mCanvasGroup; protected Transform mUIContent; protected bool mDisableAnim = false ; private void InitializeBaseComponent (){ mCanvasGroup = transform.GetComponent<CanvasGroup>(); mUIMask = transform.Find("UIMask" ).GetComponent<CanvasGroup>(); mUIContent = transform.Find("UIContent" ).transform; }
这里的初始化窗口方法会在基类的OnAwake()
里调用
2.监听事件管理相关
WindowBase
封装了Button
、Toggle
、InputField
这三种常用控件的添加监听事件方法,该方法需要传入控件本身与监听方法
若控件列表内没有这个控件会将控件加入管理列表,然后清空控件原来的监听方法,重新添加传入的监听方法
该方法会在 自动化系统生成的组件数据脚本/组件数据脚本调用,用于初始化UI窗口的控件事件监听(也就是说,我们不需要自己初始化组件监听方法)
WindowBase
还封装了Button
、Toggle
、InputField
这三种常用控件的清除事件监听方法,
该方法可以一次清空所有管理的控件的监听方法,它会在窗口销毁时会调用
三种控件的添加监听方法与清除监听方法的逻辑大同小异
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private List<Button> mAllButtonList = new List<Button>(); private List<Toggle> mToggleList = new List<Toggle>(); private List<InputField> mInputList = new List<InputField>(); public void AddButtonClickListener (Button button, UnityAction action ){ if (button != null ) { if (!mAllButtonList.Contains(button)) { mAllButtonList.Add(button); } button.onClick.RemoveAllListeners(); button.onClick.AddListener(action); } } public void RemoveAllButtonListener (){ foreach (var button in mAllButtonList) { button.onClick.RemoveAllListeners(); } } public void AddToggleClickListener (Toggle toggle, UnityAction<bool , Toggle> action )public void AddInputFieldListener (InputField input, UnityAction<string > onChangeAction, UnityAction<string > endAction )
3.通用显隐动画相关
WindowBase
通过调用DOTween的拓展方法,实现窗口通用的的显示隐藏动画方法,并提供mDisableAnim
变量来决定是否调用这些动画
开发者可以在这两个方法内覆写其他代码来实现不同的效果 ,也可以在派生类里的OnAwake()
里将mDisableAnim
赋值为true
以禁用动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 protected bool mDisableAnim = false ; public void ShowAnimation (){ if (Canvas.sortingOrder > 90 && mDisableAnim == false ) { mUIMask.alpha = 0f ; mUIMask.DOFade(1 , 0.2f ); mUIContent.localScale = Vector3.one * 0.8f ; mUIContent.DOScale(Vector3.one, 0.3f ).SetEase(Ease.OutBack); } } public void HideAnimation (){ if (Canvas.sortingOrder > 90 && mDisableAnim == false ) { mUIContent.DOScale(Vector3.one * 1.1f , 0.2f ).SetEase(Ease.OutBack).OnComplete(() => { UIModule.Instance.HideWindow(Name); }); } else { UIModule.Instance.HideWindow(Name); } }
禁用动画,将mDisableAnim
设为true
即可
1 2 3 4 5 6 7 8 public override void OnAwake (){ dataComponent = gameObject.GetComponent<UserInfoWindowDataComponent>(); dataComponent.InitComponent(this ); mDisableAnim = true ; base .OnAwake(); }
4.生命周期函数的重写
主要是在OnAwake()
里执行初始化方法,OnDestroy()
清除事件监听并释放内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #region 生命周期函数的声明 public override void OnAwake (){ base .OnAwake(); InitializeBaseComponent(); } public override void OnShow (){ base .OnShow(); ShowAnimation(); } public override void OnHide (){ base .OnHide(); } public override void OnDestroy (){ base .OnDestroy(); RemoveAllButtonListener(); RemoveAllToggleListener(); RemoveAllInputListener(); mAllButtonList.Clear(); mToggleList.Clear(); mInputList.Clear(); } #endregion
WindowBase代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 using DG.Tweening;using System.Collections.Generic;using UnityEngine;using UnityEngine.Events;using UnityEngine.UI;public class WindowBase : WindowBehaviour { private List<Button> mAllButtonList = new List<Button>(); private List<Toggle> mToggleList = new List<Toggle>(); private List<InputField> mInputList = new List<InputField>(); private CanvasGroup mUIMask; private CanvasGroup mCanvasGroup; protected Transform mUIContent; protected bool mDisableAnim = false ; private void InitializeBaseComponent () { mCanvasGroup = transform.GetComponent<CanvasGroup>(); mUIMask = transform.Find("UIMask" ).GetComponent<CanvasGroup>(); mUIContent = transform.Find("UIContent" ).transform; } #region 生命周期函数的声明 public override void OnAwake () { base .OnAwake(); InitializeBaseComponent(); } public override void OnShow () { base .OnShow(); ShowAnimation(); } public override void OnHide () { base .OnHide(); } public override void OnDestroy () { base .OnDestroy(); RemoveAllButtonListener(); RemoveAllToggleListener(); RemoveAllInputListener(); mAllButtonList.Clear(); mToggleList.Clear(); mInputList.Clear(); } #endregion #region 动画管理相关 public void ShowAnimation () { if (Canvas.sortingOrder > 90 && mDisableAnim == false ) { mUIMask.alpha = 0f ; mUIMask.DOFade(1 , 0.2f ); mUIContent.localScale = Vector3.one * 0.8f ; mUIContent.DOScale(Vector3.one, 0.3f ).SetEase(Ease.OutBack); } } public void HideAnimation () { if (Canvas.sortingOrder > 90 && mDisableAnim == false ) { mUIContent.DOScale(Vector3.one * 1.1f , 0.2f ).SetEase(Ease.OutBack).OnComplete(() => { UIModule.Instance.HideWindow(Name); }); } else { UIModule.Instance.HideWindow(Name); } } #endregion public void HideWindow () { HideAnimation(); } public override void SetVisible (bool isVisible ) { mCanvasGroup.alpha = isVisible ? 1f : 0f ; mCanvasGroup.blocksRaycasts = isVisible; Visible = isVisible; } public void SetMaskVisible (bool isVisible ) { if (!UISetting.Instance.SINGMASK_SYSTEM) return ; mUIMask.alpha = isVisible ? 1f : 0f ; } #region 事件管理方法 public void AddButtonClickListener (Button button, UnityAction action ) { if (button != null ) { if (!mAllButtonList.Contains(button)) { mAllButtonList.Add(button); } button.onClick.RemoveAllListeners(); button.onClick.AddListener(action); } } public void AddToggleClickListener (Toggle toggle, UnityAction<bool , Toggle> action ) { if (toggle != null ) { if (!mToggleList.Contains(toggle)) { mToggleList.Add(toggle); } toggle.onValueChanged.RemoveAllListeners(); toggle.onValueChanged.AddListener((isOn) => { action?.Invoke(isOn, toggle); }); } } public void AddInputFieldListener (InputField input, UnityAction<string > onChangeAction, UnityAction<string > endAction ) { if (input != null ) { if (!mInputList.Contains(input)) { mInputList.Add(input); } input.onValueChanged.RemoveAllListeners(); input.onEndEdit.RemoveAllListeners(); input.onValueChanged.AddListener(onChangeAction); input.onEndEdit.AddListener(endAction); } } public void RemoveAllButtonListener () { foreach (var button in mAllButtonList) { button.onClick.RemoveAllListeners(); } } public void RemoveAllToggleListener () { foreach (var toggle in mToggleList) { toggle.onValueChanged.RemoveAllListeners(); } } public void RemoveAllInputListener () { foreach (var input in mInputList) { input.onValueChanged.RemoveAllListeners(); input.onEndEdit.RemoveAllListeners(); } } #endregion }
三、UIModule
UIModule
是框架的核心,是所有UI窗口的单例管理器,它管理场景上唯一的UI摄像机,所有窗口。框架的多个系统都会在这里实现
UIModule
要提供的功能包括:
窗口管理:管理场景上所有的Window逻辑类,以窗口类型和窗口名为参数,提供窗口的预加载,弹出,初始化,隐藏,销毁功能
窗口生命函数的执行:当窗口加载、显隐、销毁时,UIModule
需要调用执行该窗口对应的生命周期函数
控制窗口遮罩的显隐:遮罩系统在这里实现,当开启单遮模式时,每次执行窗口显隐逻辑时,都会重新计算哪个窗口的遮罩需要开启,以实现单遮效果
窗口按顺序依次显示:堆栈系统在这里实现,可以设置窗口弹出队列,让窗口依次弹出,期间可以追加窗口或清除队列,打开队列外窗口也不影响显示队列
1.UIModule的基础成员
UIModule
是管理器,因此采用单例模式
UIModule
管理场景上的UI摄像机,窗口父对象UIRoot,
UIModule
还会管理所有加载出来的窗口,以及显示中的窗口
WindowConfig
与自动化系统的自动获取UI预设体加载路径有关
mWindowStack
和mStartPopStackWindowStatus
则与堆栈系统有关
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private static UIModule _instance;public static UIModule Instance { get { if (_instance == null ) { _instance = new UIModule(); } return _instance; } } private Camera mUICamera; private Transform mUIRoot; private WindowConfig mWindowConfig; private Dictionary<string , WindowBase> mAllWindowDic = new Dictionary<string , WindowBase>(); private List<WindowBase> mAllWindowList = new List<WindowBase>(); private List<WindowBase> mVisibleWindowList = new List<WindowBase>(); private Queue<WindowBase> mWindowStack = new Queue<WindowBase>(); private bool mStartPopStackWindowStatus = false ;
2.UIModule的初始化
UIModule
第一次调用时不会初始化,需要自己进行初始化
初始化方法是通过Find
方法获取场景上的摄像机,UIRoot对象,加载并获取所有预设体组件加载路径
mWindowConfig
与mWindowConfig.GeneratorWindowConfig()
与自动化系统的自动获取UI预设体加载路径有关
1 2 3 4 5 6 7 8 9 10 11 12 public void Initialize (){ mUICamera = GameObject.Find("UICamera" ).GetComponent<Camera>(); mUIRoot = GameObject.Find("UIRoot" ).transform; mWindowConfig = Resources.Load<WindowConfig>("WindowConfig" ); #if UNITY_EDITOR mWindowConfig.GeneratorWindowConfig(); #endif }
3.UIModule的UI管理
UIModule
是使用WindowBase
来进行窗口管理的,而不使用WindowBehaviour
来管理,
因为WindowBehaviour
作为最顶层的类,缺乏WindowBase
的派生实现,
WindowBase
内会实现更多通用的功能,例如弹出与消失动画, 遮罩处理等,
使用WindowBase
管理能够更方便的调用WindowBase
接口
1 2 3 4 5 6 7 private Camera mUICamera; private Transform mUIRoot; private WindowConfig mWindowConfig; private Dictionary<string , WindowBase> mAllWindowDic = new Dictionary<string , WindowBase>(); private List<WindowBase> mAllWindowList = new List<WindowBase>(); private List<WindowBase> mVisibleWindowList = new List<WindowBase>();
a) 窗口弹出
弹出窗口会触发窗口的OnShow()
方法,如果是第一次弹出,还会触发OnAwake
方法
弹出窗口让遮罩系统重新计算单遮
UIModule
的窗口弹出实现较繁琐,因此在这里记录,这里使用伪代码阐述思路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 要显示的窗口类 窗口对象 = UIMoudle.Instance.PopUpWindow<要显示的窗口类>(); public T PopUpWindow <T >() : where T : WindowBase, new (){ 窗口名 = 通过传入的T获取类名 if 通过窗口名发现字典内存在窗口 T t = 通过窗口名从字典获取窗口 if t.gameObject不为空 && t不可见 将t加入到可见窗口列表 将t.transform移到同层级(兄弟窗口)的最后一个位置 设置t可见,执行t的OnShow方法 计算开启哪个窗口的遮罩 return t else 报错 else T t = new T() GameObject UI对象 = 通过窗口名从硬盘内加载预制体 if UI对象 != null 初始化t的各个基础属性,将UI对象及其Canvas和UI摄像机关联到t上,执行t的OnAwake方法 将窗口对象移到同层级(兄弟窗口)的最后一个位置 设置t可见,执行t的OnShow方法 初始化t的RectTransform 将t加入到字典与列表内 计算开启哪个窗口的遮罩 return t else 报错 return null }
代码具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #region 弹出窗口相关 public T PopUpWindow <T >() where T : WindowBase, new (){ System.Type type = typeof (T); string windowName = type.Name; WindowBase window = GetWindow(windowName); if (window != null ) { return ShowWindow(windowName) as T; } T t = new T(); return InitializeWindow(t, windowName) as T; } private WindowBase InitializeWindow (WindowBase windowBase, string windowName ){ GameObject newWindowObj = TempLoadWindow(windowName); if (newWindowObj != null ) { windowBase.gameObject = newWindowObj; windowBase.transform = newWindowObj.transform; windowBase.Canvas = newWindowObj.GetComponent<Canvas>(); windowBase.Canvas.worldCamera = mUICamera; windowBase.Name = newWindowObj.name; windowBase.transform.SetAsLastSibling(); windowBase.OnAwake(); windowBase.SetVisible(true ); windowBase.OnShow(); RectTransform rectTrans = newWindowObj.GetComponent<RectTransform>(); rectTrans.anchorMax = Vector2.one; rectTrans.offsetMax = Vector2.zero; rectTrans.offsetMin = Vector2.zero; mAllWindowDic.Add(windowName, windowBase); mAllWindowList.Add(windowBase); mVisibleWindowList.Add(windowBase); SetWindowMaskVisible(); return windowBase; } Debug.LogError("未加载到对应的窗口,窗口名:" + windowName); return null ; } private WindowBase ShowWindow (string windowName ){ WindowBase window = null ; if (mAllWindowDic.ContainsKey(windowName)) { window = mAllWindowDic[windowName]; if (window.gameObject != null && window.Visible == false ) { mVisibleWindowList.Add(window); window.transform.SetAsLastSibling(); window.SetVisible(true ); SetWindowMaskVisible(); window.OnShow(); } return window; } else Debug.LogError(windowName + ":窗口不存在!请调用PopUpWindow进行弹出" ); return null ; } private WindowBase GetWindow (string windowName ){ if (mAllWindowDic.ContainsKey(windowName)) { return mAllWindowDic[windowName]; } return null ; } #endregion
b) 资源加载相关
值得一提的是,在初始化方法内,为了教学方便,临时使用Resources
的方法来加载预设体
实际使用时,可以使用自己的加载框架加载,或者使用更好的加载方案,因此,这里将加载预设体的代码封装为一个临时方法,方便未来替换
这里的mWindowConfig.GetWindowPath(windowName)
是通过继承ScriptableObject
的配置文件获取该窗口UI预设体的加载路径
这部分在自动化系统内讲解
1 2 3 4 5 6 7 8 9 10 11 public GameObject TempLoadWindow (string windowName ){ GameObject window = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>(mWindowConfig.GetWindowPath(windowName)), mUIRoot); window.transform.localScale = Vector3.one; window.transform.localPosition = Vector3.zero; window.transform.rotation = Quaternion.identity; window.name = windowName; return window; }
c) 获取窗口
GetWindow
有两种重载,一种是内部私有方法,参数为窗口名,一个提供给外部,参数为泛型
对外接口只能获取显示中的窗口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private WindowBase GetWindow (string windowName ){ if (mAllWindowDic.ContainsKey(windowName)) { return mAllWindowDic[windowName]; } return null ; } public T GetWindow <T >() where T: WindowBase{ System.Type type = typeof (T); foreach (var window in mVisibleWindowList) { if (window.Name == type.Name) { return (T)window; } } Debug.LogError("该窗口未获取到:" + type.Name); return null ; }
d) 隐藏窗口
HideWindow
有三种重载,内部私有方法的参数为窗口对象,核心逻辑也在这里实现,另外两个提供给外部,参数为泛型或窗口名
隐藏窗口会触发窗口的OnHide()
方法
隐藏窗口让遮罩系统重新计算单遮,如果还是通过堆栈系统弹出的窗口,堆栈系统将会弹出队列中的下一个窗口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #region 隐藏窗口相关 public void HideWindow <T >() where T : WindowBase{ HideWindow(typeof (T).Name); } public void HideWindow (string windowName ){ WindowBase window = GetWindow(windowName); HideWindow(window); } private void HideWindow (WindowBase window ){ if (window != null && window.Visible) { mVisibleWindowList.Remove(window); window.SetVisible(false ); SetWindowMaskVisible(); window.OnHide(); } PopNextStackWindow(window); } #endregion
e) 销毁窗口
DesttoyWindow
有三种重载,内部私有方法的参数为窗口对象,核心逻辑也在这里实现,另外一个提供给外部,参数为泛型
销毁窗口会触发窗口的OnHide()
和OnDestory()
方法
销毁窗口让遮罩系统重新计算单遮,如果还是通过堆栈系统弹出的窗口,堆栈系统将会弹出队列中的下一个窗口
销毁窗口还有一个销毁全部弹窗的方法,用于过场景时是否内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #region 销毁窗口相关 public void DestroyWindow <T >() where T : WindowBase{ DestroyWindow(typeof (T).Name); } private void DestroyWindow (string windowName ){ WindowBase window = GetWindow(windowName); DestroyWindow(window); } private void DestroyWindow (WindowBase window ){ if (window != null ) { if (mAllWindowDic.ContainsKey(window.Name)) { mAllWindowDic.Remove(window.Name); mAllWindowList.Remove(window); mVisibleWindowList.Remove(window); } window.SetVisible(false ); SetWindowMaskVisible(); window.OnHide(); window.OnDestroy(); GameObject.Destroy(window.gameObject); PopNextStackWindow(window); } } public void DestroyAllWindow (List<string > filterlist = null ){ for (int i = mAllWindowList.Count - 1 ; i >= 0 ; i--) { WindowBase window = mAllWindowList[i]; if (window == null || (filterlist != null && filterlist.Contains(window.Name))) { continue ; } DestroyWindow(window.Name); } Resources.UnloadUnusedAssets(); } #endregion
UIModule代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 using System;using System.Collections;using System.Collections.Generic;using UnityEngine;public class UIModule { private static UIModule _instance; public static UIModule Instance { get { if (_instance == null ) { _instance = new UIModule(); } return _instance; } } private Camera mUICamera; private Transform mUIRoot; private WindowConfig mWindowConfig; private Dictionary<string , WindowBase> mAllWindowDic = new Dictionary<string , WindowBase>(); private List<WindowBase> mAllWindowList = new List<WindowBase>(); private List<WindowBase> mVisibleWindowList = new List<WindowBase>(); private Queue<WindowBase> mWindowStack = new Queue<WindowBase>(); private bool mStartPopStackWindowStatus = false ; public void Initialize () { mUICamera = GameObject.Find("UICamera" ).GetComponent<Camera>(); mUIRoot = GameObject.Find("UIRoot" ).transform; mWindowConfig = Resources.Load<WindowConfig>("WindowConfig" ); #if UNITY_EDITOR mWindowConfig.GeneratorWindowConfig(); #endif } #region 窗口管理相关 #region 预加载相关 public void PreLoadWindow <T >() where T : WindowBase, new () { System.Type type = typeof (T); string windowName = type.Name; T windowBase = new T(); GameObject newWindowObj = TempLoadWindow(windowName); if (newWindowObj != null ) { windowBase.gameObject = newWindowObj; windowBase.transform = newWindowObj.transform; windowBase.Canvas = newWindowObj.GetComponent<Canvas>(); windowBase.Canvas.worldCamera = mUICamera; windowBase.Name = newWindowObj.name; windowBase.OnAwake(); windowBase.SetVisible(false ); RectTransform rectTrans = newWindowObj.GetComponent<RectTransform>(); rectTrans.anchorMax = Vector2.one; rectTrans.offsetMax = Vector2.zero; rectTrans.offsetMin = Vector2.zero; mAllWindowDic.Add(windowName, windowBase); mAllWindowList.Add(windowBase); } Debug.Log($"预加载窗口:{windowName} " ); } #endregion #region 弹出窗口相关 public T PopUpWindow <T >() where T : WindowBase, new () { System.Type type = typeof (T); string windowName = type.Name; WindowBase window = GetWindow(windowName); if (window != null ) { return ShowWindow(windowName) as T; } T t = new T(); return InitializeWindow(t, windowName) as T; } private WindowBase PopUpWindow (WindowBase QueuePopWindow ) { System.Type type = QueuePopWindow.GetType(); string windowName = type.Name; WindowBase LoadedWindow = GetWindow(windowName); if (LoadedWindow != null ) { return ShowWindow(windowName); } return InitializeWindow(QueuePopWindow, windowName); } private WindowBase InitializeWindow (WindowBase windowBase, string windowName ) { GameObject newWindowObj = TempLoadWindow(windowName); if (newWindowObj != null ) { windowBase.gameObject = newWindowObj; windowBase.transform = newWindowObj.transform; windowBase.Canvas = newWindowObj.GetComponent<Canvas>(); windowBase.Canvas.worldCamera = mUICamera; windowBase.Name = newWindowObj.name; windowBase.transform.SetAsLastSibling(); windowBase.OnAwake(); windowBase.SetVisible(true ); windowBase.OnShow(); RectTransform rectTrans = newWindowObj.GetComponent<RectTransform>(); rectTrans.anchorMax = Vector2.one; rectTrans.offsetMax = Vector2.zero; rectTrans.offsetMin = Vector2.zero; mAllWindowDic.Add(windowName, windowBase); mAllWindowList.Add(windowBase); mVisibleWindowList.Add(windowBase); SetWindowMaskVisible(); return windowBase; } Debug.LogError("未加载到对应的窗口,窗口名:" + windowName); return null ; } private WindowBase ShowWindow (string windowName ) { WindowBase window = null ; if (mAllWindowDic.ContainsKey(windowName)) { window = mAllWindowDic[windowName]; if (window.gameObject != null && window.Visible == false ) { mVisibleWindowList.Add(window); window.transform.SetAsLastSibling(); window.SetVisible(true ); SetWindowMaskVisible(); window.OnShow(); } return window; } else Debug.LogError(windowName + ":窗口不存在!请调用PopUpWindow进行弹出" ); return null ; } private WindowBase GetWindow (string windowName ) { if (mAllWindowDic.ContainsKey(windowName)) { return mAllWindowDic[windowName]; } return null ; } #endregion #region 获取窗口相关 public T GetWindow <T >() where T: WindowBase { System.Type type = typeof (T); foreach (var window in mVisibleWindowList) { if (window.Name == type.Name) { return (T)window; } } Debug.LogError("该窗口未获取到:" + type.Name); return null ; } #endregion #region 隐藏窗口相关 public void HideWindow <T >() where T : WindowBase { HideWindow(typeof (T).Name); } public void HideWindow (string windowName ) { WindowBase window = GetWindow(windowName); HideWindow(window); } private void HideWindow (WindowBase window ) { if (window != null && window.Visible) { mVisibleWindowList.Remove(window); window.SetVisible(false ); SetWindowMaskVisible(); window.OnHide(); } PopNextStackWindow(window); } #endregion #region 销毁窗口相关 public void DestroyWindow <T >() where T : WindowBase { DestroyWindow(typeof (T).Name); } private void DestroyWindow (string windowName ) { WindowBase window = GetWindow(windowName); DestroyWindow(window); } private void DestroyWindow (WindowBase window ) { if (window != null ) { if (mAllWindowDic.ContainsKey(window.Name)) { mAllWindowDic.Remove(window.Name); mAllWindowList.Remove(window); mVisibleWindowList.Remove(window); } window.SetVisible(false ); SetWindowMaskVisible(); window.OnHide(); window.OnDestroy(); GameObject.Destroy(window.gameObject); PopNextStackWindow(window); } } public void DestroyAllWindow (List<string > filterlist = null ) { for (int i = mAllWindowList.Count - 1 ; i >= 0 ; i--) { WindowBase window = mAllWindowList[i]; if (window == null || (filterlist != null && filterlist.Contains(window.Name))) { continue ; } DestroyWindow(window.Name); } Resources.UnloadUnusedAssets(); } #endregion #endregion #region 控制遮罩相关 private void SetWindowMaskVisible () { if (!UISetting.Instance.SINGMASK_SYSTEM) return ; WindowBase maxOrderWindowBase = null ; int maxOrder = 0 ; int maxIndex = 0 ; for (int i = 0 ; i < mVisibleWindowList.Count; i++) { WindowBase window = mVisibleWindowList[i]; if (window != null && window.gameObject != null ) { window.SetMaskVisible(false ); if (maxOrderWindowBase == null ) { maxOrderWindowBase = window; maxOrder = window.Canvas.sortingOrder; maxIndex = window.transform.GetSiblingIndex(); } else { if (maxOrder < window.Canvas.sortingOrder) { maxOrderWindowBase = window; maxOrder = window.Canvas.sortingOrder; } else if (maxOrder == window.Canvas.sortingOrder && maxIndex < window.transform.GetSiblingIndex()) { maxOrderWindowBase = window; maxIndex = window.transform.GetSiblingIndex(); } } } } maxOrderWindowBase?.SetMaskVisible(true ); } #endregion public GameObject TempLoadWindow (string windowName ) { GameObject window = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>(mWindowConfig.GetWindowPath(windowName)), mUIRoot); window.transform.localScale = Vector3.one; window.transform.localPosition = Vector3.zero; window.transform.rotation = Quaternion.identity; window.name = windowName; return window; } #region 堆栈系统相关 public void PushWindowToStack <T >(Action<WindowBase> popCallBack = null ) where T : WindowBase, new () { T windowBase = new T(); windowBase.PopStackListener = popCallBack; mWindowStack.Enqueue(windowBase); } public void StartPopFirstStackWindow () { if (mStartPopStackWindowStatus) return ; mStartPopStackWindowStatus = true ; PopStackWindow(); } public void PushAndPopStackWindow <T >(Action<WindowBase> popCallBack = null ) where T : WindowBase, new () { PushWindowToStack<T>(popCallBack); StartPopFirstStackWindow(); } private void PopNextStackWindow (WindowBase windowBase ) { if (windowBase != null && mStartPopStackWindowStatus && windowBase.PopStack) { windowBase.PopStack = false ; PopStackWindow(); } } public bool PopStackWindow () { if (mWindowStack.Count > 0 ) { WindowBase recordWindow = mWindowStack.Dequeue(); WindowBase popWindow = PopUpWindow(recordWindow); popWindow.PopStackListener = recordWindow.PopStackListener; popWindow.PopStack = true ; popWindow.PopStackListener?.Invoke(popWindow); popWindow.PopStackListener = null ; return true ; } else { mStartPopStackWindowStatus = false ; return false ; } } public void ClearStackWindows () { mWindowStack.Clear(); } #endregion }