這個框架簡單易懂,上手就可以直接拿來用,主要是單例管理類,界面和界面之間的互相交流通過單例去實現,個人感覺不是很好,但是我特別喜歡他的管理層級非常分明。
之后會發一個廣播機制,結合上這套UI框架,但是本文主要是講這個框架,所以后話就后話說吧,話不多說上代碼
(一)UImanager:
以panel為編程單位,儲存管理panel和下級控件,向外提供接口方法,接收界面和控件主動往管理類里注冊,排查是否重復注冊以及最后銷毀等
注意:我在使用的時候發現還是有很多需要注意的問題的
1、UIManager必須游戲啟動第一時間啟動,整個游戲進程中不能銷毀的,可以給個空物體,然后dontdestory,再動態addcompnent加入
2、整個框架主體分為三個腳本,執行順序為 UIManager > UIBase > UIBehaviour,我在第一使用的時候發現,只要一加載界面就會報空,百思不其解,然后層層斷點發現執行順序的問題,UIBehaivour先執行了,導致找不到UIManager往里注冊,有點坑
3、因為字典存的是界面name,控件name,控件對象,在RegistGameObject函數中會進行一次篩選,key不能相同,也就意味着界面名字不能重復,同界面的控件名字不能重復,我在做交易列表時被這個搞得頭疼,一定要注意,加載完換個名字
1 using System.Collections.Generic; 2 using UnityEngine; 3 4 public class UIManager : MonoBehaviour 5 { 6 Dictionary<string, Dictionary<string, GameObject>> allChild; 7 public static UIManager Instance; 8 9 private GameObject mainCanvas; 10 public GameObject MainCanvas 11 { 12 get 13 { 14 if (mainCanvas == null) 15 { 16 mainCanvas = GameObject.FindGameObjectWithTag("MainCanvas"); 17 } 18 return mainCanvas; 19 } 20 } 21 22 public GameObject InstantiateUIPanelGameObject(string path, string endWithName = "", Transform parent = null) 23 { 24 if (parent == null) 25 { 26 parent = MainCanvas.transform; 27 } 28 GameObject tmpGameObj = Instantiate(Resources.Load<GameObject>(path)); 29 tmpGameObj.name = tmpGameObj.name.Replace("(Clone)", endWithName); 30 tmpGameObj.transform.SetParent(parent,false); 31 return tmpGameObj; 32 } 33 34 void Awake() 35 { 36 allChild = new Dictionary<string, Dictionary<string, GameObject>>(); 37 Instance = this; 38 } 39 40 /// <summary> 41 /// 可以拿到字典中已經注冊過的panel下的控件 42 /// </summary> 43 /// <param name="panelName"></param> 44 /// <param name="widgeNmae"></param> 45 /// <returns></returns> 46 public GameObject GetGameObject(string panelName, string widgeNmae) 47 { 48 if (allChild.ContainsKey(panelName)) 49 { 50 return allChild[panelName][widgeNmae]; 51 } 52 return null; 53 } 54 55 /// <summary> 56 /// 反注冊,當你確定不再需要這個界面的時候,沒必要浪費空間 57 /// </summary> 58 /// <param name="panelName"></param> 59 /// <param name="widgeName"></param> 60 public void UnRegistGameObject(string panelName, string widgeName) 61 { 62 if (allChild.ContainsKey(panelName)) 63 { 64 if (allChild.ContainsKey(widgeName)) 65 { 66 allChild[panelName].Remove(widgeName); 67 } 68 } 69 } 70 71 /// <summary> 72 /// 銷毀某個panel上所有控件 73 /// </summary> 74 /// <param name="panelName"></param> 75 public void DestroyAllWidgeFormPanel(string panelName) 76 { 77 if (allChild.ContainsKey(panelName)) 78 { 79 if (allChild[panelName] != null) 80 { 81 allChild[panelName].Clear(); 82 } 83 } 84 } 85 86 /// <summary> 87 /// 供UIBehaviour調用,UIBehaviour每個控件都會動態掛載,並且在awake里面調用,注冊自己 88 /// </summary> 89 /// <param name="panelName"></param> 90 /// <param name="widgeName"></param> 91 /// <param name="widge"></param> 92 public void RegistGameObject(string panelName, string widgeName, GameObject widge) 93 { 94 if (!allChild.ContainsKey(panelName)) 95 { 96 allChild.Add(panelName, new Dictionary<string, GameObject>()); 97 } 98 99 if (!allChild[panelName].ContainsKey(widgeName)) 100 { 101 allChild[panelName].Add(widgeName, widge); 102 } 103 else 104 { 105 Debug.LogError(" has been registed => " + widgeName); 106 } 107 } 108 109 public void CleanDic() 110 { 111 allChild.Clear(); 112 } 113 }
(二)UIBase:
這是一個供界面腳本繼承的基類,前面也說次框架是以panel為編程單位的,這也就是說只要你想用這套框架,只需要在panel上掛載自己寫的腳本去控制底下的所有控件即可,但是這個腳本必須繼承UIBase
這里又存在一個硬性條件,那就是panel下,你所要寫功能的控件,對象名的結尾必須_N,這是為了動態掛腳本UIBehaivour,我想這是為了讓預設體或者AB盡量減少依賴吧
在UIBase里存着panel下所有的控件,不用再去find了,避免低效,同時在UIBase里對各種控件的回調進行了二次封裝,直接調用傳參就行,這點我很喜歡,簡單粗暴
如果有需要,也可以自己拓展封裝,底層回調在UIBehaivour封裝,UIBase進行二次封裝
1 using UnityEngine; 2 using UnityEngine.Events; 3 using UnityEngine.EventSystems; 4 using UnityEngine.UI; 5 6 public class UIBase : MonoBehaviour 7 { 8 public Transform[] allChild; 9 10 private void Awake() 11 { 12 // 將所有的子控件 獲取到。 13 allChild = gameObject.GetComponentsInChildren<Transform>(); 14 for (int i = 0; i < allChild.Length; i++) 15 { 16 //以名字區別控件,動態掛載腳本,所以必須按規范命名 17 if (allChild[i].name.EndsWith("_N")) 18 { 19 allChild[i].gameObject.AddComponent<UIBehaviour>(); 20 } 21 } 22 } 23 24 public virtual void OnDestroy() 25 { 26 UIManagerLen.Instance.DestroyAllWidgeFormPanel(transform.name); 27 } 28 29 /// <summary> 30 /// 從UIManager里面拿子控件 31 /// </summary> 32 /// <param name="widgaeName"></param> 33 /// <returns></returns> 34 public GameObject GetGameObject(string widgaeName) 35 { 36 return UIManagerLen.Instance.GetGameObject(transform.name, widgaeName); 37 } 38 39 /// <summary> 40 /// 為了拿到底層封裝 41 /// </summary> 42 /// <param name="widgaeName"></param> 43 /// <returns></returns> 44 public UIBehaviour GetBehavour(string widgaeName) 45 { 46 // 找對象 47 GameObject tmpBtn = GetGameObject(widgaeName); 48 if (tmpBtn) 49 { 50 // 拿到 UIBehavour 51 UIBehaviour behavour = tmpBtn.GetComponent<UIBehaviour>(); 52 return behavour; 53 } 54 return null; 55 } 56 57 /// <summary> 58 /// button 59 /// </summary> 60 /// <param name="widageName"></param> 61 /// <param name="action"></param> 62 public void AddButtonListen(string widageName, UnityAction action) 63 { 64 UIBehaviour behavour = GetBehavour(widageName); 65 if (behavour) 66 { 67 //綁定事件 68 behavour.AddButtonListener(action); 69 } 70 } 71 72 /// <summary> 73 /// slider 74 /// </summary> 75 /// <param name="widageName"></param> 76 /// <param name="action"></param> 77 public void AddSliderListen(string widageName, UnityAction<float> action) 78 { 79 UIBehaviour behavour = GetBehavour(widageName); 80 if (behavour) 81 { 82 //綁定事件 83 behavour.AddSliderListen(action); 84 } 85 } 86 87 88 /// <summary> 89 /// scroll view 90 /// </summary> 91 /// <param name="cellName"></param> 92 /// <param name="widageName"></param> 93 /// <param name="action"></param> 94 public void AddScrollListen(string widageName, UnityAction<Vector2> action) 95 { 96 UIBehaviour behavour = GetBehavour(widageName); 97 if (behavour) 98 { 99 //綁定事件 100 behavour.AddScrollListen(action); 101 } 102 } 103 104 /// <summary> 105 /// dropdown 106 /// </summary> 107 /// <param name="widageName"></param> 108 /// <param name="action"></param> 109 public void AddDropDownListen(string widageName, UnityAction<int> action) 110 { 111 UIBehaviour behavour = GetBehavour(widageName); 112 if (behavour) 113 { 114 //綁定事件 115 behavour.AddDropDownListen(action); 116 } 117 } 118 119 /// <summary> 120 /// toggle 121 /// </summary> 122 /// <param name="widageName"></param> 123 /// <param name="action"></param> 124 public void AddToggleListen(string widageName, UnityAction<bool> action) 125 { 126 UIBehaviour behavour = GetBehavour(widageName); 127 if (behavour) 128 { 129 //綁定事件 130 behavour.AddToggleListener(action); 131 } 132 } 133 134 135 /// <summary> 136 /// inputfiled 137 /// </summary> 138 /// <param name="widageName"></param> 139 /// <param name="action"></param> 140 public void AddInputListen(string widageName, UnityAction<string> action) 141 { 142 UIBehaviour behavour = GetBehavour(widageName); 143 if (behavour) 144 { 145 //綁定事件 146 behavour.AddInputEndListen(action); 147 } 148 } 149 150 151 /// <summary> 152 /// 文本賦值 153 /// </summary> 154 /// <param name="widageName"></param> 155 /// <param name="value"></param> 156 public void ChangeText(string widageName, string value) 157 { 158 UIBehaviour behavour = GetBehavour(widageName); 159 if (behavour) 160 { 161 //綁定事件 162 behavour.ChangeText(value); 163 } 164 } 165 166 /// <summary> 167 /// 圖片點擊 168 /// </summary> 169 /// <param name="widageName"></param> 170 /// <param name="action"></param> 171 public void AddPointClick(string widageName, UnityAction<BaseEventData> action) 172 { 173 UIBehaviour behavour = GetBehavour(widageName); 174 if (behavour) 175 { 176 behavour.AddInterfaceLisenter(EventTriggerType.PointerClick, action); 177 } 178 } 179 180 181 /// <summary> 182 /// 圖片拖拽 開始,正在拖,結束 183 /// </summary> 184 /// <param name="widageName"></param> 185 /// <param name="action"></param> 186 public void AddBeginDrag(string widageName, UnityAction<BaseEventData> action) 187 { 188 UIBehaviour behavour = GetBehavour(widageName); 189 if (behavour) 190 { 191 behavour.AddInterfaceLisenter(EventTriggerType.BeginDrag, action); 192 } 193 } 194 public void AddOnDrag(string widageName, UnityAction<BaseEventData> action) 195 { 196 UIBehaviour behavour = GetBehavour(widageName); 197 if (behavour) 198 { 199 behavour.AddInterfaceLisenter(EventTriggerType.Drag, action); 200 } 201 } 202 public void AddEndDrag(string widageName, UnityAction<BaseEventData> action) 203 { 204 UIBehaviour behavour = GetBehavour(widageName); 205 if (behavour) 206 { 207 behavour.AddInterfaceLisenter(EventTriggerType.EndDrag, action); 208 } 209 } 210 211 212 /// <summary> 213 /// 更換圖片 214 /// </summary> 215 /// <param name="widageName"></param> 216 /// <param name="value"></param> 217 public void SetImage(string widageName, Sprite value) 218 { 219 UIBehaviour subManager = GetBehavour(widageName); 220 if (subManager) 221 { 222 subManager.GetComponent<Image>().sprite = value; 223 } 224 } 225 226 /// <summary> 227 /// 獲取圖片 228 /// </summary> 229 /// <param name="widageName"></param> 230 /// <returns></returns> 231 public Image GetImage(string widageName) 232 { 233 UIBehaviour behavour = GetBehavour(widageName); 234 if (behavour) 235 { 236 return behavour.GetImage(); 237 } 238 return null; 239 } 240 241 /// <summary> 242 /// 拿到text組件的值 243 /// </summary> 244 /// <param name="widageName"></param> 245 /// <returns></returns> 246 public string GetText(string widageName) 247 { 248 UIBehaviour behavour = GetBehavour(widageName); 249 if (behavour) 250 { 251 return behavour.GetText(); 252 } 253 return null; 254 } 255 }
(三)UIBehaivour:
這個腳本就是動態掛載各個控件上的腳本,作用只有兩個
1、向UIManager注冊,接受管理
2、底層回調封裝,供UIBase調用從而進行二次封裝后直接使用 如果需要拓展也是從這里開始
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEngine.UI; 5 using UnityEngine.Events; 6 using UnityEngine.EventSystems; 7 8 9 public class UIBehaviour : MonoBehaviour 10 { 11 UIBase tmpBase; 12 private void Awake() 13 { 14 tmpBase = gameObject.GetComponentInParent<UIBase>();//找到父類,也就是panel 15 16 UIManagerLen.Instance.RegistGameObject(tmpBase.name, transform.name, gameObject);//向管理類注冊自己 17 } 18 19 private void OnDestroy() 20 { 21 UIManagerLen.Instance.UnRegistGameObject(tmpBase.name, gameObject.name); 22 } 23 24 #region UGUI各種控件的回調封裝 25 public void AddButtonListener(UnityAction action)//按鈕回調封裝 26 { 27 Button tmpBtn = gameObject.GetComponent<Button>(); 28 if (tmpBtn) tmpBtn.onClick.AddListener(action); 29 } 30 public void AddSliderListen(UnityAction<float> action)//滾動條回調封裝 31 { 32 Slider tmpBtn = gameObject.GetComponent<Slider>(); 33 if (tmpBtn) tmpBtn.onValueChanged.AddListener(action); 34 } 35 public void AddDropDownListen(UnityAction<int> action)//下拉框回調封裝 36 { 37 Dropdown tmpBtn = gameObject.GetComponent<Dropdown>(); 38 if (tmpBtn) tmpBtn.onValueChanged.AddListener(action); 39 } 40 public void AddScrollListen(UnityAction<Vector2> action)//滾動框回調封裝 41 { 42 ScrollRect tmpBtn = gameObject.GetComponent<ScrollRect>(); 43 if (tmpBtn) tmpBtn.onValueChanged.AddListener(action); 44 } 45 public void AddToggleListener(UnityAction<bool> action) 46 { 47 Toggle tmpToggle = gameObject.GetComponent<Toggle>(); 48 if (tmpToggle) tmpToggle.onValueChanged.AddListener(action); 49 } 50 public void AddInputEndListen(UnityAction<string> action)//輸入框回調封裝 51 { 52 InputField tmpBtn = GetComponent<InputField>(); 53 if (tmpBtn) tmpBtn.onEndEdit.AddListener(action); 54 } 55 public void ChangeText(string value)//改變文本的接口 56 { 57 Text tmpBtn = gameObject.GetComponent<Text>(); 58 if (tmpBtn) tmpBtn.text = value; 59 } 60 public Image GetImage()//如果控件是圖片,提供獲取圖片的接口 61 { 62 Image tmpBtn = gameObject.GetComponent<Image>(); 63 return tmpBtn; 64 } 65 public string GetText()//如果控件是文本,提供獲取文本的接口 66 { 67 Text tmpBtn = gameObject.GetComponent<Text>(); 68 return tmpBtn.text; 69 } 70 #endregion 71 72 /// <summary> 73 /// 用代碼動態添加接口事件 74 /// </summary> 75 /// <param name="action"></param> 76 public void AddInterfaceLisenter(EventTriggerType triger, UnityAction<BaseEventData> action) 77 { 78 79 EventTrigger trigger = gameObject.GetComponent<EventTrigger>(); 80 81 if (trigger == null) 82 trigger = gameObject.AddComponent<EventTrigger>(); 83 84 EventTrigger.Entry entry = new EventTrigger.Entry(); 85 //綁定哪個接口 86 entry.eventID = triger; 87 88 entry.callback = new EventTrigger.TriggerEvent(); 89 90 entry.callback.AddListener(action); 91 92 trigger.triggers.Add(entry); 93 } 94 }
(四)使用:
繼承,調用就行了,簡單示例一下
總結:
這個框架有優點,也有缺點
優點是層級管理分明,分工明確,封裝之后調用簡單方便
缺點界面和界面的交流,以及模塊和模塊之間的交流,耦合性還是不能很好的解決
有時間會寫一套廣播機制,配合着使用,已達到盡可能解耦的效果