UI的管理
阅读原文时间:2023年07月10日阅读:2

游戏的UI系统往往会比较复杂,工作量比较庞大,需要多人协作完成,为了开发和维护方便,有必要对UI系统进行管理。

一.制作预制件

将UI的各个不同的功能面板制作为预制件,放入Resources目录下,方便加载预制件。

二.开发对应预制件的枚举类,使用json存储预制件名称和地址的对应关系,预制件放在Resources目录下方便加载

public enum UIPanelType
{
ItemMessagePanel,
KnapsackPanel,
MainManuPanel,
ShopPanel,
SkillPanel,
SystemPanel,
TaskPanel
}

{
"infoList":
[
{
"panelTypeString":"ItemMessagePanel","path":"UI/ItemMessagePanel"
},
{
"panelTypeString":"KnapsackPanel","path":"UI/KnapsackPanel"
},
{
"panelTypeString":"MainManuPanel","path":"UI/MainManuPanel"
},
{
"panelTypeString":"ShopPanel","path":"UI/ShopPanel"
},
{
"panelTypeString":"SkillPanel","path":"UI/SkillPanel"
},
{
"panelTypeString":"SystemPanel","path":"UI/SystemPanel"
},
{
"panelTypeString":"TaskPanel","path":"UI/TaskPanel"
}
]
}

三.开发UIManager类

UIManager类只能实例化一次,使用单例模式。提供解析json的方法,在对象实例化时调用这个方法并将解析出来的预制件名称和地址使用字典存储。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIManager
{
//存储所有面板prefab路径的字典
private Dictionary panelPahtDict = new Dictionary();

private static UIManager \_instance;  
//单例模式,提供get方法  
public static UIManager Instance  
{  
    get  
    {  
        if (\_instance == null)  
            \_instance = new UIManager();  
        return \_instance;  
    }  
}

//单例模式,将构造方法私有化  
private UIManager() {  
    ParseUIPannelJson();  
}

//解析json  
private void ParseUIPannelJson()  
{  
    //读取json的内容  
    TextAsset textAsset = Resources.Load<TextAsset>("UIPanelType");  
    //解析json,存储为UIPanelTypeJson内部类对象,该对象包含UIPanelInfo的list,UIPanelInfo的成员变量和json中的数据名称相同  
    UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(textAsset.text);  
    //遍历list将对象中的值存储到字典中  
    foreach(UIPanelInfo info in jsonObject.infoList)  
    {  
        panelPahtDict.Add(info.panelType,info.path);  
    }  
}

public class UIPanelTypeJson  
{  
    public List<UIPanelInfo> infoList;  
}

}

提供用于实例化的对象模板类,成员变量和json的数据对应好。因为自定义的枚举类型序列化时会出现问题,所以不序列化枚举类型,转而提供对应的字符串变量,然后类继承ISerializationCallbackReceiver类,实现OnAfterDeserialize和OnBeforeSerialize方法,对应序列化前和反序列化后的行为,其中将字符串变量和枚举变量的值相互转化。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[Serializable]
public class UIPanelInfo : ISerializationCallbackReceiver
{
[NonSerialized]
public UIPanelType panelType;

public string panelTypeString;  
public string path;

public void OnAfterDeserialize()  
{  
    panelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);  
}

public void OnBeforeSerialize()  
{  
    panelTypeString = panelType.ToString();  
}  

}

三.管理所有面板实例化

管理面板的实例化使用多态特性

首先创建一个面板的基类BasePanel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BasePanel : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame  
void Update()  
{

}  

}

接下来在不同的面板上创建并挂载不同的面板脚本,但是都继承面板基类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemMessagePanel : BasePanel
{
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame  
void Update()  
{

}  

}

最后在UIManager类中管理面板:

使用字典保存实例化好的面板信息

//保存实例化出来的物体的BasePanel组件  
private Dictionary<UIPanelType, BasePanel> panelDict = new Dictionary<UIPanelType, BasePanel>();

获取画布的transform组件,面板需要设置为画布的子物体

//画布的transform组件,用于为面板设定父子级关系等参数  
private Transform cavasTransform;  
private Transform CavasTransform  
{  
    get  
    {  
        if (cavasTransform == null)  
            cavasTransform = GameObject.Find("Canvas").transform;  
        return cavasTransform;  
    }  
}

提供获取根据面板类型获取面板上BasePanel组件的方法,如果面板不存在则创建面板

/// <summary>  
/// 根据给定的panel类型获取物体身上的BasePanel组件  
/// </summary>  
/// <param name="panelType"></param>  
/// <returns></returns>  
private BasePanel GetPanel(UIPanelType panelType)  
{  
    BasePanel panel;  
    if (!panelDict.ContainsKey(panelType))  
    {  
        string path;  
        panelPathDict.TryGetValue(panelType, out path);  
        GameObject go = GameObject.Instantiate(Resources.Load(path)) as GameObject;  
        //设置panel的父级关系同时设置坐标为局部坐标,否则默认世界坐标显示会出现问题  
        go.transform.SetParent(CavasTransform,false);  
        panel = go.GetComponent<BasePanel>();  
        panelDict.Add(panelType, panel);  
    }  
    else  
    {  
        panelDict.TryGetValue(panelType, out panel);  
    }  
    return panel;  
}

四.面板的显示

面板的显示遵循先显示的后移除,后显示的先移除的原则,所以使用栈管理所有面板的显示

提供存储已显示面板的栈

//使用栈保存显示在cavas中的面板  
private Stack<BasePanel> panelStack = new Stack<BasePanel>();

提供入栈的方法,即显示面板

///

/// 入栈,即显示面板 ///
///
public void PushPanel(UIPanelType panelType)
{
BasePanel panel = GetPanel(panelType);
panelStack.Push(panel);
}

在画布上挂载UIRoot脚本,用于管理面板的显示

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UIRoot : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
UIManager.Instance.PushPanel(UIPanelType.MainManuPanel);
}

}

提供ButtonRegister脚本,用于注册按钮,按下后显示界面,要显示的界面通过公开的UIPanelTypeString变量指定

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ButtonRegist : MonoBehaviour
{
public string UIPanelTypeString;
private Button btn;
private void Awake()
{
btn = gameObject.GetComponent