自定义节点编辑器xNode——NodeDataCache(三)(番外)

本文中的项目来源于 https://github.com/Siccity/xNode
本章节内容和之后关系不太大,可不看

1.6 NodeDataCache

我们能可以在编辑器中预处理反射数据,这样我们就不必在运行时进行处理

1.6.1 PortDataCache

这个类继承了Dictionary和ISerializationCallbackReceiver接口,通过Type来保存NodePort

[System.Serializable]
private class PortDataCache : Dictionary<System.Type, List<NodePort>>, ISerializationCallbackReceiver {
    [SerializeField] private List<System.Type> keys = new List<System.Type>();
    [SerializeField] private List<List<NodePort>> values = new List<List<NodePort>>();

    // save the dictionary to lists
    public void OnBeforeSerialize() {
        keys.Clear();
        values.Clear();
        foreach (var pair in this) {
            keys.Add(pair.Key);
            values.Add(pair.Value);
        }
    }

    // load dictionary from lists
    public void OnAfterDeserialize() {
        this.Clear();

        if (keys.Count != values.Count)
            throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable."));

        for (int i = 0; i < keys.Count; i++)
            this.Add(keys[i], values[i]);
    }
}
1.6.2 基本属性

在NodeDataCache中有一个portDataCache字典保存数据,还有一个Initialized布尔值来确认是否初始化成功

public static class NodeDataCache {
	private static PortDataCache portDataCache;
	private static bool Initialized { get { return portDataCache != null; } }
1.6.3 函数介绍
1.6.3.1 GetNodeFields
///通过反射根据Type获取字段信息(包括私有和公有的)
public static List<FieldInfo> GetNodeFields(System.Type nodeType) 
{
    List<System.Reflection.FieldInfo> fieldInfo = new List<System.Reflection.FieldInfo>(nodeType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance));

    //GetFields不返回继承的私有字段,所以需要再遍历获取那些私有的字段
    System.Type tempType = nodeType;
    while ((tempType = tempType.BaseType) != typeof(XNode.Node)) {
        fieldInfo.AddRange(tempType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance));
    }
    return fieldInfo;
}
1.6.3.2 CachePorts
private static void CachePorts(System.Type nodeType) 
{
	//通过1.6.3.1的函数获取Node的所有字段信息
    List<System.Reflection.FieldInfo> fieldInfo = GetNodeFields(nodeType);

    for (int i = 0; i < fieldInfo.Count; i++) {

        //通过Linq获取InputAttribute和OutputAttribute
        object[] attribs = fieldInfo[i].GetCustomAttributes(true);
        Node.InputAttribute inputAttrib = attribs.FirstOrDefault(x => x is Node.InputAttribute) as Node.InputAttribute;
        Node.OutputAttribute outputAttrib = attribs.FirstOrDefault(x => x is Node.OutputAttribute) as Node.OutputAttribute;
		
		//如果都没有就跳过
        if (inputAttrib == null && outputAttrib == null) continue;
		//如果都有就提示不能都有
        if (inputAttrib != null && outputAttrib != null) 
        	Debug.LogError("Field " + fieldInfo[i].Name + " of type " + nodeType.FullName + " cannot be both input and output.");
        else {
        	//如果portDataCache没有这个Key就添加一个Key
            if (!portDataCache.ContainsKey(nodeType)) portDataCache.Add(nodeType, new List<NodePort>());
            //为portDataCache添加NodePort
            portDataCache[nodeType].Add(new NodePort(fieldInfo[i]));
        }
    }
}
1.6.3.3 BuildCache
///缓存类型
private static void BuildCache() 
{
    portDataCache = new PortDataCache();
    System.Type baseType = typeof(Node);
    List<System.Type> nodeTypes = new List<System.Type>();
    System.Reflection.Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();

    //遍历程序集并将节点类型添加到列表中
    foreach (Assembly assembly in assemblies) 
    {
        //跳过某些dll库以提高遍历速度
        string assemblyName = assembly.GetName().Name;
        int index = assemblyName.IndexOf('.');
        if (index != -1) assemblyName = assemblyName.Substring(0, index);
        switch (assemblyName) {
            case "UnityEditor":
            case "UnityEngine":
            case "System":
            case "mscorlib":
                continue;
            default:
                nodeTypes.AddRange(assembly.GetTypes().Where(t => !t.IsAbstract && baseType.IsAssignableFrom(t)).ToArray());
                break;
        }
    }
	
	//然后根据已经加载的Node类型,使用1.6.3.2的函数缓存节点的字段信息
    for (int i = 0; i < nodeTypes.Count; i++) {
        CachePorts(nodeTypes[i]);
    }
}
1.6.3.4 UpdatePorts
///更新静态端口以反射类的字段
public static void UpdatePorts(Node node, Dictionary<string, NodePort> ports) 
{
	//根据前面的介绍,这个函数把程序集中所有和Node有关的字段都加载到了目前的一个字典中
	//如果没初始化就先初始化一下
    if (!Initialized) BuildCache();
	
    Dictionary<string, NodePort> staticPorts = new Dictionary<string, NodePort>();
    Dictionary<string, List<NodePort>> removedPorts = new Dictionary<string, List<NodePort>>();
    //当前节点的类型
    System.Type nodeType = node.GetType();

    List<NodePort> typePortCache;
    //如果能从字典里根据Key获取到值,那么就把信息复制到typePortCache中
    if (portDataCache.TryGetValue(nodeType, out typePortCache)) {
        for (int i = 0; i < typePortCache.Count; i++) {
            staticPorts.Add(typePortCache[i].fieldName, portDataCache[nodeType][i]);
        }
    }

    //清除接口-删除不存在的静态接口-更新静态接口类型
    //循环当前节点接口
    foreach (NodePort port in ports.Values.ToList()) {
        // 如果接口仍然存在,检查它是否已经更改
        NodePort staticPort;
        if (staticPorts.TryGetValue(port.fieldName, out staticPort)) {
            //如果接口存在但设置错误,就删除它,稍后重新添加
            if (port.IsDynamic || port.direction != staticPort.direction || port.connectionType != staticPort.connectionType || port.typeConstraint != staticPort.typeConstraint) {
                //如果接口不是动态的且方向没有改变,就将其添加到列表中,以便我们恢复接口原本的连接
                if (!port.IsDynamic && port.direction == staticPort.direction) removedPorts.Add(port.fieldName, port.GetConnections());
                port.ClearConnections();
                ports.Remove(port.fieldName);
            } else port.ValueType = staticPort.ValueType;
        }
        //如果接口不存在了就删除它
        else if (port.IsStatic) {
            port.ClearConnections();
            ports.Remove(port.fieldName);
        }
    }
    //添加丢失的接口
    foreach (NodePort staticPort in staticPorts.Values) {
        if (!ports.ContainsKey(staticPort.fieldName)) {
            NodePort port = new NodePort(staticPort, node);
            //如果我们刚刚删除了接口,尝试恢复接口原本的连接
            List<NodePort> reconnectConnections;
            if (removedPorts.TryGetValue(staticPort.fieldName, out reconnectConnections)) {
                for (int i = 0; i < reconnectConnections.Count; i++) {
                    NodePort connection = reconnectConnections[i];
                    if (connection == null) continue;
                    if (port.CanConnectTo(connection)) port.Connect(connection);
                }
            }
            ports.Add(staticPort.fieldName, port);
        }
    }
}

这一部分就是让我们能够预先处理需要反射的数据,这样在运行时就不需要处理这些数据

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值