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

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

下面是官网的介绍



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


其中

DataNode是数据结点类,存储结点数据以及父子结点信息

DataNodeManager是数据结点管理器,负责管理根结点


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

/// 
/// 数据结点
/// 
public class DataNode {

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

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

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

    /// 
    /// 结点数据
    /// 
    private object m_Data;


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

    /// 
    /// 子结点
    /// 
    private List m_Childs;

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

}

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

    /// 
    /// 检测数据结点名称是否合法。
    /// 
    /// 要检测的数据节点名称。
    /// 是否是合法的数据结点名称。
    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;
    }

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

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

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

添加子结点的相关方法

子结点的获取与增加

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

    /// 
    /// 根据名称获取子数据结点
    /// 
    /// 子数据结点名称
    /// 指定名称的子数据结点,如果没有找到,则返回空
    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;
    }

    /// 
    /// 根据名称获取或增加子数据结点
    /// 
    /// 子数据结点名称
    /// 指定名称的子数据结点,如果对应名称的子数据结点已存在,则返回已存在的子数据结点,否则增加子数据结点
    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();
        }

        m_Childs.Add(node);

        return node;
    }

子结点的移除

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

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

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

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

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

            m_Childs.Clear();
        }
    }

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

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

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

    private static readonly string[] s_EmptyStringArray = new string[] { };

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

    /// 
    /// 根结点名称
    /// 
    private const string RootName = "";

    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)
    {

    }

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

    /// 
    /// 数据结点路径切分
    /// 
    /// 要切分的数据结点路径
    /// 切分后的字符串数组
    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}的字符串数组,以便我们通过遍历该数组,逐步寻找到最末尾的子结点


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

结点的获取与增加

    /// 
    /// 获取数据结点。
    /// 
    /// 相对于 node 的查找路径
    /// 查找起始结点
    /// 指定位置的数据结点,如果没有找到,则返回空
    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;
    }

    /// 
    /// 获取或增加数据结点
    /// 
    /// 相对于 node 的查找路径
    /// 查找起始结点
    /// 指定位置的数据结点,如果没有找到,则增加相应的数据结点
    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;
    }

移除结点

    /// 
    /// 移除数据结点
    /// 
    /// 相对于 node 的查找路径
    /// 查找起始结点
    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);
        }
    }

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

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

        return current.GetData();

    }

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

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

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

简易版GameFramework游戏框架搭建教程(五)DataNode_第1张图片

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

public class DataNodeTestMain : MonoBehaviour {

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

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

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

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

简易版GameFramework游戏框架搭建教程(五)DataNode_第2张图片


你可能感兴趣的:(简易版GameFramework游戏框架搭建教程(五)DataNode)