单例脚本
Singleton.cs
using System;
/// <summary>
///
/// 使用方法:
/// 1.需要单例的类继承Singleton<T>
/// 例如:class UIManager:Singleton<UIManager>
///
/// 2.在代码内调用一次 UIManager.Init();
/// 之后即可使用UIManager.Instance调用单例
///
/// 3.Panel预制物需添加Canvas Group组件
///
/// </summary>
/// <typeparam name="T"></typeparam>
public class Singleton<T> where T : new() {
private static T singleton = default;
private static readonly object monitorLock = new object();
public static T Instance {
get {
if (singleton == null) {
//Monitor Class:提供同步访问对象的机制。
//获取指定对象上的排他锁
System.Threading.Monitor.Enter(monitorLock); //加锁防止多线程创建单例
//如果另一个线程的对象已执行了Enter但尚未执行相应Exit,当前线程会阻塞,直到另一个线程释放对象。
try {
if (singleton == null) {
//创建单例的实例
//使用无参数构造函数创建由指定泛型类型参数指定的类型的实例。
singleton = ((default(T) == null) ? Activator.CreateInstance<T>() : default);
}
}
finally {
//释放指定对象上的排他锁
System.Threading.Monitor.Exit(monitorLock);
}
}
return singleton;
}
}
public static T Init() {
return Instance;
}
}
简单UI框架
- UIFroms:存储所有UIFormd的路径信息,用于从Resources中动态加载。
- UIType:存储UIPrefab的路径和名字
- BaseView:定义UI的各种行为,OnOpen,OnPause,OnResume,OnExit
- UIManager:UI框架的主要逻辑,创建和销毁UI
框架使用方法:
- 创建GameRoot脚本,在Awake()或Start()中调用:
//初始化UIManager单例
UIManager.Init();
//创建第一个界面
UIManager.Instance.OpenUI(UIFroms.panel_1);
- 创建UIFroms中的界面的预制体,并创建相应的XXXView脚本,继承BaseView,即可在其中重写BaseView的方法
public class PanelView : BaseView
{
//将此方法绑定在该界面的按钮上
public void OpenPlane2() {
UIManager.Instance.OpenUI(UIFroms.panel_2);
}
}
代码
UIType.cs
public class UIType {
/// <summary>
/// 每个字段,都有两个权限:读和写
/// </summary>
public string Path { get; set; }
public string Name { get; private set; }
public UIType(string _Path) {
Path = _Path;
Name = Path.Substring(Path.LastIndexOf('/') + 1);
}
}
UIFroms.cs
/// <summary>
/// 这个脚本中,存放了所有UIForm的路径信息,是一个数据类
///
/// 当我创建了一个Panel,就把这个panel的路径信息写进来
///
/// </summary>
public class UIFroms {
//static 为了方便调用。 readonly为了防止数据被修改
public static readonly UIType panel_1 = new UIType("UIPrefabs/Panel1");
public static readonly UIType panel_2 = new UIType("UIPrefabs/Panel2");
}
BaseView.cs
using UnityEngine;
public class BaseView : MonoBehaviour {
public UIType Type {
get;
private set;
}
/// <summary>
/// 被打开时:生成后,入栈时调用
/// </summary>
public virtual void OnOpen(UIType _type) {
Type = _type;
}
/// <summary>
/// 被暂停:当有新的UI入栈时,被调用
/// </summary>
public virtual void OnPause() {
CanvasGroup cg = gameObject.GetComponent<CanvasGroup>();
if (cg==null) {
cg.gameObject.AddComponent<CanvasGroup>();
}
cg.blocksRaycasts = false;
}
/// <summary>
/// 重新被激活:当上层UI出栈时,被调用
/// </summary>
public virtual void OnResume() {
CanvasGroup cg = gameObject.GetComponent<CanvasGroup>();
if (cg == null) {
cg.gameObject.AddComponent<CanvasGroup>();
}
cg.blocksRaycasts = true;
}
/// <summary>
/// 当被销毁时:出栈时调用
/// </summary>
public virtual void OnExit() {
Destroy(gameObject);
}
}
UIManager.cs
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 存放UI框架的逻辑
/// </summary>
public class UIManager:Singleton<UIManager> {
/// <summary>
/// 生成的UI对象
/// </summary>
public Stack<BaseView> uiViewsStack = new Stack<BaseView>();
/// <summary>
/// 所有生成的UI物体
/// </summary>
public Dictionary<UIType, GameObject> allUIFormsDic = new Dictionary<UIType, GameObject>();
private Transform canvas = null;
public UIManager() {
if (canvas == null) {
canvas = GameObject.Find("Canvas").transform;
}
}
/// <summary>
/// 打开一个UI,入栈
/// 伪代码:算法描述
/// 如果栈中没有元素,那么调用自己的OnOpen
/// 如果已经已经有了元素,先调用上一个元素的OnPause,再调用这个的OnOpen
/// 入栈
///
/// </summary>
public void OpenUI(UIType uiType) {
BaseView thisView = GetUI(uiType);
if (uiViewsStack.Count != 0) {
uiViewsStack.Peek().OnPause();
}
thisView.OnOpen(uiType);
uiViewsStack.Push(thisView);
/*GB
if (uiViewsStack.Count==0) {
thisView.OnOpen(uiType);
uiViewsStack.Push(thisView);
return;
}
BaseView lastView = uiViewsStack.Peek();
lastView.OnPause();
thisView.OnOpen(uiType);
uiViewsStack.Push(thisView);
*/
}
/// <summary>
/// 销毁一个UI,出栈
/// 做一个安全性判断:
/// 先判断,栈中是否有元素,没有,直接返回,不做操作
/// 有元素,从栈中移除,执行OnExit(),销毁这个物体
/// 判断栈中是否还有元素
/// 如果没有,跳过
/// 如果有,渠道栈顶元素,执行OnResume()
/// </summary>
public void CloseUI() {
if (uiViewsStack.Count != 0) {
BaseView firstView = uiViewsStack.Pop();
firstView.OnExit();
DestroyUI(firstView.Type); //销毁栈顶元素
}
if (uiViewsStack.Count != 0) {
BaseView secondView = uiViewsStack.Peek();
secondView.OnResume();
}
/*GB
if (uiViewsStack.Count == 0) {
return;
}
BaseView firstView = uiViewsStack.Pop();//要销毁的栈顶元素
firstView.OnExit();
DestroyUI(firstView.Type); //销毁栈顶元素
if (uiViewsStack.Count == 0) {
return;
}
BaseView secondView = uiViewsStack.Peek();
secondView.OnResume();
*/
}
/// <summary>
/// 获取一个UI
/// 从字典中检测这个类型的UI是否存在
/// 如果不存在,生成一个新的,返回,并把这个UI存到字典中
/// 如果存在,直接返回
/// </summary>
/// <param name="uiType"></param>
/// <returns></returns>
public BaseView GetUI(UIType uiType) {
//不存在
if (allUIFormsDic.ContainsKey(uiType) == false || allUIFormsDic[uiType] == null) {
GameObject go = GameObject.Instantiate(Resources.Load<GameObject>(uiType.Path), canvas);
go.transform.localScale = Vector3.one*0.5f;
allUIFormsDic[uiType]=go;
return go.GetComponent<BaseView>();
}
return allUIFormsDic[uiType].GetComponent<BaseView>();
}
/// <summary>
/// 从字典中删除UI
/// </summary>
public void DestroyUI(UIType type) {
if (allUIFormsDic.ContainsKey(type)) {
if (allUIFormsDic[type] != null) {
GameObject.Destroy(allUIFormsDic[type].gameObject);
}
//从字典中删除
allUIFormsDic.Remove(type);
return;
}
}
}
使用
- 挂在在空物体上
GameRoot.cs
using UnityEngine;
public class GameRoot : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Init();
}
public void Init() {
UIManager.Init();
UIManager.Instance.OpenUI(UIFroms.panel_1);
}
}
- 挂在在界面预制物上
PanelView.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PanelView : BaseView {
public void OpenPlane1() {
UIManager.Instance.OpenUI(UIFroms.panel_1);
}
}
- Panel预制物需添加Canvas Group组件