UF_OLDL9——UI管理模块
注意!该UI模块是基于 UGUI 的! 请先学习UGUI再学习这里的内容
在游戏中,各种游戏对象可能会打开或者隐藏各种UI面板
如果让它们直接相互关联,那这些对象和面板的联系会变成错综复杂难以管理,也就是说程序的耦合性变高了
制作UI模块的目的,就是为了降低这种耦合性
首先,对于 游戏对象(也有可能是一个面板) 显示隐藏一个UI面板的需求
我们可以构建一个UI管理器,通过调用UI管理器来控制各个UI面板的显示隐藏
对于各个UI面板(Panel),
它们的共同点在于:它们需要管理控制自己面板内的各个控件,需要显示隐藏等
这时我们就可以抽象出这些共同行为,将其写入一个UI基类,让面板对象继承这个UI基类
UI基类
首先可以利用.GetComponentsInChildren<>()获取所有的组件(它们都有一个父类UIBehaviour),再用字典存储
我们可以通过输入控件对象名和控件类型来获取某个控件
其次,每个UI都有隐藏显示的方法,所以我们需要首先在基类声明显示和隐藏的方法,到之后具体到某个子类在重写
使用方法:
让UI面板类继承它
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 using System.Collections.Generic;using UnityEngine;using UnityEngine.EventSystems;using UnityEngine.UI;public abstract class BasePanel : MonoBehaviour { private Dictionary<string , List<UIBehaviour>> controlDic = new Dictionary<string , List<UIBehaviour>>(); protected virtual void Awake () { FindChildrenControl<Button>(); FindChildrenControl<Toggle>(); FindChildrenControl<InputField>(); FindChildrenControl<Slider>(); FindChildrenControl<ScrollRect>(); FindChildrenControl<Dropdown>(); FindChildrenControl<Image>(); FindChildrenControl<Text>(); } protected T GetControl <T >(string controlName ) where T : UIBehaviour { if (controlDic.ContainsKey(controlName)) { for (int i = 0 ; i < controlDic[controlName].Count; i++) { if (controlDic[controlName][i] is T) return controlDic[controlName][i] as T; } } return null ; } private void FindChildrenControl <T >() where T : UIBehaviour { T[] controls = this .GetComponentsInChildren<T>(); for (int i = 0 ; i < controls.Length; i++) { if (controlDic.ContainsKey(controls[i].gameObject.name)) controlDic[controls[i].gameObject.name].Add(controls[i]); controlDic.Add(controls[i].gameObject.name, new List<UIBehaviour>() { controls[i] }); } } public abstract void ShowMe () ; public abstract void HideMe () ; }
UI管理器
最基础的UI管理器
使用方法:
显示面板
隐藏面板
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 using System.Collections.Generic;using UnityEngine;using UnityEngine.Events;public enum E_UILayer{ Bottom, Middle, Top, System, } public class UIManager : BaseManager <UIManager >{ private Dictionary<string , BasePanel> panelDic = new Dictionary<string , BasePanel>(); private Transform top; private Transform middle; private Transform bottom; private Transform system; public UIManager () { GameObject canvasObj = ResourcesManager.Instance().Load<GameObject>("UI/Canvas" ); GameObject.DontDestroyOnLoad(canvasObj); Transform canvas = canvasObj.transform; GameObject.DontDestroyOnLoad(ResourcesManager.Instance().Load<GameObject>("UI/EventSystem" )); top = canvas.Find("Top" ); middle = canvas.Find("Middle" ); bottom = canvas.Find("Bottom" ); system = canvas.Find("System" ); } public void ShowPanel <T >(string panelName, E_UILayer layer = E_UILayer.Middle, UnityAction<T> callback = null ) where T : BasePanel { if (panelDic.ContainsKey(panelName)) { if (callback != null ) { panelDic[panelName].ShowMe(); callback(panelDic[panelName] as T); } } else { ResourcesManager.Instance().LoadAsync<GameObject>("UI/" + panelName, (panelObj) => { Transform father = bottom; switch (layer) { case E_UILayer.Bottom: father = bottom; break ; case E_UILayer.Middle: father = middle; break ; case E_UILayer.Top: father = top; break ; case E_UILayer.System: father = system; break ; } panelObj.transform.SetParent(father, false ); panelObj.transform.localPosition = Vector3.zero; panelObj.transform.localScale = Vector3.one; (panelObj.transform as RectTransform).offsetMax = Vector2.zero; (panelObj.transform as RectTransform).offsetMin = Vector2.zero; T panel = panelObj.GetComponent<T>(); if (panel != null ) callback(panel); panel.ShowMe(); panelDic.Add(panelName, panel); }); } } public void HidePanel (string panelName ) { if (panelDic.ContainsKey(panelName)) { panelDic[panelName].HideMe(); GameObject.Destroy(panelDic[panelName].gameObject); panelDic.Remove(panelName); } } }
优化面板基类事件监听的UI模块
首先修正了UI管理器在显示过某个面板后再执行显示面板方法仍然会加载该面板的bug
其次是增加的获取面板和获取层级对象的方法
然后是优化面板控件添加事件监听的方法,
原来的实现虽然优化了拖曳控件这一步,但仍然要通过控件名一个一个添加方法
这次优化,向控件添加方法的一部分实现,直接合并到UI基类的FindChildrenControl()的方法里
即某一种控件都添加一个空的有参的虚方法,虚方法参数为控件名字(用于区分不同的控件)
之后为某一种控件添加方法,只需要重写那个虚方法即可,使用switch通过控件名字参数来执行不同逻辑
UI管理器(修正后的)
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 using System.Collections.Generic;using UnityEngine;using UnityEngine.Events;public enum E_UILayer{ Bottom, Middle, Top, System, } public class UIManager : BaseManager <UIManager >{ private Dictionary<string , BasePanel> panelDic = new Dictionary<string , BasePanel>(); public RectTransform canvas; private Transform top; private Transform middle; private Transform bottom; private Transform system; public UIManager () { GameObject canvasObj = ResourcesManager.Instance().Load<GameObject>("UI/Canvas" ); GameObject.DontDestroyOnLoad(canvasObj); canvas = canvasObj.transform as RectTransform; GameObject.DontDestroyOnLoad(ResourcesManager.Instance().Load<GameObject>("UI/EventSystem" )); top = canvas.Find("Top" ); middle = canvas.Find("Middle" ); bottom = canvas.Find("Bottom" ); system = canvas.Find("System" ); } public void ShowPanel <T >(string panelName, E_UILayer layer = E_UILayer.Middle, UnityAction<T> callback = null ) where T : BasePanel { if (panelDic.ContainsKey(panelName)) { if (callback != null ) { panelDic[panelName].ShowMe(); callback(panelDic[panelName] as T); } } else { ResourcesManager.Instance().LoadAsync<GameObject>("UI/" + panelName, (panelObj) => { Transform father = bottom; switch (layer) { case E_UILayer.Bottom: father = bottom; break ; case E_UILayer.Middle: father = middle; break ; case E_UILayer.Top: father = top; break ; case E_UILayer.System: father = system; break ; } panelObj.transform.SetParent(father, false ); panelObj.transform.localPosition = Vector3.zero; panelObj.transform.localScale = Vector3.one; (panelObj.transform as RectTransform).offsetMax = Vector2.zero; (panelObj.transform as RectTransform).offsetMin = Vector2.zero; T panel = panelObj.GetComponent<T>(); if (callback != null ) callback(panel); panel.ShowMe(); panelDic.Add(panelName, panel); }); } } public void HidePanel (string panelName ) { if (panelDic.ContainsKey(panelName)) { panelDic[panelName].HideMe(); GameObject.Destroy(panelDic[panelName].gameObject); panelDic.Remove(panelName); } } public T GetPanel <T >(string panelName ) where T : BasePanel { if (panelDic.ContainsKey(panelName)) return panelDic[panelName] as T; return null ; } public Transform GetLayer (E_UILayer layer ) { switch (layer) { case E_UILayer.Bottom: return this .bottom; case E_UILayer.Middle: return this .middle; case E_UILayer.Top: return this .top; case E_UILayer.System: return this .system; } return null ; } }
UI基类(优化了事件监听方法)
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 using System.Collections.Generic;using UnityEngine;using UnityEngine.EventSystems;using UnityEngine.UI;public abstract class BasePanel : MonoBehaviour { private Dictionary<string , List<UIBehaviour>> controlDic = new Dictionary<string , List<UIBehaviour>>(); protected virtual void Awake () { FindChildrenControl<Button>(); FindChildrenControl<Toggle>(); FindChildrenControl<InputField>(); FindChildrenControl<Slider>(); FindChildrenControl<ScrollRect>(); FindChildrenControl<Dropdown>(); FindChildrenControl<Image>(); FindChildrenControl<Text>(); } protected T GetControl <T >(string controlName ) where T : UIBehaviour { if (controlDic.ContainsKey(controlName)) { for (int i = 0 ; i < controlDic[controlName].Count; i++) { if (controlDic[controlName][i] is T) return controlDic[controlName][i] as T; } } return null ; } private void FindChildrenControl <T >() where T : UIBehaviour { T[] controls = this .GetComponentsInChildren<T>(); for (int i = 0 ; i < controls.Length; i++) { string objName = controls[i].gameObject.name; if (controlDic.ContainsKey(objName)) controlDic[objName].Add(controls[i]); else controlDic.Add(objName, new List<UIBehaviour>() { controls[i] }); if (controls[i] is Button) { (controls[i] as Button).onClick.AddListener(() => { OnClick(objName); }); } else if (controls[i] is Toggle) { (controls[i] as Toggle).onValueChanged.AddListener((value ) => { OnValueChanged(objName, value ); }); } } } protected abstract void OnClick (string btnName ) ; protected abstract void OnValueChanged (string toggleName, bool value ) ; public virtual void ShowMe () { } public virtual void HideMe () { } }
UI基类使用示例
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 UnityEngine.UI;public class LoginPanel : BasePanel { public void InitInfo () { Debug.Log("初始化数据" ); } protected override void OnClick (string btnName ) { switch (btnName) { case "btnStart" : Debug.Log("btnStart被点击" ); break ; case "btnQuit" : Debug.Log("btnQuit被点击" ); break ; } } protected override void OnValueChanged (string toggleName, bool value ) { switch (toggleName) { } } }
自定义事件监听的UI模块
关于自定义事件监听请看这里 ——> EventTrigger
每次为EventTrigger添加自定义事件,都会写一些重复的代码,例如:声明Entry,修改entryID,添加回调函数等
我们可以将这些重复的代码在UI管理器写成一个方法,为UI控件添加自定义事件监听
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 using System.Collections.Generic;using UnityEngine;using UnityEngine.Events;using UnityEngine.EventSystems;public enum E_UILayer{ Bottom, Middle, Top, System, } public class UIManager : BaseManager <UIManager >{ private Dictionary<string , BasePanel> panelDic = new Dictionary<string , BasePanel>(); public RectTransform canvas; private Transform top; private Transform middle; private Transform bottom; private Transform system; public UIManager () { GameObject canvasObj = ResourcesManager.Instance().Load<GameObject>("UI/Canvas" ); GameObject.DontDestroyOnLoad(canvasObj); canvas = canvasObj.transform as RectTransform; GameObject.DontDestroyOnLoad(ResourcesManager.Instance().Load<GameObject>("UI/EventSystem" )); top = canvas.Find("Top" ); middle = canvas.Find("Middle" ); bottom = canvas.Find("Bottom" ); system = canvas.Find("System" ); } public void ShowPanel <T >(string panelName, E_UILayer layer = E_UILayer.Middle, UnityAction<T> callback = null ) where T : BasePanel { if (panelDic.ContainsKey(panelName)) { if (callback != null ) { panelDic[panelName].ShowMe(); callback(panelDic[panelName] as T); } } else { ResourcesManager.Instance().LoadAsync<GameObject>("UI/" + panelName, (panelObj) => { Transform father = bottom; switch (layer) { case E_UILayer.Bottom: father = bottom; break ; case E_UILayer.Middle: father = middle; break ; case E_UILayer.Top: father = top; break ; case E_UILayer.System: father = system; break ; } panelObj.transform.SetParent(father, false ); panelObj.transform.localPosition = Vector3.zero; panelObj.transform.localScale = Vector3.one; (panelObj.transform as RectTransform).offsetMax = Vector2.zero; (panelObj.transform as RectTransform).offsetMin = Vector2.zero; T panel = panelObj.GetComponent<T>(); if (callback != null ) callback(panel); panel.ShowMe(); panelDic.Add(panelName, panel); }); } } public void HidePanel (string panelName ) { if (panelDic.ContainsKey(panelName)) { panelDic[panelName].HideMe(); GameObject.Destroy(panelDic[panelName].gameObject); panelDic.Remove(panelName); } } public T GetPanel <T >(string panelName ) where T : BasePanel { if (panelDic.ContainsKey(panelName)) return panelDic[panelName] as T; return null ; } public Transform GetLayer (E_UILayer layer ) { switch (layer) { case E_UILayer.Bottom: return this .bottom; case E_UILayer.Middle: return this .middle; case E_UILayer.Top: return this .top; case E_UILayer.System: return this .system; } return null ; } public static void AddCustomEventTrigger (UIBehaviour control, EventTriggerType type, UnityAction<BaseEventData> callback ) { EventTrigger trigger = control.GetComponent<EventTrigger>(); if (trigger == null ) trigger = control.gameObject.AddComponent<EventTrigger>(); EventTrigger.Entry entry = new EventTrigger.Entry(); entry.eventID = type; entry.callback.AddListener(callback); trigger.triggers.Add(entry); } }
使用示例
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 using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.EventSystems;using UnityEngine.UI;public class LoginPanel : BasePanel { private void Start () { UIManager.AddCustomEventTrigger(GetControl<Button>("btnStart" ), EventTriggerType.PointerEnter, (data) => { Debug.Log("进入" ); }); UIManager.AddCustomEventTrigger(GetControl<Button>("btnStart" ), EventTriggerType.PointerExit, (data) => { Debug.Log("离开" ); }); } private void Drag (BaseEventData data ) { } public void InitInfo () { Debug.Log("初始化数据" ); } protected override void OnClick (string btnName ) { switch (btnName) { case "btnStart" : Debug.Log("btnStart被点击" ); break ; case "btnQuit" : Debug.Log("btnQuit被点击" ); break ; } } protected override void OnValueChanged (string toggleName, bool value ) { } }