这个框架简单易懂,上手就可以直接拿来用,主要是单例管理类,界面和界面之间的互相交流通过单例去实现,个人感觉不是很好,但是我特别喜欢他的管理层级非常分明。
之后会发一个广播机制,结合上这套UI框架,但是本文主要是讲这个框架,所以后话就后话说吧,话不多说上代码
(一)UImanager:
以panel为编程单位,储存管理panel和下级控件,向外提供接口方法,接收界面和控件主动往管理类里注册,排查是否重复注册以及最后销毁等
注意:我在使用的时候发现还是有很多需要注意的问题的
1、UIManager必须游戏启动第一时间启动,整个游戏进程中不能销毁的,可以给个空物体,然后dontdestory,再动态addcompnent加入
2、整个框架主体分为三个脚本,执行顺序为 UIManager > UIBase > UIBehaviour,我在第一使用的时候发现,只要一加载界面就会报空,百思不其解,然后层层断点发现执行顺序的问题,UIBehaivour先执行了,导致找不到UIManager往里注册,有点坑
3、因为字典存的是界面name,控件name,控件对象,在RegistGameObject函数中会进行一次筛选,key不能相同,也就意味着界面名字不能重复,同界面的控件名字不能重复,我在做交易列表时被这个搞得头疼,一定要注意,加载完换个名字
using System.Collections.Generic;
using UnityEngine; public class UIManager : MonoBehaviour
{
Dictionary<string, Dictionary<string, GameObject>> allChild;
public static UIManager Instance; private GameObject mainCanvas;
public GameObject MainCanvas
{
get
{
if (mainCanvas == null)
{
mainCanvas = GameObject.FindGameObjectWithTag("MainCanvas");
}
return mainCanvas;
}
} public GameObject InstantiateUIPanelGameObject(string path, string endWithName = "", Transform parent = null)
{
if (parent == null)
{
parent = MainCanvas.transform;
}
GameObject tmpGameObj = Instantiate(Resources.Load<GameObject>(path));
tmpGameObj.name = tmpGameObj.name.Replace("(Clone)", endWithName);
tmpGameObj.transform.SetParent(parent,false);
return tmpGameObj;
} void Awake()
{
allChild = new Dictionary<string, Dictionary<string, GameObject>>();
Instance = this;
} /// <summary>
/// 可以拿到字典中已经注册过的panel下的控件
/// </summary>
/// <param name="panelName"></param>
/// <param name="widgeNmae"></param>
/// <returns></returns>
public GameObject GetGameObject(string panelName, string widgeNmae)
{
if (allChild.ContainsKey(panelName))
{
return allChild[panelName][widgeNmae];
}
return null;
} /// <summary>
/// 反注册,当你确定不再需要这个界面的时候,没必要浪费空间
/// </summary>
/// <param name="panelName"></param>
/// <param name="widgeName"></param>
public void UnRegistGameObject(string panelName, string widgeName)
{
if (allChild.ContainsKey(panelName))
{
if (allChild.ContainsKey(widgeName))
{
allChild[panelName].Remove(widgeName);
}
}
} /// <summary>
/// 销毁某个panel上所有控件
/// </summary>
/// <param name="panelName"></param>
public void DestroyAllWidgeFormPanel(string panelName)
{
if (allChild.ContainsKey(panelName))
{
if (allChild[panelName] != null)
{
allChild[panelName].Clear();
}
}
} /// <summary>
/// 供UIBehaviour调用,UIBehaviour每个控件都会动态挂载,并且在awake里面调用,注册自己
/// </summary>
/// <param name="panelName"></param>
/// <param name="widgeName"></param>
/// <param name="widge"></param>
public void RegistGameObject(string panelName, string widgeName, GameObject widge)
{
if (!allChild.ContainsKey(panelName))
{
allChild.Add(panelName, new Dictionary<string, GameObject>());
} if (!allChild[panelName].ContainsKey(widgeName))
{
allChild[panelName].Add(widgeName, widge);
}
else
{
Debug.LogError(" has been registed => " + widgeName);
}
} public void CleanDic()
{
allChild.Clear();
}
}
(二)UIBase:
这是一个供界面脚本继承的基类,前面也说次框架是以panel为编程单位的,这也就是说只要你想用这套框架,只需要在panel上挂载自己写的脚本去控制底下的所有控件即可,但是这个脚本必须继承UIBase
这里又存在一个硬性条件,那就是panel下,你所要写功能的控件,对象名的结尾必须_N,这是为了动态挂脚本UIBehaivour,我想这是为了让预设体或者AB尽量减少依赖吧
在UIBase里存着panel下所有的控件,不用再去find了,避免低效,同时在UIBase里对各种控件的回调进行了二次封装,直接调用传参就行,这点我很喜欢,简单粗暴
如果有需要,也可以自己拓展封装,底层回调在UIBehaivour封装,UIBase进行二次封装
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI; public class UIBase : MonoBehaviour
{
public Transform[] allChild; private void Awake()
{
// 将所有的子控件 获取到。
allChild = gameObject.GetComponentsInChildren<Transform>();
for (int i = ; i < allChild.Length; i++)
{
//以名字区别控件,动态挂载脚本,所以必须按规范命名
if (allChild[i].name.EndsWith("_N"))
{
allChild[i].gameObject.AddComponent<UIBehaviour>();
}
}
} public virtual void OnDestroy()
{
UIManagerLen.Instance.DestroyAllWidgeFormPanel(transform.name);
} /// <summary>
/// 从UIManager里面拿子控件
/// </summary>
/// <param name="widgaeName"></param>
/// <returns></returns>
public GameObject GetGameObject(string widgaeName)
{
return UIManagerLen.Instance.GetGameObject(transform.name, widgaeName);
} /// <summary>
/// 为了拿到底层封装
/// </summary>
/// <param name="widgaeName"></param>
/// <returns></returns>
public UIBehaviour GetBehavour(string widgaeName)
{
// 找对象
GameObject tmpBtn = GetGameObject(widgaeName);
if (tmpBtn)
{
// 拿到 UIBehavour
UIBehaviour behavour = tmpBtn.GetComponent<UIBehaviour>();
return behavour;
}
return null;
} /// <summary>
/// button
/// </summary>
/// <param name="widageName"></param>
/// <param name="action"></param>
public void AddButtonListen(string widageName, UnityAction action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
//绑定事件
behavour.AddButtonListener(action);
}
} /// <summary>
/// slider
/// </summary>
/// <param name="widageName"></param>
/// <param name="action"></param>
public void AddSliderListen(string widageName, UnityAction<float> action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
//绑定事件
behavour.AddSliderListen(action);
}
} /// <summary>
/// scroll view
/// </summary>
/// <param name="cellName"></param>
/// <param name="widageName"></param>
/// <param name="action"></param>
public void AddScrollListen(string widageName, UnityAction<Vector2> action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
//绑定事件
behavour.AddScrollListen(action);
}
} /// <summary>
/// dropdown
/// </summary>
/// <param name="widageName"></param>
/// <param name="action"></param>
public void AddDropDownListen(string widageName, UnityAction<int> action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
//绑定事件
behavour.AddDropDownListen(action);
}
} /// <summary>
/// toggle
/// </summary>
/// <param name="widageName"></param>
/// <param name="action"></param>
public void AddToggleListen(string widageName, UnityAction<bool> action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
//绑定事件
behavour.AddToggleListener(action);
}
} /// <summary>
/// inputfiled
/// </summary>
/// <param name="widageName"></param>
/// <param name="action"></param>
public void AddInputListen(string widageName, UnityAction<string> action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
//绑定事件
behavour.AddInputEndListen(action);
}
} /// <summary>
/// 文本赋值
/// </summary>
/// <param name="widageName"></param>
/// <param name="value"></param>
public void ChangeText(string widageName, string value)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
//绑定事件
behavour.ChangeText(value);
}
} /// <summary>
/// 图片点击
/// </summary>
/// <param name="widageName"></param>
/// <param name="action"></param>
public void AddPointClick(string widageName, UnityAction<BaseEventData> action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
behavour.AddInterfaceLisenter(EventTriggerType.PointerClick, action);
}
} /// <summary>
/// 图片拖拽 开始,正在拖,结束
/// </summary>
/// <param name="widageName"></param>
/// <param name="action"></param>
public void AddBeginDrag(string widageName, UnityAction<BaseEventData> action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
behavour.AddInterfaceLisenter(EventTriggerType.BeginDrag, action);
}
}
public void AddOnDrag(string widageName, UnityAction<BaseEventData> action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
behavour.AddInterfaceLisenter(EventTriggerType.Drag, action);
}
}
public void AddEndDrag(string widageName, UnityAction<BaseEventData> action)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
behavour.AddInterfaceLisenter(EventTriggerType.EndDrag, action);
}
} /// <summary>
/// 更换图片
/// </summary>
/// <param name="widageName"></param>
/// <param name="value"></param>
public void SetImage(string widageName, Sprite value)
{
UIBehaviour subManager = GetBehavour(widageName);
if (subManager)
{
subManager.GetComponent<Image>().sprite = value;
}
} /// <summary>
/// 获取图片
/// </summary>
/// <param name="widageName"></param>
/// <returns></returns>
public Image GetImage(string widageName)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
return behavour.GetImage();
}
return null;
} /// <summary>
/// 拿到text组件的值
/// </summary>
/// <param name="widageName"></param>
/// <returns></returns>
public string GetText(string widageName)
{
UIBehaviour behavour = GetBehavour(widageName);
if (behavour)
{
return behavour.GetText();
}
return null;
}
}
(三)UIBehaivour:
这个脚本就是动态挂载各个控件上的脚本,作用只有两个
1、向UIManager注册,接受管理
2、底层回调封装,供UIBase调用从而进行二次封装后直接使用 如果需要拓展也是从这里开始
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems; public class UIBehaviour : MonoBehaviour
{
UIBase tmpBase;
private void Awake()
{
tmpBase = gameObject.GetComponentInParent<UIBase>();//找到父类,也就是panel UIManagerLen.Instance.RegistGameObject(tmpBase.name, transform.name, gameObject);//向管理类注册自己
} private void OnDestroy()
{
UIManagerLen.Instance.UnRegistGameObject(tmpBase.name, gameObject.name);
} #region UGUI各种控件的回调封装
public void AddButtonListener(UnityAction action)//按钮回调封装
{
Button tmpBtn = gameObject.GetComponent<Button>();
if (tmpBtn) tmpBtn.onClick.AddListener(action);
}
public void AddSliderListen(UnityAction<float> action)//滚动条回调封装
{
Slider tmpBtn = gameObject.GetComponent<Slider>();
if (tmpBtn) tmpBtn.onValueChanged.AddListener(action);
}
public void AddDropDownListen(UnityAction<int> action)//下拉框回调封装
{
Dropdown tmpBtn = gameObject.GetComponent<Dropdown>();
if (tmpBtn) tmpBtn.onValueChanged.AddListener(action);
}
public void AddScrollListen(UnityAction<Vector2> action)//滚动框回调封装
{
ScrollRect tmpBtn = gameObject.GetComponent<ScrollRect>();
if (tmpBtn) tmpBtn.onValueChanged.AddListener(action);
}
public void AddToggleListener(UnityAction<bool> action)
{
Toggle tmpToggle = gameObject.GetComponent<Toggle>();
if (tmpToggle) tmpToggle.onValueChanged.AddListener(action);
}
public void AddInputEndListen(UnityAction<string> action)//输入框回调封装
{
InputField tmpBtn = GetComponent<InputField>();
if (tmpBtn) tmpBtn.onEndEdit.AddListener(action);
}
public void ChangeText(string value)//改变文本的接口
{
Text tmpBtn = gameObject.GetComponent<Text>();
if (tmpBtn) tmpBtn.text = value;
}
public Image GetImage()//如果控件是图片,提供获取图片的接口
{
Image tmpBtn = gameObject.GetComponent<Image>();
return tmpBtn;
}
public string GetText()//如果控件是文本,提供获取文本的接口
{
Text tmpBtn = gameObject.GetComponent<Text>();
return tmpBtn.text;
}
#endregion /// <summary>
/// 用代码动态添加接口事件
/// </summary>
/// <param name="action"></param>
public void AddInterfaceLisenter(EventTriggerType triger, UnityAction<BaseEventData> action)
{ EventTrigger trigger = gameObject.GetComponent<EventTrigger>(); if (trigger == null)
trigger = gameObject.AddComponent<EventTrigger>(); EventTrigger.Entry entry = new EventTrigger.Entry();
//绑定哪个接口
entry.eventID = triger; entry.callback = new EventTrigger.TriggerEvent(); entry.callback.AddListener(action); trigger.triggers.Add(entry);
}
}
(四)使用:
继承,调用就行了,简单示例一下
总结:
这个框架有优点,也有缺点
优点是层级管理分明,分工明确,封装之后调用简单方便
缺点界面和界面的交流,以及模块和模块之间的交流,耦合性还是不能很好的解决
有时间会写一套广播机制,配合着使用,已达到尽可能解耦的效果