[Unity] Estructura de árbol en el desarrollo de juegos - sistema de puntos rojos

Las características más comunes que utilizan estructuras de árbol en el desarrollo de juegos son los sistemas de puntos rojos y los árboles de comportamiento.

Primero escribiré sobre el desarrollo del sistema de puntos rojos hoy.

1. Análisis de la demanda

La función del punto rojo es recordarle al jugador. Por ejemplo, si el jugador tiene correos electrónicos sin leer, aparecerá un punto rojo en la función de correo de la interfaz principal. Después de ver el punto rojo, el jugador hace clic en la entrada del función de correo, y después de ingresar a la interfaz principal de la función de correo, la página de la pestaña de correo muestra un clic rojo, luego y luego haga clic en la etiqueta del correo para ingresar a la lista de correo y encontrar un correo no leído con un punto rojo entre los muchos correos.

Todo el proceso de notificación es: entrada de correo de la interfaz principal → pestaña de correo de la interfaz de correo → correo no leído en la lista de correo. Intuitivamente, es apuntar capa por capa desde el exterior hacia el interior.

Sin embargo, al implementar la función de punto rojo, ¿necesita escribir códigos de aviso de punto rojo en la interfaz principal, la interfaz de correo y la interfaz de la lista de correo, respectivamente?

Sin mencionar que he visto a alguien hacer esto. El indicador de punto rojo para una función debe escribirse en todas partes. Uno puede imaginar cuánto código está involucrado en la función que hizo, especialmente en la interfaz principal. El código ya no se puede leer. Una llamada de interfaz El método de más de diez o veinte módulos es juzgar si el punto rojo se muestra o no.

Entonces, el nacimiento del código elegante comienza con la pereza. No quiero agregar una oración de XXX.IsShowRedPoint () que no debería aparecer en otro lugar para un indicador de punto rojo. Espero que solo necesite agregar una oración en la lógica de actualización de la lista de correo: RedPointMgr.ShowPoint( RedPointType.Mail, true) para completar todo el proceso de solicitud anterior.

2. Realización de funciones

Así nació el árbol de puntos rojos. Cuando se llama a SetState, avanzará desde el nodo actual capa por capa, es decir, todos los nodos principales del nodo que activa la función de punto rojo se mostrarán una vez con un punto rojo. .

PD: ¿Por qué no pasar del nodo raíz a los nodos secundarios? Obviamente, cada nodo tiene solo un nodo principal y tiene muchos nodos secundarios. Es imposible saber en qué nodo se encuentra el nodo actual. Solo podemos atravesar una vez para encontrar el nodo actual y luego retroceder desde el nodo actual.

Entonces está muy claro aquí, en primer lugar, debe haber una clase de punto rojo, que es una estructura de árbol.

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

public enum RedPointType
{
    None,
    Enternal,//一直存在
    Once,//点击一次就消失
}

public enum RedPointState
{
    None,
    Show,
    Hide,
}

public class RedPoint
{
    /// <summary>
    /// 主关键字(属于哪一个根节点)
    /// </summary>
    public string key
    {
        get
        {
            return m_Key;
        }
    }

    /// <summary>
    /// 自己的关键字
    /// </summary>
    public string subKey
    {
        get
        {
            return m_SubKey;
        }
    }

    /// <summary>
    /// 是否是根节点
    /// </summary>
    public bool isRoot
    {
        get
        {
            return m_IsRoot;
        }
    }

    /// <summary>
    /// 红点类型
    /// </summary>
    public RedPointType type
    {
        get
        {
            return m_Type;
        }
    }

    /// <summary>
    /// 当前状态
    /// </summary>
    public RedPointState state
    {
        get
        {
            return m_State;
        }
    }

    /// <summary>
    /// 数据
    /// </summary>
    public int data
    {
        get
        {
            return m_Data;
        }
    }

    /// <summary>
    /// 父节点
    /// </summary>
    public RedPoint parent
    {
        get
        {
            return m_Parent;
        }
    }

    /// <summary>
    /// 子节点
    /// </summary>
    public List<RedPoint> children
    {
        get
        {
            return m_Children;
        }
    }


    public RedPoint(string key, string subKey, bool isRoot, RedPointType type)
    {
        m_Key = key;
        m_SubKey = subKey;
        m_IsRoot = isRoot;
        m_Type = type;
        m_State = RedPointState.Hide;
        m_Data = 0;
        m_Children = new List<RedPoint>();
    }

    public void Init(Action<RedPointState, int> showEvent, Button btn)
    {
        m_ShowEvent = showEvent;

        if (btn != null)
        {
            m_Btn = btn;
            m_Btn.onClick.AddListener(OnClick);
        }

        m_ShowEvent?.Invoke(m_State, m_Data);
    }

    public void AddChild(RedPoint node, string parentKey)
    {
        if (m_SubKey.Equals(parentKey))
        {
            node.SetParent(this);
            m_Children.Add(node);
            return;
        }

        for (int i = 0; i < m_Children.Count; i++)
        {
            m_Children[i].AddChild(node, parentKey);
        }
    }

    public RedPoint GetChild(string subKey)
    {
        if (m_SubKey.Equals(subKey))
        {
            return this;
        }

        if (m_Children == null)
        {
            return null;
        }

        for (int i = 0; i < m_Children.Count; i++)
        {
            RedPoint node = m_Children[i].GetChild(subKey);

            if (node != null)
            {
                return node;
            }
        }

        return null;
    }

    public void RemoveChild(string subKey)
    {
        if (m_SubKey.Equals(subKey))
        {
            m_Parent.children.Remove(this);
            Dispose();
            return;
        }

        if (m_Children == null)
        {
            return;
        }

        for (int i = 0; i < m_Children.Count; i++)
        {
            m_Children[i].RemoveChild(subKey);
        }
    }

    public void SetParent(RedPoint parent)
    {
        m_Parent = parent;
    }

    public void SetState(string subKey, RedPointState state, int data)
    {
        RedPoint node = GetChild(subKey);

        if (node == null)
        {
            return;
        }

        node.SetTreeState(subKey, state, data);

        m_Data = 0;

        for (int i = 0; i < m_Children.Count; i++)
        {
            m_Data += m_Children[i].m_Data;
        }

        m_ShowEvent?.Invoke(m_State, m_Data);
    }

    private void SetTreeState(string subKey, RedPointState state, int data)
    {
        m_State = state;

        if (m_SubKey.Equals(subKey))
        {
            m_Data = data;
        }
        else
        {
            m_Data = 0;

            for (int i = 0; i < m_Children.Count; i++)
            {
                if (m_Children[i].state == RedPointState.Show)
                {
                    m_State = RedPointState.Show;
                    m_Data += m_Children[i].data;
                }
            }
        }

        if (m_Parent != null)
        {
            m_Parent.SetTreeState(subKey, state, data);
        }

        m_ShowEvent?.Invoke(m_State, m_Data);
    }

    private void OnClick()
    {
        if (m_Type == RedPointType.Once)
        {
            HideChildren();
            SetState(m_SubKey, RedPointState.Hide, m_Data);
        }
    }

    private void HideChildren()
    {
        m_State = RedPointState.Hide;

        for (int i = 0; i < m_Children.Count; i++)
        {
            m_Children[i].HideChildren();
        }

        m_ShowEvent?.Invoke(m_State, m_Data);
    }

    public void Dispose()
    {
        for (int i = 0; i < m_Children.Count; i++)
        {
            m_Children[i].Dispose();
        }

        m_Children.Clear();
        m_Children = null;

        if (m_Btn != null)
        {
            m_Btn.onClick.RemoveListener(OnClick);
        }

        m_Btn = null;
        m_Parent = null;
        m_Key = null;
        m_SubKey = null;
        m_ShowEvent = null;
        m_Type = RedPointType.None;
        m_State = RedPointState.None;
    }

    private string m_Key = string.Empty;
    private string m_SubKey = string.Empty;
    private bool m_IsRoot = false;
    private int m_Data = 0;
    private RedPointType m_Type = RedPointType.None;
    private RedPointState m_State = RedPointState.None;
    private Action<RedPointState, int> m_ShowEvent = null;
    private Button m_Btn;
    private RedPoint m_Parent = null;
    private List<RedPoint> m_Children = null;
}


La clave aquí es la atribución, es decir, a qué función principal pertenece, y la subclave es su propia palabra clave.



Luego, se necesita un administrador para administrar los nodos raíz de todos los puntos rojos en el juego. Cuando sea necesario, busque el nodo raíz de los puntos rojos a través de palabras clave y luego inserte un nodo secundario en uno de los nodos.

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


public class RedPointMgr : IDisposable
{
    public static RedPointMgr instance
    {
        get
        {
            if (s_Instance == null)
            {
                s_Instance = new RedPointMgr();
            }

            return s_Instance;
        }
    }
    public RedPointMgr()
    {
        m_ListRedPointTrees = new List<RedPoint>();
    }

    public void Add(string key, string subKey, string parentKey, RedPointType type)
    {
        RedPoint root = GetRoot(key);

        if (string.IsNullOrEmpty(subKey) || key.Equals(subKey))
        {
            if (root != null)
            {
                Debug.LogError("The red point root [" + key + "] is already exist!");
                return;
            }

            root = new RedPoint(key, key, true, type);
            m_ListRedPointTrees.Add(root);
        }
        else
        {
            if (root == null)
            {
                Debug.LogError("The red point root [" + key + "] is invalid,please add it first");
                return;
            }

            RedPoint node = new RedPoint(key, subKey, false, type);
            root.AddChild(node, parentKey);
        }
    }

    public void Remove(string key, string subKey)
    {
        if (string.IsNullOrEmpty(subKey) || key.Equals(subKey))
        {
            for (int i = m_ListRedPointTrees.Count - 1; i >= 0; i--)
            {
                if (m_ListRedPointTrees[i].key.Equals(key))
                {
                    m_ListRedPointTrees[i].Dispose();
                    m_ListRedPointTrees.RemoveAt(i);
                    return;
                }
            }

            return;
        }

        RedPoint root = GetRoot(key);

        if (root == null)
        {
            return;
        }

        root.RemoveChild(subKey);
    }

    public void Init(string key, string subKey, Action<RedPointState, int> showEvent, Button btn = null)
    {
        RedPoint root = GetRoot(key);

        if (root == null)
        {
            Debug.LogError("The red point root [" + key + "] is invalid,please add it first");
            return;
        }

        RedPoint node = root.GetChild(subKey);

        if (node == null)
        {
            Debug.LogError("The red point node [" + subKey + "] is invalid,please add it first");
            return;
        }

        node.Init(showEvent, btn);
    }

    public void SetState(string key, string subKey, RedPointState state, int data = 0)
    {
        RedPoint root = GetRoot(key);

        if (root == null)
        {
            Debug.LogError("The red point root [" + key + "] is invalid,please add it first");
            return;
        }

        root.SetState(subKey, state, data);
    }


    private RedPoint GetRoot(string key)
    {
        if (string.IsNullOrEmpty(key))
        {
            return null;
        }

        for (int i = 0; i < m_ListRedPointTrees.Count; i++)
        {
            if (m_ListRedPointTrees[i].key.Equals(key))
            {
                return m_ListRedPointTrees[i];
            }
        }

        return null;
    }

    public void Dispose()
    {
        for (int i = m_ListRedPointTrees.Count - 1; i >= 0; i--)
        {
            m_ListRedPointTrees[i].Dispose();
        }

        m_ListRedPointTrees.Clear(); ;
    }

    private static RedPointMgr s_Instance = null;
    private List<RedPoint> m_ListRedPointTrees = null;
}

Cuando lo use, primero llame al método Add para declarar qué puntos rojos construir un árbol de puntos rojos cuando se inicialice el juego; luego llame al método Init para agregar la visualización de puntos rojos y haga clic en devoluciones de llamada cuando se inicialice la interfaz de la interfaz de usuario; finalmente llame a SetState método en la lógica de la función.

3. Prueba de ejemplo

Por ejemplo: ahora hay mail1, mail2, mail3, mail4, mail5 y mail6 que necesitan avisos de puntos rojos, mail4, mail5 y mail6 son gestión comercial específica y todos los nodos secundarios de mail3, mial1, mail2 y mail3 necesitan siga sus nodos secundarios Variedad.

Primero construya, luego inicialice y finalmente escriba la devolución de llamada de la pantalla. (La operación es tan feroz como un tigre, y el salario es de 2k5 de un vistazo)

using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    public GameObject mail1RedPoint;
    public GameObject mail2RedPoint;
    public GameObject mail3RedPoint;
    public GameObject mail4RedPoint;
    public GameObject mail5RedPoint;
    public GameObject mail6RedPoint;

    public Text txtMail1;
    public Text txtMail2;
    public Text txtMail3;
    public Text txtMail4;
    public Text txtMail5;
    public Text txtMail6;

    public Button mail4Btn;
    public Button mail5Btn;
    public Button mail6Btn;
    public Button btnSet1;
    public Button btnSet2;
    public Button btnSet3;

    public int count1 = 5;
    public int count2 = 6;
    public int count3 = 7;

    string mail1 = "mail1";
    string mail2 = "mail2";
    string mail3 = "mail3";
    string mail4 = "mail4";
    string mail5 = "mail5";
    string mail6 = "mail6";

    private void Awake()
    {
        RedPointMgr.Init(gameObject);

        //在实际开发中,整个游戏的红点树要在游戏初始化时全部构建出来
        //声明mail1根节点,它的主key是mail1,无subKey,无父节点,红点类型是随着子节点变化
        RedPointMgr.instance.Add(mail1, null, null, RedPointType.Enternal);
        //声明mail2节点,它的主key是mail1,subKey是mail2,父节点是mail1,红点类型是随着子节点变化
        RedPointMgr.instance.Add(mail1, mail2, mail1, RedPointType.Enternal);
        //声明mail3节点,它的主key是mail1,subKey是mail3,父节点是mail2,红点类型是随着子节点变化
        RedPointMgr.instance.Add(mail1, mail3, mail2, RedPointType.Enternal);
        //声明mai4节点,它的主key是mail1,subKey是mail4,父节点是mail3,红点类型是点击即消失
        RedPointMgr.instance.Add(mail1, mail4, mail3, RedPointType.Once);
        //声明mai5节点,它的主key是mail1,subKey是mail5,父节点是mail3,红点类型是点击即消失
        RedPointMgr.instance.Add(mail1, mail5, mail3, RedPointType.Once);
        //声明mai5节点,它的主key是mail1,subKey是mail6,父节点是mail3,红点类型是点击即消失
        RedPointMgr.instance.Add(mail1, mail6, mail3, RedPointType.Once);

        //在实际开发中,初始化代码要写在对应UI界面的初始化函数中
        RedPointMgr.instance.Init(mail1, mail1, OnMail1Show);
        RedPointMgr.instance.Init(mail1, mail2, OnMail2Show);
        RedPointMgr.instance.Init(mail1, mail3, OnMail3Show);
        RedPointMgr.instance.Init(mail1, mail4, OnMail4Show, mail4Btn);
        RedPointMgr.instance.Init(mail1, mail5, OnMail5Show, mail5Btn);
        RedPointMgr.instance.Init(mail1, mail6, OnMail6Show, mail6Btn);

        btnSet1.onClick.AddListener(OnBtnSet1Click);
        btnSet2.onClick.AddListener(OnBtnSet2Click);
        btnSet3.onClick.AddListener(OnBtnSet3Click);
    }

    private void OnMail1Show(RedPointState state, int data)
    {
        mail1RedPoint.SetActive(state == RedPointState.Show);
        txtMail1.text = data.ToString();
    }

    private void OnMail2Show(RedPointState state, int data)
    {
        mail2RedPoint.SetActive(state == RedPointState.Show);
        txtMail2.text = data.ToString();
    }

    private void OnMail3Show(RedPointState state, int data)
    {
        mail3RedPoint.SetActive(state == RedPointState.Show);
        txtMail3.text = data.ToString();
    }

    private void OnMail4Show(RedPointState state, int data)
    {
        mail4RedPoint.SetActive(state == RedPointState.Show);
        txtMail4.text = data.ToString();
    }

    private void OnMail5Show(RedPointState state, int data)
    {
        mail5RedPoint.SetActive(state == RedPointState.Show);
        txtMail5.text = data.ToString();
        
    }

    private void OnMail6Show(RedPointState state, int data)
    {
        mail6RedPoint.SetActive(state == RedPointState.Show);
        txtMail6.text = data.ToString();
    }

    private void OnBtnSet1Click()
    {
        RedPointMgr.instance.SetState(mail1, mail4, count1 == 0 ? RedPointState.Hide : RedPointState.Show, count1);
    }

    private void OnBtnSet2Click()
    {
        RedPointMgr.instance.SetState(mail1, mail5, count2 == 0 ? RedPointState.Hide : RedPointState.Show, count2);
    }

    private void OnBtnSet3Click()
    {
        RedPointMgr.instance.SetState(mail1, mail6, count3 == 0 ? RedPointState.Hide : RedPointState.Show, count3);
    }
}

El tercer parámetro del método RedPointMgr.instance.SetState es el número que se muestra en el punto rojo, que también son los datos que la empresa debe contar. Si solo se muestra el punto rojo pero no se muestra el número, hay no hay necesidad de pasarlo.

 Ejecute el script de prueba, el efecto es el siguiente
 

4. Conclusión


Lo anterior es una implementación simple del sistema de puntos rojos.Escribí esto porque vi que mi gran colega escribía cada punto rojo a mano, lo que realmente me sorprendió.

No quiero escribir tanto código cv.

PD de nuevo:

Construí un blog personal con hexo hace unos días, bienvenido a venir a jugar conmigo. Ming-e https://ming-e.space/

Supongo que te gusta

Origin blog.csdn.net/s_GQY/article/details/126900574
Recomendado
Clasificación