这个框架简单易懂,上手就可以直接拿来用,主要是单例管理类,界面和界面之间的互相交流通过单例去实现,个人感觉不是很好,但是我特别喜欢他的管理层级非常分明。
之后会发一个广播机制,结合上这套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 }
(四)使用:
继承,调用就行了,简单示例一下
总结:
这个框架有优点,也有缺点
优点是层级管理分明,分工明确,封装之后调用简单方便
缺点界面和界面的交流,以及模块和模块之间的交流,耦合性还是不能很好的解决
有时间会写一套广播机制,配合着使用,已达到尽可能解耦的效果