UI分为三个类型,正常显示,显示后隐藏其他所有UI,和反向切换,我用模板设计出这三个类型接口,将公共的显示与隐藏,释放,初始化,更新等公共接口抽象于父类IUserInterface,UISystem是管理容器,对所有生成的UIFrm进行资源管理,UITool提供帮助方法,UIMaskMgr控制弹窗的遮罩。UIFactory是创建UIFrm工厂,通常都是创建出来建造然后交给System管理。
可扩展,若有不同类型的UIFrm,可继承IUserInterface重写逻辑,避免修改其他类。
内存释放,交给UISystem管理类来单独释放某个UIFrm的资源或者全部释放,释放某个UIFrm的时候会销毁3d成像和代码引用。同时强转垃圾回收。
使用:利用泛型创建panel,同时传递动态参数,将显示与UI界面分离,即UI界面不挂任何一个脚本,UISystem内控制显示Panel的更新,可以自己改写一个游戏循环框架,配合UI。
文章目录
- IUserInterface
- INormalUserInterface
- IHideOtherUserInterface
- IReverseChangeUserInterface
- UIMaskMgr
- UIFactory
- UISystem
- UITool
- CanvasScript
- 结果展示
- 使用
IUserInterface
using ABFW;
using System.Dynamic;
using UnityEngine;
namespace UIFW
{
/// <summary>
/// UI界面
/// 模板设计模式:接口控制流程
/// </summary>
public abstract class IUserInterface
{
public string ResName; //资源名称
protected UISystem _UISystem; //UIContext
protected Vector3 _SpawnPosition = Vector3.zero; //生成位置
//3D成像
protected GameObject _GameObject;
public IUserInterface()
{
}
/// <summary>
/// UI界面初始化
/// </summary>
public virtual void Initialize()
{
}
/// <summary>
/// UI逻辑更新
/// </summary>
public virtual void Update()
{
}
/// <summary>
/// UI释放
/// </summary>
public virtual void Release()
{
//删除3D成像
GameObject.Destroy(_GameObject);
//释放prefab的内存镜像资源。
GameManager.Instance.ReleaseResource(ResName);
}
/// <summary>
/// 设置3D成像
/// </summary>
/// <param name="_gameobject"></param>
public virtual void SetObject(GameObject _gameObject)
{
_GameObject = _gameObject;
}
/// <summary>
/// 设置管理上下文
/// </summary>
/// <param name="uiSystem"></param>
public void AddContext(UISystem uiSystem)
{
_UISystem = uiSystem;
}
/// <summary>
/// 窗体的显示
/// </summary>
public virtual void Display()
{
_GameObject.SetActive(true);
}
/// <summary>
/// 窗体的隐藏
/// </summary>
public virtual void Hide()
{
_GameObject.SetActive(false);
}
/// <summary>
/// 窗体的打开
/// </summary>
public abstract void Open();
/// <summary>
/// 窗体的关闭
/// </summary>
public abstract void Close();
/// <summary>
/// 窗体是否打开
/// </summary>
/// <returns></returns>
public virtual bool IsOpen()
{
return _GameObject.activeSelf;
}
}
}
INormalUserInterface
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace UIFW
{
public class INormalUserInterface : IUserInterface
{
/// <summary>
/// Normal的父节点:可能为Fixed或者Normal
/// </summary>
protected Transform TraParent;
/// <summary>
/// 设置3D成像
/// </summary>
/// <param name="_gameObject"></param>
public override void SetObject(GameObject _gameObject)
{
base.SetObject(_gameObject);
_GameObject.transform.SetParent(TraParent, false);
}
public override void Close()
{
Hide();
if (base._UISystem.DicCurShowUIFrm.ContainsKey(ResName))
_UISystem.DicCurShowUIFrm.Remove(ResName);
}
public override void Open()
{
Display();
if (!base._UISystem.DicCurShowUIFrm.ContainsKey(ResName))
{
_UISystem.DicCurShowUIFrm[ResName] = this;
}
}
public override void Release()
{
if (base._UISystem.DicCurShowUIFrm.ContainsKey(ResName))
_UISystem.DicCurShowUIFrm.Remove(ResName);
base.Release();
}
}
}
IHideOtherUserInterface
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UIFW;
using UnityEngine;
namespace UIFW
{
public class IHideOtherUserInterface : IUserInterface
{
/// <summary>
/// 设置3D成像
/// </summary>
/// <param name="_gameObject"></param>
public override void SetObject(GameObject _gameObject)
{
base.SetObject(_gameObject);
_GameObject.transform.SetParent(_UISystem.TraNormal, false);
}
public override void Close()
{
IUserInterface ui;
Hide();
if (_UISystem.DicCurShowUIFrm.ContainsKey(ResName))
_UISystem.DicCurShowUIFrm.Remove(ResName);
//显示Normal
foreach (var item in _UISystem.DicCurShowUIFrm)
{
item.Value.Display();
}
//显示PopUp
ui = _UISystem.StackUIFrm.Peek();
ui.Display();
}
public override void Open()
{
IUserInterface ui;
//隐藏Normal
foreach (var item in _UISystem.DicCurShowUIFrm)
{
item.Value.Hide();
}
//隐藏PopUp
ui = _UISystem.StackUIFrm.Peek();
ui.Hide();
//显示自身
Display();
if (!_UISystem.DicCurShowUIFrm.ContainsKey(ResName))
_UISystem.DicCurShowUIFrm.Add(ResName, this);
}
public override void Release()
{
if (!_UISystem.DicCurShowUIFrm.ContainsKey(ResName))
_UISystem.DicCurShowUIFrm.Remove(ResName);
base.Release();
}
}
}
IReverseChangeUserInterface
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace UIFW
{
public class IReverseChangeUserInterface : IUserInterface
{
/// <summary>
/// 设置3D成像
/// </summary>
/// <param name="_gameObject"></param>
public override void SetObject(GameObject _gameObject)
{
base.SetObject(_gameObject);
_GameObject.transform.SetParent(_UISystem.TraPopUp, false);
}
/// <summary>
/// 关闭
/// </summary>
public override void Close()
{
if (!IsOpen())
{
Debug.LogWarning($"这个窗体还没有打开,不需要关闭!{this.GetType().Name}");
return;
}
//关闭遮罩
_UISystem.UIMaskMgr.CancelMaskWindow();
//清除栈顶元素
IUserInterface userInterface = base._UISystem.StackUIFrm.Pop();
if (userInterface != this)
{
Debug.LogError("当前PopUp不是栈顶元素无法关闭!Close");
return;
}
userInterface.Hide();
//重绘栈顶元素
if (base._UISystem.StackUIFrm.Count > 0)
{
userInterface = base._UISystem.StackUIFrm.Peek();
userInterface.Display();
}
}
/// <summary>
/// 打开
/// </summary>
public override void Open()
{
IUserInterface ui;
//需要显示的场景
if (_UISystem.StackUIFrm.Count() > 0)
{
ui = _UISystem.StackUIFrm.Peek();
ui.Hide();
}
else
{
//打开遮罩
_UISystem.UIMaskMgr.SetMaskWindow(_GameObject);
}
//把栈顶元素隐藏
if (_UISystem.StackUIFrm.Contains(this))
{
Stack<IUserInterface> tmpS = new Stack<IUserInterface>();
while (this != _UISystem.StackUIFrm.Peek())
{
tmpS.Push(_UISystem.StackUIFrm.Pop());
}
//找到自身
_UISystem.StackUIFrm.Pop();
//把tmps中的元素重新加会stack中
while (tmpS.Count() > 0)
{
ui = tmpS.Pop();
_UISystem.StackUIFrm.Push(ui);
}
//把自身换到最顶层
_UISystem.StackUIFrm.Push(this);
}
else
{
//加入栈顶元素
_UISystem.StackUIFrm.Push(this);
}
//显示自身元素
Display();
}
public override void Release()
{
//删除缓存中元素
if (base._UISystem.StackUIFrm.Contains(this))
{
Stack<IUserInterface> tmps = new Stack<IUserInterface>();
while (base._UISystem.StackUIFrm.Peek() != this)
{
tmps.Push(base._UISystem.StackUIFrm.Pop());
}
base._UISystem.StackUIFrm.Pop();
while (tmps.Count() > 0)
{
_UISystem.StackUIFrm.Push(tmps.Pop());
}
}
base.Release();
}
}
}
UIMaskMgr
using System;
using System.Collections.Generic;
using System.Text;
using UIFW;
using UnityEngine;
using UnityEngine.UI;
/***
* Title:
UI遮罩管理器
* Description:
* 控制启用遮罩和关闭遮罩的功能
*
*/
namespace UIFW
{
public class UIMaskMgr
{
/// <summary>
/// 节点
/// </summary>
GameObject UIMask; //遮罩的物体
public UIMaskMgr(GameObject uiMask)
{
//获得UIMask
UIMask = uiMask;
}
public void SetMaskWindow(GameObject UIForm)
{
UIMask.SetActive(true);
//PopUp UIForm下移
UIForm.transform.SetAsLastSibling();
}
public void CancelMaskWindow()
{
//隐藏UiMask
if (UIMask.activeInHierarchy)
UIMask.SetActive(false);
}
}
}
UIFactory
using ABFW;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace UIFW
{
/// <summary>
/// 创建UI Panel对象
/// </summary>
public class UIFactory
{
private UISystem _UISystem;
public UIFactory(UISystem uisystem)
{
this._UISystem = uisystem;
}
/// <summary>
/// 创建一个UI窗体
/// </summary>
/// <typeparam name="T">UI窗体类型</typeparam>
/// <param name="obj">Model</param>
/// <returns></returns>
public IUserInterface GetUIPanel<T>() where T:IUserInterface,new()
{
T panel = new T();
//建造3D成像
GameObject prefab = GameManager.Instance.Asset.LoadAsset<GameObject>(panel.ResName);
Debug.Log($"加载UI Prefab={prefab.name}");
//设置上下文
panel.AddContext(_UISystem);
//实例化
GameObject RootUI = GameObject.Instantiate(prefab);
panel.SetObject(RootUI);
//新UI的初始化
panel.Initialize();
return panel;
}
}
}
UISystem
using ABFW;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UIFW;
using UnityEngine;
namespace UIFW
{
/// <summary>
/// UI框架管理容器
/// 管理所有已加载UIFrm
/// </summary>
public class UISystem
{
private static UISystem _Instance;
public static UISystem Instance
{
get
{
if (_Instance == null)
_Instance = new UISystem();
return _Instance;
}
}
//工厂
private UIFactory _Factory;
//Canvas
public Transform TraCanvas;
//Normal
public Transform TraNormal;
//Fixed
public Transform TraFixed;
//PopUp
public Transform TraPopUp;
//UIMask
public UIMaskMgr UIMaskMgr;
//是否处于更新UI逻辑之中
public bool IsUpdate;
//所有UIFrm
public Dictionary<string, IUserInterface> DicAllUIFrm;
//用于添加新的UIFrm,确保在UI逻辑更新完成时添加进DicAllUIFrm中
public Dictionary<string, IUserInterface> DicAllUIFrmTmp;
//用于删除UIFrm的缓存容器
public Dictionary<string, IUserInterface> DicDelUIFrmTmp;
//当前显示在游戏中的UIFrm
public Dictionary<string, IUserInterface> DicCurShowUIFrm;
//堆栈中的UIFrm
public Stack<IUserInterface> StackUIFrm;
public UISystem()
{
//加载Canvas
TraCanvas = LoadCanvas();
Debug.Log("获得TraCanvas" + TraCanvas.name);
//获得Normal
TraNormal = UITool.FindChildNode(TraCanvas, "Normal");
Debug.Log("获得TraNormal" + TraNormal.name);
//获得Fixed
TraFixed = UITool.FindChildNode(TraCanvas, "Fixed");
Debug.Log("获得TraFixed" + TraFixed.name);
//获得PopUp
TraPopUp = UITool.FindChildNode(TraCanvas, "PopUp");
Debug.Log("获得TraPopUp" + TraPopUp.name);
//获得UIMask
GameObject maskObj = UITool.FindChildNode(TraCanvas, "UIMask").gameObject;
UIMaskMgr = new UIMaskMgr(maskObj);
//获得工厂
_Factory = new UIFactory(this);
//集合分配内存空间
IsUpdate = false;
DicAllUIFrm = new Dictionary<string, IUserInterface>();
DicAllUIFrmTmp = new Dictionary<string, IUserInterface>();
DicDelUIFrmTmp = new Dictionary<string, IUserInterface>();
DicCurShowUIFrm = new Dictionary<string, IUserInterface>();
StackUIFrm = new Stack<IUserInterface>();
}
/// <summary>
/// 加载Canvas
/// </summary>
/// <returns></returns>
public Transform LoadCanvas()
{
GameObject prefab = Asset.Instance.LoadAsset<GameObject>("Canvas");
GameObject go = GameObject.Instantiate(prefab);
Asset.Instance.ReleaseResources("Canvas");
return go.transform;
}
/// <summary>
/// 打开一个UI窗体
/// </summary>
/// <param name="uiFrmName"></param>
/// <returns></returns>
public IUserInterface OpenUIFrm<T>() where T:IUserInterface,new()
{
IUserInterface userInterface = null;
//key
string key = typeof(T).Name;
Debug.Log("开启:"+key);
//从缓存中查询
if(DicAllUIFrm.ContainsKey(key))
{
userInterface = DicAllUIFrm[key];
//UI资源已经加载在游戏中,只需要显示即可
if(userInterface.IsOpen())
{
Debug.LogWarning($"UI已经处于显示中...{key}");
}
else
userInterface.Open();
Debug.Log("UI项目已经加载在游戏中了");
return userInterface;
}
//获得Panel
userInterface = _Factory.GetUIPanel<T>();
//开启UIPanel
userInterface.Open();
//加入缓存
if (!IsUpdate)
DicAllUIFrm[key] = userInterface;
else
DicAllUIFrmTmp[key] = userInterface;
return userInterface;
}
/// <summary>
/// 关闭UIFrm
/// </summary>
/// <param name="userInterface"></param>
public void CloseUIFrm<T>()
{
string key = typeof(T).Name;
IUserInterface userInterface = null;
Debug.Log($"关闭:{key}");
if (DicAllUIFrm.ContainsKey(key))
userInterface = DicAllUIFrm[key];
else if (DicAllUIFrmTmp.ContainsKey(key))
userInterface = DicAllUIFrmTmp[key];
if(userInterface==null)
{
Debug.LogWarning($"这个资源并没有开启,未加载进缓存中,不需要关闭{key}");
return;
}
if (!userInterface.IsOpen())
{
Debug.LogWarning("这个资源并没有开启,不需要关闭");
return;
}
userInterface.Close();
}
/// <summary>
/// 卸载某个指定的UIFrm
/// </summary>
/// <param name="userInterface"></param>
public void ReleaseUIFrm<T>()
{
string key = typeof(T).Name;
Debug.Log("释放:" + key);
IUserInterface userInterface = null;
if (DicAllUIFrm.ContainsKey(key))
{
userInterface = DicAllUIFrm[key];
//加入删除缓存
DicDelUIFrmTmp.Add(key, userInterface);
}
else if (DicAllUIFrmTmp.ContainsKey(key))
{
userInterface = DicAllUIFrmTmp[key];
DicAllUIFrmTmp.Remove(key);
}
if (userInterface == null)
{
Debug.LogWarning($"这个资源未加载进缓存中,不需要释放{key}");
return;
}
userInterface.Close();
userInterface.Release();
}
/// <summary>
/// 更新界面UI逻辑
/// </summary>
public void Update()
{
//以当前场景下还存在显示的UI物体来判断需要显示的UI,不能根据字典DicAllUIFrm来更新UI,字典会动态变化
//更新场景下的UI
IsUpdate = true;
foreach (var kv in DicAllUIFrm)
{
if (kv.Value.IsOpen())
kv.Value.Update();
}
IsUpdate = false;
//检查挂起容器内是否有新加入的UI,添加到当前UI中
foreach (var kv in DicAllUIFrmTmp)
{
DicAllUIFrm.Add(kv.Key, kv.Value);
}
DicAllUIFrmTmp.Clear();
//检查删除容器
foreach (var kv in DicDelUIFrmTmp)
{
DicAllUIFrm.Remove(kv.Key);
}
DicDelUIFrmTmp.Clear();
}
/// <summary>
/// 卸载全部资源
/// </summary>
public void ReleaseAll()
{
//清除场景引用
foreach (var item in DicAllUIFrm)
{
DicDelUIFrmTmp.Add(item.Key, item.Value);
item.Value.Release();
}
foreach (var item in DicAllUIFrmTmp)
{
item.Value.Release();
}
DicAllUIFrmTmp.Clear();
//清除Canvas
GameObject.Destroy(TraCanvas.gameObject);
//清除代码引用
DicAllUIFrmTmp.Clear();
TraCanvas = null;
TraNormal = null;
TraFixed = null;
TraPopUp = null;
//资源回收
Resources.UnloadUnusedAssets();
}
}
}
UITool
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace UIFW
{
/// <summary>
/// 工具人
/// </summary>
public class UITool
{
/// <summary>
/// 根据父节点和子节点名称来查找到子节点
/// </summary>
/// <param name="parent">父节点</param>
/// <param name="childName">子节点名字</param>
/// <returns></returns>
public static Transform FindChildNode(Transform parent, string childName)
{
Transform target = null;
target = parent.Find(childName);
if (target == null)
{
foreach (Transform childTran in parent)
{
if (childTran != parent)
{
target = FindChildNode(childTran, childName);
if (target != null) //这一句很关键,如果搜索出来的target等于null了,返回了就不会继续搜索了
return target;
}
}
}
return target;
}
/// <summary>
/// 根据父节点和子节点名字来返回T类型的脚本组件
/// Button
/// </summary>
/// <typeparam name="T">需要返回的脚本类型</typeparam>
/// <param name="parent">父节点</param>
/// <param name="chlldName">子节点名字</param>
/// <returns></returns>
public static T GetComponentFromChildNode<T>(Transform parent, string childName) where T : Component
{
T res = null;
Transform childTran = FindChildNode(parent, childName);
if (childTran != null)
{
res = childTran.GetComponent<T>();
}
return res;
}
}
}
CanvasScript
结果展示
关闭PopUp会重绘栈顶元素,关闭HideOther会重绘其他正常显示的元素。使用了我前面自己写的Assetbundle框架来加载元素。
使用
根据泛型来Open Panel同时可以传递动态Model参数供给View初始化的时候使用。
不同类型的UIFrm分别继承不同的接口即可,按需求更改3D成像的Prefab和显示位置和父亲结点。有需要传递额外模型数据的话,这样。