一、首先这篇文章我是参考麦子学院的一个Unity3D制作3D塔防游戏的其中的UI部分编写的,老师很不错,给了我很大的启发,
在此记录下,主要为了日后的查阅,先讲讲思路吧!
二、为何需要搭建框架?需要哪些条件?具备哪些功能?是否具有拓展性呢?
围绕上面的问题,开始一一阐述吧。
三、总所周知,一个游戏稍微复杂点都会涉及到众多的UI界面,而每个界面上有各种交互性和非交互性的UI控件,当随着游戏的不断迭代,
管理这些UI界面和控件的难度将不断增大,框架能够大大的简化、拓展和维护整个UI系统,学习它是必然的。
四、需要的基本条件有三个。1. Resources下保存各个UI界面的预制体 2.每个UI界面有一个主脚本,而此脚本的名称必须和预制体名称一致
3.每个UI界面根控件应该覆盖整个画布
五、1.该UI框架在初始时加载主界面,2.然后以主界面为桥梁可以通过界面上的交互控件动态到任意界面,3.当加载新的界面时,前一个界面销毁,
能够节省内存,4.所有游戏界面继承自UIPanel类,只需继承或者重写其父类的方法,即可实现打开其他界面,关闭当前界面,关闭其他界面等。。。
5.UIPanel类同样封装了交互控件的注册和绑定细节,其他界面只需按照所需添加控件,并给控件自定义响应方法即可。
六、该框架是具备拓展性的,1.可以结合Json,动态从本地文件中读取出UI界面,2.每个界面通过重写可以实现自身独有功能
-------------------------------------------
理论部分告一段落,接下来贴出一部分代码来直观的了解下吧。
public class Main : MonoBehaviour {
void Start () {
UIManager.OpendPanel<UIMainPanel>();
}
}该脚本挂在空对象UIManager上,负责加载主界面
public class UIManager : MonoBehaviour
{
public static List<MonoBehaviour> UIPanelList = new List<MonoBehaviour>();
public static int flag;
private static GameObject soleCanvas;
private const string UIPrefabsLoad = "UIPrefabs/";
public static void OpendPanel<T>() where T : MonoBehaviour
{
if (soleCanvas == null)
{
soleCanvas = GameObject.Find("Canvas");
}
var assetsUIPanle = Resources.Load(UIPrefabsLoad + typeof(T).Name.ToString()) as GameObject;//动态加载UI面板
var currentUIPanle = Instantiate(assetsUIPanle); //实例化UI面板到场景中
currentUIPanle.transform.SetParent(soleCanvas.transform);//动态加载出来的UIPanle放置到唯一Canvas下
(currentUIPanle.transform as RectTransform).anchoredPosition = Vector3.zero; //初始ui面板各项参数
(currentUIPanle.transform as RectTransform).anchoredPosition3D = Vector3.zero;
(currentUIPanle.transform as RectTransform).anchorMin = Vector2.zero;
(currentUIPanle.transform as RectTransform).anchorMax = Vector2.one;
(currentUIPanle.transform as RectTransform).pivot = new Vector2(0.5f, 0.5f);
(currentUIPanle.transform as RectTransform).sizeDelta = Vector2.zero;
var currentUIPanelScript = currentUIPanle.gameObject.AddComponent<T>();//动态加载和UI面板同名的脚本对象
UIPanelList.Add(currentUIPanelScript);//添加到UI面板列表中
flag += 1;//用来标记上一个UI面板的索引
}
}
上面是主要脚本之一,它可以用来动态加载任意指定UI界面,本身是一个泛型方法,T类型由调用者指定,当类型确定时,那么就加载与类型名称
相对应的UI界面,并且将相同名称的脚本动态挂在该界面上。
public class UIPanel : MonoBehaviour
{
/// <summary>
/// 递归获取根UI对象及其子UI对象
/// </summary>
/// <param name="tran">根UI对象</param>
/// <param name="name">该对象的名称</param>
public void Init(Transform tran, string name)
{
Register(tran, name);//注册除按钮外的交互控件
var button = tran.GetComponent<Button>();
if (button != null)
{
Bining(button, button.gameObject.name);//单纯绑定按钮控件
}
for (int i = 0; i < tran.childCount; i++)
{
Init(tran.GetChild(i), tran.GetChild(i).name);
}
}
/// <summary>
/// 非Button按钮注册
/// </summary>
/// <param name="tran">筛选根面板下的每个UI对象</param>
/// <param name="name">该对象的名称</param>
public virtual void Register(Transform tran, string name)
{
}
/// <summary>
/// Button按钮注册
/// </summary>
/// <param name="btn">筛选结果有Button组件的对象</param>
/// <param name="name">该组件对象的名称</param>
public virtual void Bining(Button btn, string name)
{
}
public virtual void Start()
{
Init(transform, name);
}
/// <summary>
/// 加载下一个界面时,调用此方法关闭上一个UI界面
/// </summary>
public virtual void Close()
{
if (UIManager.flag > 0)
{
Destroy(UIManager.UIPanelList[UIManager.flag - 1].gameObject);
}
}
}
上面脚本是所有需要动态生成界面的父类,它里面首先封装了交互性控件的注册和绑定细节,然后定义了所有界面所共用的行为,即打开、关闭等方法,
子类根据自身的特点可以选择是否重写父类的虚函数实现新的功能。
public class UIMainPanel : UIPanel
{
private Button btn_shop;
private Button btn_achievement;
private Button btn_exit;
public override void Bining(Button btn, string name)
{
switch (name)
{
case "btn_shop":
btn.onClick.AddListener(() => xx());
break;
case "btn_achievement":
btn.onClick.AddListener(() => yy());
break;
case "btn_exit":
btn.onClick.AddListener(() => Close());
break;
}
}
private void xx()
{
Close();
UIManager.OpendPanel<UIShopPanel>();
}
private void yy()
{
Close();
UIManager.OpendPanel<UIAchivementPanel>();
}
public override void Close()
{
base.Close();
}
}
这是继承自UIPanel的其中一个UI界面,通过它大家应该能够理解该简易框架如何运作的了吧。其他界面也和它差不太多,事实上应该还有许多可以优化的地方,
再次也仅仅只是简单的实现了一个框架该有的易用性、通用性、拓展性罢了,后面我会继续对它作进一步的优化。。。