简易版GameFramework游戏框架搭建教程(五)DataNode

这次我们来编写一个比较简单的模块——DataNode(数据结点)

下面是官网的介绍



首先新建一个DataNode文件夹,在其中新建DataNode类与DataManager类


DataNode是数据结点类,存储结点数据以及父子结点信息。DataNodeManager是数据结点管理器,负责管理根结点


打开DataNode类,为其添加对应的字段与属性

/// <summary>
/// 数据结点
/// </summary>
public class DataNode {

    public static readonly DataNode[] s_EmptyArray = new DataNode[] { };
    public static readonly string[] s_PathSplit = new string[] { ".", "/", "\\" };

    /// <summary>
    /// 结点名称
    /// </summary>
    public string Name { get; private set; }

    /// <summary>
    /// 结点全名
    /// </summary>
    public string FullName
    {
        get
        {
            return Parent == null ? Name : string.Format("{0}{1}{2}", Parent.FullName,s_PathSplit[0], Name);
        }
    }

    /// <summary>
    /// 结点数据
    /// </summary>
    private object m_Data;


    /// <summary>
    /// 父结点
    /// </summary>
    public DataNode Parent { get; private set; }

    /// <summary>
    /// 子结点
    /// </summary>
    private List<DataNode> m_Childs;

    /// <summary>
    /// 子结点数量
    /// </summary>
    public int ChildCount
    {
        get
        {
            return m_Childs != null ? m_Childs.Count : 0;
        }
    }

}

在添加构造方法进行数据初始化之前,我们先添加一个检测数据结点名称是否合法的方法

    /// <summary>
    /// 检测数据结点名称是否合法。
    /// </summary>
    /// <param name="name">要检测的数据节点名称。</param>
    /// <returns>是否是合法的数据结点名称。</returns>
    private static bool IsValidName(string name)
    {
        if (string.IsNullOrEmpty(name))
        {
            return false;
        }


        foreach (string pathSplit in s_PathSplit)
        {
            if (name.Contains(pathSplit))
            {
                return false;
            }
        }


        return true;
    }

然后就可以添加构造方法了

    public DataNode(string name, DataNode parent)
    {
        if (!IsValidName(name))
        {
            Debug.LogError("数据结点名字不合法:" + name);
        }

        Name = name;
        m_Data = null;
        Parent = parent;
        m_Childs = null;
    }

接下来添加结点数据的相关方法

    /// <summary>
    /// 获取结点数据
    /// </summary>
    public T GetData<T>()
    {
        return (T)m_Data;
    }

    /// <summary>
    /// 设置结点数据
    /// </summary>
    public void SetData(object data)
    {
        m_Data = data;
    }

添加子结点的相关方法

子结点的获取与增加

    /// <summary>
    /// 根据索引获取子数据结点
    /// </summary>
    /// <param name="index">子数据结点的索引</param>
    /// <returns>指定索引的子数据结点,如果索引越界,则返回空</returns>
    public DataNode GetChild(int index)
    {
        return index >= ChildCount ? null : m_Childs[index];
    }

    /// <summary>
    /// 根据名称获取子数据结点
    /// </summary>
    /// <param name="name">子数据结点名称</param>
    /// <returns>指定名称的子数据结点,如果没有找到,则返回空</returns>
    public DataNode GetChild(string name)
    {
        if (!IsValidName(name))
        {
            Debug.LogError("子结点名称不合法,无法获取");
            return null;
        }

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

        foreach (DataNode child in m_Childs)
        {
            if (child.Name == name)
            {
                return child;
            }
        }

        return null;
    }

    /// <summary>
    /// 根据名称获取或增加子数据结点
    /// </summary>
    /// <param name="name">子数据结点名称</param>
    /// <returns>指定名称的子数据结点,如果对应名称的子数据结点已存在,则返回已存在的子数据结点,否则增加子数据结点</returns>
    public DataNode GetOrAddChild(string name)
    {
        DataNode node = GetChild(name);
        if (node != null)
        {
            return node;
        }

        node = new DataNode(name, this);

        if (m_Childs == null)
        {
            m_Childs = new List<DataNode>();
        }

        m_Childs.Add(node);

        return node;
    }

子结点的移除

    /// <summary>
    /// 根据索引移除子数据结点
    /// </summary>
    /// <param name="index">子数据结点的索引位置</param>
    public void RemoveChild(int index)
    {
        DataNode node = GetChild(index);
        if (node == null)
        {
            return;
        }

        node.Clear();
        m_Childs.Remove(node);
    }

    /// <summary>
    /// 根据名称移除子数据结点
    /// </summary>
    /// <param name="name">子数据结点名称</param>
    public void RemoveChild(string name)
    {
        DataNode node = GetChild(name);
        if (node == null)
        {
            return;
        }

        node.Clear();
        m_Childs.Remove(node);
    }

    /// <summary>
    /// 移除当前数据结点的数据和所有子数据结点
    /// </summary>
    public void Clear()
    {
        m_Data = null;
        if (m_Childs != null)
        {
            foreach (DataNode child in m_Childs)
            {
                child.Clear();
            }

            m_Childs.Clear();
        }
    }

到这里DataNode就编写完成了,整个模块也算完成一半了

接下来打开DataNodeManager类,使其继承ManagerBase,添加对应字段与属性,并在构造方法中初始化数据

/// <summary>
/// 数据结点管理器
/// </summary>
public class DataNodeManager : ManagerBase {

    /// <summary>
    /// 根结点
    /// </summary>
    public DataNode Root { get; private set; }

    /// <summary>
    /// 根结点名称
    /// </summary>
    private const string RootName = "<Root>";

    public DataNodeManager()
    {
        Root = new DataNode(RootName, null);
    }

    public override void Init()
    {

    }

    public override void Shutdown()
    {
        Root.Clear();
        Root = null;
    }

    public override void Update(float elapseSeconds, float realElapseSeconds)
    {

    }

在添加结点相关的方法之前,我们需要先添加一个用来切分结点路径的方法

    /// <summary>
    /// 数据结点路径切分
    /// </summary>
    /// <param name="path">要切分的数据结点路径</param>
    /// <returns>切分后的字符串数组</returns>
    private static string[] GetSplitPath(string path)
    {
        if (string.IsNullOrEmpty(path))
        {
            return s_EmptyStringArray;
        }

        return path.Split(DataNode.s_PathSplit, StringSplitOptions.RemoveEmptyEntries);
    }
这个方法能够将形如aaa.bbb.ccc的路径切分成{aaa,bbb,ccc}的字符串数组,以便我们通过遍历该数组,逐步寻找到最末尾的子结点


接下来添加结点相关的方法

结点的获取与增加

    /// <summary>
    /// 获取数据结点。
    /// </summary>
    /// <param name="path">相对于 node 的查找路径</param>
    /// <param name="node">查找起始结点</param>
    /// <returns>指定位置的数据结点,如果没有找到,则返回空</returns>
    public DataNode GetNode(string path, DataNode node = null)
    {
        DataNode current = (node ?? Root);

        //获取子结点路径的数组
        string[] splitPath = GetSplitPath(path);

        foreach (string ChildName in splitPath)
        {
            //根据数组里的路径名获取子结点
            current = current.GetChild(ChildName);
            if (current == null)
            {
                return null;
            }
        }

        return current;
    }

    /// <summary>
    /// 获取或增加数据结点
    /// </summary>
    /// <param name="path">相对于 node 的查找路径</param>
    /// <param name="node">查找起始结点</param>
    /// <returns>指定位置的数据结点,如果没有找到,则增加相应的数据结点</returns>
    public DataNode GetOrAddNode(string path, DataNode node = null)
    {
        DataNode current = (node ?? Root);
        string[] splitPath = GetSplitPath(path);
        foreach (string childName in splitPath)
        {
            current = current.GetOrAddChild(childName);
        }

        return current;
    }

移除结点

    /// <summary>
    /// 移除数据结点
    /// </summary>
    /// <param name="path">相对于 node 的查找路径</param>
    /// <param name="node">查找起始结点</param>
    public void RemoveNode(string path, DataNode node = null)
    {
        DataNode current = (node ?? Root);
        DataNode parent = current.Parent;
        string[] splitPath = GetSplitPath(path);
        foreach (string childName in splitPath)
        {
            parent = current;
            current = current.GetChild(childName);
            if (current == null)
            {
                return;
            }
        }

        if (parent != null)
        {
            parent.RemoveChild(current.Name);
        }
    }

最后添加结点数据的相关方法

    /// <summary>
    /// 根据类型获取数据结点的数据
    /// </summary>
    /// <typeparam name="T">要获取的数据类型</typeparam>
    /// <param name="path">相对于 node 的查找路径</param>
    /// <param name="node">查找起始结点</param>
    public T GetData<T>(string path, DataNode node = null)
    {
        DataNode current = GetNode(path, node);
        if (current == null)
        {
            Debug.Log("要获取数据的结点不存在:" + path);
            return default(T);
        }

        return current.GetData<T>();

    }

    /// <summary>
    /// 设置数据结点的数据。
    /// </summary>
    /// <param name="path">相对于 node 的查找路径。</param>
    /// <param name="data">要设置的数据</param>
    /// <param name="node">查找起始结点</param>
    public void SetData(string path, object data, DataNode node = null)
    {
        DataNode current = GetOrAddNode(path, node);
        current.SetData(data);
    }

现在DataNode模块已经完全编写完成,该测试一下了

先仿照我们在上一章的步骤建立起测试需要的文件夹,脚本和场景


打开脚本,在Start方法里编写测试代码(这里我就直接仿照官网的文档例子来写测试代码了)

public class DataNodeTestMain : MonoBehaviour {

    private void Start()
    {
        //根据绝对路径设置与获取数据
        DataNodeManager dataNodeManager = FrameworkEntry.Instance.GetManager<DataNodeManager>();
        dataNodeManager.SetData("Player.Name", "Ellan");
        string playerName = dataNodeManager.GetData<string>("Player.Name");
        Debug.Log(playerName);

        //根据相对路径设置与获取数据
        DataNode playerNode = dataNodeManager.GetNode("Player");
        dataNodeManager.SetData("Level", 99, playerNode);
        int playerLevel = dataNodeManager.GetData<int>("Level", playerNode);
        Debug.Log(playerLevel);

        //直接通过数据结点来操作
        DataNode playerExpNode = playerNode.GetOrAddChild("Exp");
        playerExpNode.SetData(1000);
        int playerExp = playerExpNode.GetData<int>();
        Debug.Log(playerExp);
    }
}

启动游戏,看看能不能顺利跑起来吧



猜你喜欢

转载自blog.csdn.net/qq_32821435/article/details/80499804