Unity: marco de interfaz de usuario

Primero explique que la función del marco de la interfaz de usuario es controlar el salto mutuo entre los paneles de la interfaz de usuario. Después de usar el marco de la interfaz de usuario, el mayor uso es evitar operaciones complicadas al cambiar de página. Usar el marco de la interfaz de usuario puede administrar mejor las páginas de la interfaz de usuario, la visualización y el cierre de la página de control también está controlado por una sola función, lo que optimiza en gran medida el código

Primer vistazo a un diagrama de marco de interfaz de usuario

 Combinado con la imagen de arriba, comience a hacer el marco de la interfaz de usuario paso a paso

1. Primero haga cada panel de la interfaz de usuario por separado y luego colóquelo en el archivo de recursos como prefabricado (el propósito de esto es obtener todos los paneles directamente al cargar (la implementación específica es a través del análisis json en el diccionario In), la implementación específica está debajo)

2. Cree un tipo de enumeración UIPanelType para guardar el panel correspondiente

Cree un archivo json de UIPanelType para guardar el panel y su ruta correspondiente. Tenga en cuenta que el valor de panelType aquí debe ser consistente con el valor en el tipo de enumeración, para que el análisis pueda ser exitoso más adelante (debe cargarse a través del Archivo de recursos, así que póngalo en el archivo de recursos (puede crear otro archivo de recursos))

 3. Cree la clase UIManager, que tiene tres funciones,

 La primera función: analizar y guardar la información del panel (al analizar el archivo json y luego almacenarlo en el diccionario, la clave es el UIType de la enumeración y el valor es la ruta) (tenga en cuenta que al analizar el json, un archivo de datos se necesita una clase para recibirlo, así que cree una clase UIPanelInfo para recibir datos de análisis json) Tenga en cuenta que la clase UIPanelInfo aquí es el [ {} , {}] correspondiente, pero se puede ver que esta estructura se analiza como una matriz o lista , pero el análisis de unidad solo puede analizar la clase ( a partir de la versión 0.9.0, LitJSON admite la serialización de objetos List en cadenas JSON, sin necesidad de crear más clases más adelante ), por lo que esta estructura debe cambiarse a { [ { }, {}, {} ] }, y la matriz Póngalo en {}, es decir, se necesita otra clase UIPanelTypeJson para representar el {} más externo. Tenga en cuenta que al analizar json, debe usar una cadena para el tipo de enumeración (porque no se puede analizar directamente en la enumeración). Para obtener más información, consulte Conversión de cadenas y enumeraciones (actualmente, la iteración de funciones solo usa uimanager), después de leer la primera función, consulte el paso 4

El contenido del archivo Json también cambia a este

La segunda función: crear y guardar una instancia del panel (a través del diccionario, la clave es la enumeración del panel y el valor es el componente BasePanel del panel (la clase principal se puede obtener a través del objeto de subclase y BasePanel se usa para representar la página)), y el método de creación ( GetPanel ( ) ) Obtenga la clase principal del panel a través de la enumeración del panel (hay un método para extender el diccionario al obtenerlo, consulte la extensión del Diccionario)

La tercera función: administrar y guardar todos los paneles mostrados (las páginas mostradas se guardan por medio de una pila, y todas las páginas mostradas se muestran en la pila, la parte superior de la pila es la página que se puede operar, es decir, la página que se puede hacer clic, y la parte inferior de la pila La página solo se muestra y no se puede operar), por lo que hay operaciones de crear una pila, hacer estallar y empujar (estas dos son las más importantes, porque la visualización y el cierre de todas las páginas confíe en estas operaciones de hacer estallar y empujar (y los siguientes pasos 5 estado agregado)

4. Cree una clase base común de paneles, BasePanel, para que todos los paneles hereden de BasePanel, porque la secuencia de comandos de cada panel hereda de BasePanel (la clase de panel aquí se crea sola), de modo que después de instanciar cada panel, puede directamente crear una instancia del objeto a través del panel GetComponent<BasePanel>(), para que pueda agregar la segunda función de la clase UIManager

Estos son los paneles, creados según su propio proyecto, y adjunte el código de clase del panel en la parte inferior:

5. Hay cuatro estados para cada página: OnEnter (se muestra la página), OnPause (la página está en pausa porque aparecieron otras páginas), OnResume (la página continúa, otras páginas cubiertas en esta página se eliminan y la página es restaurada. Interacción), OnExit (eliminar la página, eliminar la visualización de la página) para obtener detalles, consulte el diagrama de flujo. Tenga en cuenta que debido a que cada página tiene estos cuatro estados, estos cuatro estados se definen en BasePanel, a través del método virtual, y luego deje que Cada subclase se reescriba, que puede referirse al panel de la mochila, involucra estos cuatro estados (si desea agregar animaciones de visualización y salida, agréguelas en OnEnter y OnExit)

6. Agregue eventos de clic para cada botón en cada panel (en el script heredado de BasePanel).Por ejemplo, ahora tengo un menú principal, el script es MainPanel y hay tareas, mochilas y otros botones en el menú principal. En este momento entra en juego el marco de la interfaz de usuario. Puede mostrar la página directamente a través del método push en UIManager en el evento de clic de botón y cerrar la página con el método pop. Recuerde que también debe pasar el tipo de enumeración del panel. (el evento de clic no puede pasar el tipo de enumeración aquí. parámetros del tipo de enumeración, por lo que aún se requiere el paso de convertir la cadena al tipo de enumeración), así

7. Es la parte GameRoot, que se usa para iniciar el marco de la interfaz de usuario

 La parte del código se adjunta a continuación, en el orden presentado anteriormente

Clase UIPanelType:

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

public enum UIPanelType
{
    ItemMessage,
    Knapsack,
    MainMenu,
    Shop,
    Skill,
    System,
    Task

}

El archivo json de UIPanelType:

{

  "infoList": [
 
    {
      "panelTypeString": "ItemMessage",
      "path": "UIPanel/ItemMessagePanel"
    },
    {
      "panelTypeString": "Knapsack",
      "path": "UIPanel/KnapsackPanel"
    },
    {
      "panelTypeString": "MainMenu",
      "path": "UIPanel/MainMenuPanel"
    },
    {
      "panelTypeString": "Shop",
      "path": "UIPanel/ShopPanel"
    },
    {
      "panelTypeString": "Skill",
      "path": "UIPanel/SkillPanel"
    },
    {
      "panelTypeString": "System",
      "path": "UIPanel/SystemPanel"
    },
    {
      "panelTypeString": "Task",
      "path": "UIPanel/TaskPanel"
    }
    ]
}

Clase UIManager (el contenido central del marco)

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

public class UIManager 
{
    #region 第一部分功能
    private Dictionary<UIPanelType, string> panelPathDict;//面板信息字典,用来存放面板所对应的Prefab路径
    private static UIManager _instance;
    public static UIManager Instance//单例模式不是这章重点,不做讲解
    {
        get 
        {
            if(_instance == null)
            {
                _instance = new UIManager();
            }
            return _instance; 
        }

    }

    private UIManager()
    {
        ParseUIPanelTypeJson();
    }

    [Serializable]
    class UIPanelTypeJson
    {
        public List<UIPanelInfo> infoList;
    }
    private void ParseUIPanelTypeJson()//解析json文件
    {
        panelPathDict=new Dictionary<UIPanelType, string>();
        TextAsset ta = Resources.Load<TextAsset>("UIPanelType");//json文件在unity中是属于TextAsset类,因此用这个类来接收
        //这里应该也可以用txt文件保存json数据,然后用读取流的形式读取
        UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);//注意,如果这样的话就会报错,原因在解析json的时候,并不能解析成枚举类型,因为在UIPanelInfo类的panelType成员为枚举类型,因此就会报解析错误
        //将解析的json信息,填入panelPathDict字典中,键对应面板枚举值,值对应面板路径
        foreach (UIPanelInfo info in jsonObject.infoList)
        {
            panelPathDict.Add(info.panelType, info.path);

        }
    }
    #endregion

    #region 第二部分功能
    private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体身上的脚本组件(继承自BasePanel的)
    private Transform canvasTransform;//获取面板
    public Transform CanvasTransform
    {
        get
        {
            if(canvasTransform == null)
            {
                canvasTransform = GameObject.Find("Canvas").transform;
            }
            return canvasTransform;
        }
    }
    private BasePanel GetPanel(UIPanelType panelType)//根据面板的枚举类型,得到实例化的面板
    {
        if (panelDict == null)//判断字典是否为空
        {
            panelDict = new Dictionary<UIPanelType, BasePanel>();//如果为空就创建字典
        }
        // BasePanel panel;
        //panelDict.TryGetValue(panelType, out panel);
        BasePanel panel = panelDict.TryGet(panelType);//字典扩展方法
        if (panel == null)//判断是否得到该面板
        {
            //如果为空就根据panelPathDict字典中路径实例化面板,并保存在panelDict中和放在Canvas画布下
            //string path;
            //  panelPathDict.TryGetValue(panelType, out path);
            string path = panelPathDict.TryGet(panelType);

            //GameObject instPanelPrefab = Resources.Load<GameObject>(path);//这是通过动态加载的形式获取到了面板预制件的引用
            //GameObject instPanel=GameObject.Instantiate(instPanelPrefab);//因为没有继承自MonoBehaviour,所有需要加GameObject
            GameObject instPanel = GameObject.Instantiate(Resources.Load<GameObject>(path));//这是简洁写法
            panelDict.Add(panelType, instPanel.GetComponent<BasePanel>());
            instPanel.transform.SetParent(CanvasTransform, false);//放在画布下
            return instPanel.GetComponent<BasePanel>();
        }
        else
        {
            return panel;
        }
    }
    #endregion

    #region 第三部分功能
    private Stack<BasePanel> panelStack;
    //都是根据页面的枚举类型来获取到页面,才能方便出栈入栈操作
    public void PushPanel(UIPanelType panelType )//把页面入栈,把某个页面显示在界面上
    {
        if(panelStack == null)
        {
            panelStack = new Stack<BasePanel>();
        }
        //判断栈里面是否有页面
        if(panelStack.Count > 0 )
        {
            BasePanel topPanel=panelStack.Peek();//这是获取到栈顶元素Pop是出栈
            topPanel.OnPause();//将栈顶元素暂停
        }
        BasePanel panel=GetPanel(panelType);//这里就用到了第二部分功能中的获取面板的方法
        panelStack.Push(panel);//入栈
        panel.OnEnter();//这是新的栈顶元素显示
        //注意这里都只是统一调用BasePanel中的方法,因为BasePanel中的方法是虚方法没有具体实现,需要在子类中具体实现后,就能有具体的功能
    }
    public void PopPanel()//把页面出栈, 把页面从界面上移除
    {

        if (panelStack == null)
        {
            panelStack = new Stack<BasePanel>();
        }
        if (panelStack.Count <= 0)
        {
            return;
        }
        BasePanel topPanel=panelStack.Pop();//栈顶出栈
        topPanel.OnExit();
        if (panelStack.Count <= 0)//这是如果关闭页面后下方还有页面就需要将栈顶页面恢复
        {
            return;
        }
        BasePanel topPanel2 = panelStack.Peek();
        topPanel2.OnResume();//这里同理,具体实现都是在子类脚本中重新,这里只负责调用
    }
    #endregion
}

 clase UIPanelInfo:

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

[Serializable]
public class UIPanelInfo:ISerializationCallbackReceiver
{
    [NonSerialized]//因为json解析成不了枚举类型,因此就不解析成它
    public UIPanelType panelType;//注意,因为这个类时用来接收解析的json的,因此变量成员名一定要和json中的名字要相同

    public string panelTypeString;//json解析成字符串

    public string path;

    public void OnAfterDeserialize()//反序列化成功之后调用
    {
        // throw new NotImplementedException();
        //这里就是将字符串转化为枚举类型的过程,放在反序列化成功之后执行
        UIPanelType Type = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
        panelType = Type;
    }

    public void OnBeforeSerialize()//反序列化成功之前调用
    {
      //  throw new NotImplementedException();
    }
}

Clase DictionaryExtension: clase de extensión de diccionario

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

public static class DictionaryExtension //字典扩展类,对字典使用方法进行扩展
{
   
    public static Tvalue TryGet<Tkey,Tvalue>(this Dictionary<Tkey,Tvalue> dict,Tkey key)
    {
        Tvalue value;
        dict.TryGetValue(key, out value);
        return value;
    }
}

Clase BasePanel:

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

public class BasePanel : MonoBehaviour
{
    /// <summary>
    /// 界面被显示出来
    /// </summary>
    public virtual void OnEnter()
    {

    }

    /// <summary>
    /// 界面暂停
    /// </summary>
    public virtual void OnPause()
    {

    }

    /// <summary>
    /// 界面继续
    /// </summary>
    public virtual void OnResume()
    {

    }

    /// <summary>
    /// 界面不显示,退出这个界面,界面被关系
    /// </summary>
    public virtual void OnExit()
    {

    }
}

Clase GameRoot:

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

public class GameRoot : MonoBehaviour
{
    private void Start()
    {
        UIManager.Instance.PushPanel(UIPanelType.MainMenu);
    }
}

Una clase que muestra una parte del panel del proyecto (heredada de BasePanel), que no forma parte del marco

La clase del menú principal (montada en la página del menú principal), el método de evento de clic, como hacer clic en el botón de la tarea en el menú principal para mostrar la página de la tarea, debe escribir su propia función y vincularla al botón, aquí está la función OnClickPushPanel()

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

public class MainMenuPanel : BasePanel
{
    private CanvasGroup canvasGroup;
    private void Start()
    {
        canvasGroup = GetComponent<CanvasGroup>();
    }
    //这里是重新的MainMenuPanel页面枚举类型对应的BasePanel中的OnPause方法,因为在UIManager中的panelDict保存了枚举类型对应的BasePanel
    //因此这里的调用流程就是先调用下方的PushPanel,在PushPanel函数中根据panelType获取到BasePanel,然后执行 panel.OnPause();等方法,又因为
    //获取到的BasePanel就是 MainMenuPanel的父类这个 BasePanel,因此调用的panel.OnPause();方法就是执行的下方的内容
    public override void OnPause()//这里具体的暂停方法就是取消掉主页面中的所有可交互的东西,因此可以用CanvasGroup控制,详细使用方法见手册
    {
       canvasGroup.blocksRaycasts = false;//暂停该页面,让鼠标不再和该页面交互
    }
    public override void OnResume()
    {
        canvasGroup.blocksRaycasts = true;
    }
    public void OnClickPushPanel(string panelString )//定义点击事件,将要显示的页面添加进栈中,这里是因为要复用才通过字符串形式转换
    {
        UIPanelType panelType=(UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelString);
        UIManager.Instance.PushPanel( panelType );
    }
}

Otro ejemplo es la clase mochila:

using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class KnapsackPanel : BasePanel
{
    private CanvasGroup canvasGroup;
    private void Start()
    {
        if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();
    }
    public override void OnEnter()
    {
        if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>();//这里是防止面板在实例化出来后,立马调用了OnEnter()方法,导致 canvasGroup还没赋值而报空
        canvasGroup.alpha = 1;
        canvasGroup.blocksRaycasts = true;
        // gameObject.SetActive(true); //这里也可以直接可以设置页面的active来表示进入和退出

        Vector3 temp = transform.localPosition;
        temp.x = 1300;
        transform.localPosition = temp;
        transform.DOLocalMoveX(0, 0.5f);
    }
    public override void OnExit()//关闭该页面的具体实现内容
    {
        //canvasGroup.alpha = 0;
        canvasGroup.blocksRaycasts = false;
        transform.DOLocalMoveX(1800, 0.5f).OnComplete(() => { canvasGroup.alpha = 0; });
        // gameObject.SetActive(false);
    }
    public override void OnPause()
    {
        canvasGroup.blocksRaycasts = false;
    }
    public override void OnResume()
    {
        canvasGroup.blocksRaycasts = true;
    }
    public void OnClosePanel()
    {
        UIManager.Instance.PopPanel();
    }
    public void OnItemButtonClick()//点击显示物品按钮
    {
        UIManager.Instance.PushPanel(UIPanelType.ItemMessage);
    }
}

Entonces, para resumir, solo hay 7 clases que pertenecen al marco de la interfaz de usuario

Otras clases como KnapsackPanel cambian según los cambios del proyecto.

Supongo que te gusta

Origin blog.csdn.net/qq_62947569/article/details/130773177
Recomendado
Clasificación