一、简介
最近在各大网站看了一下 Unity3d 的 UI 框架,各种 UI 框架已经有很多的版本了,各有千秋,有的功能虽然写的完善,但用起来太复杂,有的框架功能不完善,搞个课程就上架了,还有什么 MVC 框架,绕来绕去的看的头都大了,这些根本不想用。
于是我自己就写了一个 UI 框架,只有两个脚本,不用向 UI 预制体上挂载脚本,所有的组件访问都可以通过 UIManager 来控制,常用的几个方法:显示界面,关闭界面,查找子物体,就这么多。
二、UI 框架
下面的两个脚本是 UI 框架的核心部分,具体的用法在下面的章节有介绍
UIBase
using UnityEngine;
public class UIBase
{
#region 字段
/// <summary>
/// Prefabs路径
/// </summary>
public string PrefabsPath { get; set; }
/// <summary>
/// UI面板的名字
/// </summary>
public string UIName { get; set; }
/// <summary>
/// 当前UI所在的场景名
/// </summary>
public string SceneName { get; set; }
/// <summary>
/// Type 的全名
/// </summary>
public string FullName { get; set; }
/// <summary>
/// 当前UI的游戏物体
/// </summary>
public GameObject UIGameObject { get; set; }
#endregion
/// <summary>
/// 面板实例化时执行一次
/// </summary>
public virtual void Start() { }
/// <summary>
/// 每帧执行
/// </summary>
public virtual void Update() { }
/// <summary>
/// 当前UI面板销毁之前执行一次
/// </summary>
public virtual void Destroy() { }
/// <summary>
/// 根据名称查找一个子对象
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public GameObject GetObject(string name)
{
Transform[] trans = UIGameObject.GetComponentsInChildren<Transform>();
foreach (var item in trans)
{
if (item.name == name)
return item.gameObject;
}
Debug.LogError(string.Format("找不到名为 {0} 的子对象", name));
return null;
}
/// <summary>
/// 根据名称获取一个子对象的组件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
public T GetOrAddCommonent<T>(string name) where T : Component
{
GameObject child = GetObject(name);
if (child)
{
if (child.GetComponent<T>() == null)
child.AddComponent<T>();
return child.GetComponent<T>();
}
return null;
}
protected UIBase() { }
}
UIManager
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class UIManager : MonoBehaviour
{
public static UIManager Instance;
//存储场景中的UI信息
private Dictionary<string, UIBase> UIDic = new Dictionary<string, UIBase>();
//当前场景的 Canvas 游戏物体
private Transform CanvasTransform = null;
//当前字典中UI的个数
public int UICount
{
get { return UIDic.Count; }
}
private void Awake()
{
Instance = this;
}
private void Start()
{
}
private void Update()
{
if (UIDic.Count > 0)
{
foreach (var key in UIDic.Keys)
{
if (UIDic[key] != null)
UIDic[key].Update();
}
}
}
/// <summary>
/// 显示面板
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public UIBase ShowUI<T>() where T : UIBase
{
Type t = typeof(T);
string fullName = t.FullName;
if (UIDic.ContainsKey(fullName))
{
Debug.Log("当前面板已经显示了,名字:" + fullName);
return UIDic[fullName];
}
GameObject canvasObj = GameObject.Find("Canvas");
if (canvasObj == null)
{
Debug.LogError("场景中没有Canvas组件,无法显示UI物体");
return null;
}
CanvasTransform = canvasObj.transform;
UIBase uiBase = Activator.CreateInstance(t) as UIBase;
if (string.IsNullOrEmpty(uiBase.PrefabsPath))
{
Debug.LogError("Prefabs 路径不能为空");
return null;
}
GameObject prefabs = Resources.Load<GameObject>(uiBase.PrefabsPath);
GameObject uiGameOjbect = GameObject.Instantiate(prefabs, CanvasTransform);
uiGameOjbect.name = uiBase.PrefabsPath.Substring(uiBase.PrefabsPath.LastIndexOf('/') + 1);
uiBase.UIName = uiGameOjbect.name;
uiBase.SceneName = SceneManager.GetActiveScene().name;
uiBase.UIGameObject = uiGameOjbect;
uiBase.FullName = fullName;
uiBase.Start();
UIDic.Add(fullName, uiBase);
return uiBase;
}
/// <summary>
/// 移除面板
/// </summary>
/// <typeparam name="T"></typeparam>
public void RemoveUI<T>()
{
Type t = typeof(T);
string fullName = t.FullName;
if (UIDic.ContainsKey(fullName))
{
UIBase uIBase = UIDic[fullName];
uIBase.Destroy();
GameObject.Destroy(uIBase.UIGameObject);
UIDic.Remove(fullName);
return;
}
Debug.Log(string.Format("当前的UI物体未实例化,名字:{0}", fullName));
}
/// <summary>
/// 清除所有的UI物体
/// </summary>
public void ClearAllPanel()
{
foreach (var key in UIDic.Keys)
{
UIBase uIBase = UIDic[key];
if (uIBase != null)
{
uIBase.Destroy();
GameObject.Destroy(uIBase.UIGameObject);
}
}
UIDic.Clear();
}
/// <summary>
/// 找到指定的UI面板
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
public GameObject GetGameObject<T>(string name)
{
Type t = typeof(T);
string fullName = t.FullName;
UIBase uIBase = null;
if (!UIDic.TryGetValue(fullName, out uIBase))
{
Debug.Log("没有找到对应的UI面板,名字:" + fullName);
return null;
}
return uIBase.GetObject(name);
}
private UIManager() { }
}
三、UI 框架的用法
我用了两个 场景来测试框架,start 和 main 场景
start 场景如下:
在 Start 场景 GameRoot 上挂上 StartSceneRoot 脚本
这个脚本就调用框架在场景中显示一个UI
using UnityEngine;
public class StartSceneRoot : MonoBehaviour {
void Start () {
UIManager.Instance.ShowUI<Panel_MainUI>();
}
}
在 GameManager 游戏物体上有两个脚本,这个是要跟随着场景一起跳转的。
UIManager 的代码在上一节,下面是 GameManager 代码
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
public static GameManager Instance;
private static bool origional = true;
private void Awake()
{
if (origional)
{
Instance = this as GameManager;
origional = false;
DontDestroyOnLoad(this.gameObject);
}
else
{
Destroy(this.gameObject);
}
}
void Start () {
SceneManager.sceneUnloaded += SceneManager_sceneUnloaded;
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
}
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
{
//Debug.Log("场景加载了,场景名:" + arg0.name);
}
private void SceneManager_sceneUnloaded(Scene arg0)
{
//Debug.Log("场景卸载了,场景名:" + arg0.name);
//注意:切换场景要清除掉 UIManager 中保存的 UI 数据
UIManager.Instance.ClearAllPanel();
}
public void LoadScene(string sceneName)
{
if (SceneManager.GetActiveScene().name != sceneName)
{
SceneManager.LoadScene(sceneName);
}
}
private GameManager() { }
}
在 start 场景中挂载到游戏物体上的脚本就这三个:StartSceneRoot,GameManager,UIManager
下面就准备要显示的 UI 了,在这里,我做了三个 UI 界面,并做成预制体
界面 Panel_MainUI
界面 Panel_Setting
界面 Panel_Affiche
这三个预制体对应的也是三个脚本,但不用挂在游戏物体上,作用是UI的逻辑部分。
Panel_MainUI
using UnityEngine;
using UnityEngine.UI;
public class Panel_MainUI : UIBase
{
public Panel_MainUI()
{
PrefabsPath = "Prefabs/UI/Panel_MainUI";
}
public override void Start()
{
GetOrAddCommonent<Button>("Button_Setting").onClick.AddListener(() =>
{
//显示设置面板
UIManager.Instance.ShowUI<Panel_Setting>();
});
GetOrAddCommonent<Button>("Button_Task").onClick.AddListener(() =>
{
//清除所有的面板
//UIManager.Instance.ClearAllPanel();
//跳转到 main 场景
GameManager.Instance.LoadScene("main");
});
GetOrAddCommonent<Button>("Button_Equipage").onClick.AddListener(() =>
{
Debug.Log("UIManager 中 UI 面板的个数:" + UIManager.Instance.UICount);
});
}
public override void Update()
{
//Debug.Log("我是 Panel_MainUI Update 方法");
}
public override void Destroy()
{
//Debug.Log("我是 Panel_MainUI Destroy 方法");
}
}
Panel_Setting
using UnityEngine;
using UnityEngine.UI;
public class Panel_Setting : UIBase
{
public Panel_Setting()
{
PrefabsPath = "Prefabs/UI/Panel_Setting";
}
public override void Start()
{
//Debug.Log("我是 Panel_Setting Start 方法");
GetOrAddCommonent<Button>("Button_Close").onClick.AddListener(() =>
{
//移除自己
UIManager.Instance.RemoveUI<Panel_Setting>();
});
GetOrAddCommonent<Button>("Button_Test").onClick.AddListener(() =>
{
//访问其他的面板的游戏物体
GameObject obj = UIManager.Instance.GetGameObject<Panel_MainUI>("Button_Map");
if (obj != null)
obj.transform.Find("Text").GetComponent<Text>().text = "Map";
});
GetOrAddCommonent<Button>("Button_Test1").onClick.AddListener(() =>
{
Debug.Log("UIManager 中 UI 面板的个数:" + UIManager.Instance.UICount);
});
}
public override void Update()
{
//Debug.Log("我是 Panel_Setting Update 方法");
}
public override void Destroy()
{
//Debug.Log("我是 Panel_Setting Destroy 方法");
}
}
Panel_Affiche
using UnityEngine;
using UnityEngine.UI;
public class Panel_Affiche : UIBase
{
public Panel_Affiche()
{
PrefabsPath = "Prefabs/UI/Panel_Affiche";
}
public override void Start()
{
GetOrAddCommonent<Button>("Button_Close").onClick.AddListener(() =>
{
GameManager.Instance.LoadScene("start");
});
}
public override void Update()
{
//Debug.Log("我是 Panel_Affiche Update 方法");
}
public override void Destroy()
{
//Debug.Log("我是 Panel_Affiche Destroy 方法");
}
}
main 场景只挂了一个脚本
MainSceneRoot 脚本同样也是只是用来显示UI用的
using UnityEngine;
public class MainSceneRoot : MonoBehaviour {
void Start () {
UIManager.Instance.ShowUI<Panel_Affiche>();
}
}
运行后就能看到,显示了 Panel_MainUI 的UI界面,点击设置,就会打开设置的界面,点击任务按钮,就会跳转到 main 场景(这里只是测试)
跳转到 main 场景后,点击关闭按钮,又会返回到 start 场景。
演示就这些了,写这个框架我也就用了几个小时而已,当然还有待继续完善和改进的,另外,我用的 Unity版本是 Unity 5.6.7f1 ,C# 的一些高级语法用不了,不然可以写的更优雅一些。
源码:点击跳转
结束
如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言
end
版权归原作者 熊思宇 所有, 如有侵权,请联系我们删除。