基于ET框架和UGUI的简单UI框架实现(转)
前言
此框架对应Et2.0版本,目前已过时!请跳转到最新的et3.3版本:http://www.tinkingli.com/?p=270
框架介绍
本框架特点:
1、层级分明,本框架把UI分成5层,层级依次递增,在Unity面板设置好属性后自动加载到该层
2、关闭界面不直接销毁UI物体,而是调用Close方法,并把界面移到UIHiden层进行隐藏,等待下次Show调用,这样可以避免重复加载和实例化UI物体,当你部分UI界面可能出现多次关闭和打开操作时,能减轻运行压力
3、只需进行一次获取引用操作,一般我们会在Awake中通过ReferenceCollector获取需要引用到的物体(例如:返回按钮,输入文本框等),而Show中负责展示界面逻辑(文本框内容初始化,弹出动画等),因为Awake只会调用一次,而Show则每次打开都会调用,所以能减少GetComponent,以及从ReferenceCollector中取出物体的调用,节省性能的开销
4、拓展方便,假如想要拓展什么想要的功能,大部分都可直接在UIBaseComponent中直接修改即可(下面实例拓展了两个我用到的事件OnCloseOneTime和OnClose,大家用不到可直接去掉)
5、当然也可以手动调用UIComponent.Remove()方法进行真正的移除UI操作(直接销毁物体),有需要的也可以自行实现一个定时卸载操作,就是关闭(Close)一定时间后没有再打开(Show)的UI实行移除(Remove),节约内存
Unity工程中的修改
namespace Model
{
//窗体的层级类型
public enum WindowLayer
{
UIHiden = 0,
Bottom = 1,
Medium = 2,
Top = 3,
TopMost = 4,
}
}
public class CanvasConfig: MonoBehaviour
{
public string CanvasName;
public WindowLayer UiWindowLayer = WindowLayer.Medium;
}
UIComponent中的修改(Hotfix工程)
//初始化UI层级
private void InstantiateUi(Transform parent)
{
WindowLayer[] _names = new WindowLayer[] {
WindowLayer.UIHiden,
WindowLayer.Bottom,
WindowLayer.Medium,
WindowLayer.Top,
WindowLayer.TopMost
};
Camera _cam = new GameObject().AddComponent<Camera>();
_cam.clearFlags = CameraClearFlags.Depth;
_cam.cullingMask = 1 << LayerMask.NameToLayer("UI");
_cam.orthographic = true;
_cam.depth = 10;
_cam.name = "UiCamera";
_cam.transform.SetParent(parent);
_cam.transform.localPosition = Vector3.zero;
foreach (var layer in _names)
{
var it = layer.ToString();
GameObject _go = new GameObject();
this.m_allLayers.Add(layer, _go);
Canvas _canvas = _go.AddComponent<Canvas>();
_canvas.renderMode = RenderMode.ScreenSpaceCamera;
_canvas.worldCamera = _cam;
_canvas.sortingOrder = (int)layer;
CanvasScaler _scale = _go.AddComponent<CanvasScaler>();
_scale.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
_scale.referenceResolution = new Vector2(1920, 1080);
_scale.matchWidthOrHeight = 1;
GraphicRaycaster _graphic = _go.AddComponent<GraphicRaycaster>();
_go.name = it;
_go.transform.SetParent(parent);
_go.transform.localPosition = Vector3.zero;
if (layer == WindowLayer.UIHiden)
{
_go.layer = LayerMask.NameToLayer("UIHiden");
_graphic.enabled = false;
}
else
{
_go.layer = LayerMask.NameToLayer("UI");
}
}
}
//修改UI层级
private void SetViewParent(UI ui, WindowLayer layer)
{
RectTransform _rt = ui.GameObject.GetComponent<RectTransform>();
_rt.SetParent(m_allLayers[layer].transform);
_rt.anchorMin = Vector2.zero;
_rt.anchorMax = Vector2.one;
_rt.offsetMax = Vector2.zero;
_rt.offsetMin = Vector2.zero;
_rt.pivot = new Vector2(0.5f, 0.5f);
_rt.localScale = Vector3.one;
_rt.localPosition = Vector3.zero;
_rt.localRotation = Quaternion.identity;
}
public void Awake()
{
this.Root = GameObject.Find("Global/UI/");
this.InstantiateUi(Root.transform);
this.Load();
}

public UI Create(UIType type)
{
try
{
UI ui;
if (uis.ContainsKey(type))
{
ui = uis[type];
}
else
{
ui = UiTypes[type].Create(this.GetParent<Scene>(), type, Root);
uis.Add(type, ui);
}
// 设置Ui层级
SetViewParent(ui, ui.GameObject.GetComponent<CanvasConfig>().UiWindowLayer);
//调用Show方法
ui.UiComponent.Show();
return ui;
}
catch (Exception e)
{
throw new Exception($"{type} UI 错误: {e.ToStr()}");
}
}
public void Close(UIType type)
{
UI ui;
if (!uis.TryGetValue(type, out ui))
{
return;
}
uis[type].UiComponent.Close();
SetViewParent(uis[type], WindowLayer.UIHiden);
}
其他修改
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hotfix
{
public abstract class UIBaseComponent:Component
{
//单次关闭界面调用,调用后清空
public event Action OnCloseOneTime;
//每次关闭界面调用,调用后不清空
public event Action OnClose;
public virtual void Show()
{
GetParent<UI>().GameObject.SetActive(true);
}
public virtual void Close()
{
GetParent<UI>().GameObject.SetActive(false);
if (OnCloseOneTime!=null)
{
OnCloseOneTime.Invoke();
OnCloseOneTime = null;
}
OnClose?.Invoke();
}
}
}
public UIBaseComponent UiComponent { get; private set; }
/// <summary>
/// 添加主UI组件,继承自UIBaseComponent
/// </summary>
/// <typeparam name="K"></typeparam>
/// <returns></returns>
public K AddUiComponent<K>() where K : UIBaseComponent, new()
{
UiComponent = this.AddComponent<K>();
return (K) UiComponent;
}
public K AddUiComponent<K, P1>(P1 p1) where K : UIBaseComponent, new()
{
UiComponent = this.AddComponent<K, P1>(p1);
return (K)UiComponent;
}
public K AddUiComponent<K, P1, P2>(P1 p1, P2 p2) where K : UIBaseComponent, new()
{
UiComponent = this.AddComponent<K, P1, P2>(p1,p2);
return (K)UiComponent;
}
public K AddUiComponent<K, P1, P2, P3>(P1 p1, P2 p2, P3 p3) where K : UIBaseComponent, new()
{
UiComponent = this.AddComponent<K, P1, P2, P3>(p1,p2,p3);
return (K)UiComponent;
}
public override void Dispose()
{
if (this.Id == 0)
{
return;
}
base.Dispose();
//设置为空
UiComponent = null;
foreach (UI ui in this.children.Values)
{
ui.Dispose();
}
UnityEngine.Object.Destroy(GameObject);
children.Clear();
}
public class UITestComponent: UIBaseComponent
{
public static AccountPanelClose OnAccountPanelClose;
public static UIAccountInfo Info;
private Button _backBtn;//返回按钮
//只在初始化的时候调用一次,用于获取各种引用
public void Awake()
{
ReferenceCollector rc = this.GetParent<UI>().GameObject.GetComponent<ReferenceCollector>();
//获取返回按钮引用
_backBtn = rc.GetUnityComponent<Button>("backBtn");
_backBtn.onClick.AddListener(OnClickBack);
}
//每次弹出该UI调用一次
public override void Show()
{
base.Show();
}
//每次关闭该UI调用一次
public override void Close()
{
base.Close();
}
//点击返回
public void OnClickBack()
{
Hotfix.Scene.GetComponent<UIComponent>().Close(UIType.UITest);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Model;
using UnityEngine;
namespace Hotfix
{
[UIFactory((int)UIType.UIAccount)]
public class UITestFactory : IUIFactory
{
public UI Create(Scene scene, UIType type, GameObject gameObject)
{
try
{
ResourcesComponent resourcesComponent = Game.Scene.GetComponent<ResourcesComponent>();
resourcesComponent.LoadBundle($"{type}.unity3d");
GameObject bundleGameObject = resourcesComponent.GetAsset<GameObject>($"{type}.unity3d", $"{type}");
GameObject newUi = UnityEngine.Object.Instantiate(bundleGameObject);
newUi.layer = LayerMask.NameToLayer(LayerNames.UI);
UI ui = EntityFactory.Create<UI, Scene, UI, GameObject>(scene, null, newUi);
//调用前面添加的方法,UITestComponent继承自UIBaseComponent
ui.AddUiComponent<UITestComponent>();
return ui;
}
catch (Exception e)
{
Log.Error(e.ToStr());
return null;
}
}
public void Remove(UIType type)
{
Game.Scene.GetComponent<ResourcesComponent>().UnloadBundle($"{type}.unity3d");
}
}
}
总结
附录(补充两个修改后的类)
using System;
using System.Collections.Generic;
using System.Linq;
using Model;
using UnityEngine;
using UnityEngine.UI;
namespace Hotfix
{
[ObjectEvent]
public class UIComponentEvent : ObjectEvent<UIComponent>, IAwake, ILoad
{
public void Awake()
{
this.Get().Awake();
}
public void Load()
{
this.Get().Load();
}
}
/// <summary>
/// 管理所有UI
/// </summary>
public class UIComponent: Component
{
private GameObject Root;
private Dictionary<UIType, IUIFactory> UiTypes;
private readonly Dictionary<UIType, UI> uis = new Dictionary<UIType, UI>();
private Dictionary<WindowLayer, GameObject> m_allLayers = new Dictionary<WindowLayer, GameObject>();
public override void Dispose()
{
if (Id == 0)
{
return;
}
base.Dispose();
foreach (UIType type in uis.Keys.ToArray())
{
UI ui;
if (!uis.TryGetValue(type, out ui))
{
continue;
}
uis.Remove(type);
ui.Dispose();
}
}
public void Awake()
{
this.Root = GameObject.Find("Global/UI/");
this.InstantiateUi(Root.transform);
this.Load();
}
public void Load()
{
UiTypes = new Dictionary<UIType, IUIFactory>();
Type[] types = DllHelper.GetHotfixTypes();
foreach (Type type in types)
{
object[] attrs = type.GetCustomAttributes(typeof (UIFactoryAttribute), false);
if (attrs.Length == 0)
{
continue;
}
UIFactoryAttribute attribute = attrs[0] as UIFactoryAttribute;
if (UiTypes.ContainsKey((UIType)attribute.Type))
{
Log.Debug($"已经存在同类UI Factory: {attribute.Type}");
throw new Exception($"已经存在同类UI Factory: {attribute.Type}");
}
object o = Activator.CreateInstance(type);
IUIFactory factory = o as IUIFactory;
if (factory == null)
{
Log.Error($"{o.GetType().FullName} 没有继承 IUIFactory");
continue;
}
this.UiTypes.Add((UIType)attribute.Type, factory);
}
}
#region 修改
private void InstantiateUi(Transform parent)
{
WindowLayer[] _names = new WindowLayer[] {
WindowLayer.UIHiden,
WindowLayer.Bottom,
WindowLayer.Medium,
WindowLayer.Top,
WindowLayer.TopMost
};
Camera _cam = new GameObject().AddComponent<Camera>();
_cam.clearFlags = CameraClearFlags.Depth;
_cam.cullingMask = 1 << LayerMask.NameToLayer("UI");
_cam.orthographic = true;
_cam.depth = 10;
_cam.name = "UiCamera";
_cam.transform.SetParent(parent);
_cam.transform.localPosition = Vector3.zero;
foreach (var layer in _names)
{
var it = layer.ToString();
GameObject _go = new GameObject();
this.m_allLayers.Add(layer, _go);
Canvas _canvas = _go.AddComponent<Canvas>();
_canvas.renderMode = RenderMode.ScreenSpaceCamera;
_canvas.worldCamera = _cam;
_canvas.sortingOrder = (int)layer;
CanvasScaler _scale = _go.AddComponent<CanvasScaler>();
_scale.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
_scale.referenceResolution = new Vector2(1920, 1080);
_scale.matchWidthOrHeight = 1;
GraphicRaycaster _graphic = _go.AddComponent<GraphicRaycaster>();
_go.name = it;
_go.transform.SetParent(parent);
_go.transform.localPosition = Vector3.zero;
if (layer == WindowLayer.UIHiden)
{
//GameTool.SetLayer(_go, WindowLayer.UIHiden.ToString());
_go.layer = LayerMask.NameToLayer("UIHiden");
_graphic.enabled = false;
}
else
{
//GameTool.SetLayer(_go, "UI");
_go.layer = LayerMask.NameToLayer("UI");
}
}
}
public UI Create(UIType type)
{
try
{
UI ui;
if (uis.ContainsKey(type))
{
ui = uis[type];
}
else
{
ui = UiTypes[type].Create(this.GetParent<Scene>(), type, Root);
uis.Add(type, ui);
}
// 设置canvas
SetViewParent(ui, ui.GameObject.GetComponent<CanvasConfig>().UiWindowLayer);
ui.UiComponent.Show();
return ui;
}
catch (Exception e)
{
throw new Exception($"{type} UI 错误: {e.ToStr()}");
}
}
public void Close(UIType type)
{
UI ui;
if (!uis.TryGetValue(type, out ui))
{
return;
}
uis[type].UiComponent.Close();
SetViewParent(uis[type], WindowLayer.UIHiden);
}
private void SetViewParent(UI ui, WindowLayer layer)
{
RectTransform _rt = ui.GameObject.GetComponent<RectTransform>();
_rt.SetParent(m_allLayers[layer].transform);
_rt.anchorMin = Vector2.zero;
_rt.anchorMax = Vector2.one;
_rt.offsetMax = Vector2.zero;
_rt.offsetMin = Vector2.zero;
_rt.pivot = new Vector2(0.5f, 0.5f);
_rt.localScale = Vector3.one;
_rt.localPosition = Vector3.zero;
_rt.localRotation = Quaternion.identity;
}
#endregion
public void Add(UIType type, UI ui)
{
this.uis.Add(type, ui);
}
public void Remove(UIType type)
{
UI ui;
if (!uis.TryGetValue(type, out ui))
{
return;
}
UiTypes[type].Remove(type);
uis.Remove(type);
ui.Dispose();
//SetViewParent(ui,WindowLayer.UIHiden);
}
public void RemoveAll()
{
foreach (UIType type in this.uis.Keys.ToArray())
{
UI ui;
if (!this.uis.TryGetValue(type, out ui))
{
continue;
}
this.uis.Remove(type);
ui.Dispose();
}
}
public UI Get(UIType type)
{
UI ui;
this.uis.TryGetValue(type, out ui);
return ui;
}
public List<UIType> GetUITypeList()
{
return new List<UIType>(this.uis.Keys);
}
}
}
using System.Collections.Generic;
using UnityEngine;
namespace Hotfix
{
[Model.ObjectEvent]
public class UIEvent : ObjectEvent<UI>, IAwake<Scene, UI, GameObject>
{
public void Awake(Scene scene, UI parent, GameObject gameObject)
{
this.Get().Awake(scene, parent, gameObject);
}
}
public sealed class UI: Entity
{
public Scene Scene { get; set; }
public string Name
{
get
{
return this.GameObject.name;
}
}
public GameObject GameObject { get; private set; }
public Dictionary<string, UI> children = new Dictionary<string, UI>();
#region 修改
public UIBaseComponent UiComponent { get; private set; }
/// <summary>
/// 添加主UI组件,继承自UIBaseComponent
/// </summary>
/// <typeparam name="K"></typeparam>
/// <returns></returns>
public K AddUiComponent<K>() where K : UIBaseComponent, new()
{
UiComponent = this.AddComponent<K>();
return (K) UiComponent;
}
public K AddUiComponent<K, P1>(P1 p1) where K : UIBaseComponent, new()
{
UiComponent = this.AddComponent<K, P1>(p1);
return (K)UiComponent;
}
public K AddUiComponent<K, P1, P2>(P1 p1, P2 p2) where K : UIBaseComponent, new()
{
UiComponent = this.AddComponent<K, P1, P2>(p1,p2);
return (K)UiComponent;
}
public K AddUiComponent<K, P1, P2, P3>(P1 p1, P2 p2, P3 p3) where K : UIBaseComponent, new()
{
UiComponent = this.AddComponent<K, P1, P2, P3>(p1,p2,p3);
return (K)UiComponent;
}
#endregion
public void Awake(Scene scene, UI parent, GameObject gameObject)
{
this.children.Clear();
this.Scene = scene;
if (parent != null)
{
gameObject.transform.SetParent(parent.GameObject.transform, false);
}
this.GameObject = gameObject;
}
public override void Dispose()
{
if (this.Id == 0)
{
return;
}
base.Dispose();
UiComponent = null;
foreach (UI ui in this.children.Values)
{
ui.Dispose();
}
UnityEngine.Object.Destroy(GameObject);
children.Clear();
}
public void SetAsFirstSibling()
{
this.GameObject.transform.SetAsFirstSibling();
}
public void Add(UI ui)
{
this.children.Add(ui.Name, ui);
}
public void Remove(string name)
{
UI ui;
if (!this.children.TryGetValue(name, out ui))
{
return;
}
this.children.Remove(name);
ui.Dispose();
}
public UI Get(string name)
{
UI child;
if (this.children.TryGetValue(name, out child))
{
return child;
}
GameObject childGameObject = this.GameObject.transform.Find(name)?.gameObject;
if (childGameObject == null)
{
return null;
}
child = EntityFactory.Create<UI, Scene, UI, GameObject>(this.Scene, this, childGameObject);
this.Add(child);
return child;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)