这次我们来编写一个比较简单的模块——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模块已经完全编写完成,该测试一下了
先仿照我们在上一章的步骤建立起测试需要的文件夹,脚本和场景
打开脚本,在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);
}
}
启动游戏,看看能不能顺利跑起来吧