这个框架简单易懂,上手就可以直接拿来用,主要是单例管理类,界面和界面之间的互相交流通过单例去实现,个人感觉不是很好,但是我特别喜欢他的管理层级非常分明。

之后会发一个广播机制,结合上这套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 }

(四)使用:

继承,调用就行了,简单示例一下

unity好用的UI框架 unity ugui框架_封装

 

 

总结:

这个框架有优点,也有缺点

优点是层级管理分明,分工明确,封装之后调用简单方便

缺点界面和界面的交流,以及模块和模块之间的交流,耦合性还是不能很好的解决

有时间会写一套广播机制,配合着使用,已达到尽可能解耦的效果