diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index ec93cc1..6648c2d 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -254,8 +254,8 @@ namespace XNodeEditor { } [Obsolete("Use DynamicPortList instead")] - public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { - DynamicPortList(fieldName, type, serializedObject, io, connectionType, typeConstraint, onCreation); + public static void InstancePortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None,Type inputTypeConstraintBaseType = null, Action onCreation = null) { + DynamicPortList(fieldName, type, serializedObject, io, connectionType, typeConstraint, inputTypeConstraintBaseType,onCreation); } #endregion @@ -276,8 +276,9 @@ namespace XNodeEditor { /// Value type of added dynamic ports /// The serializedObject of the node /// Connection type of added dynamic ports + /// 并且时可用 /// Called on the list on creation. Use this if you want to customize the created ReorderableList - public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None, Action onCreation = null) { + public static void DynamicPortList(string fieldName, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType = XNode.Node.ConnectionType.Multiple, XNode.Node.TypeConstraint typeConstraint = XNode.Node.TypeConstraint.None,Type inputTypeConstraintBaseType = null, Action onCreation = null) { XNode.Node node = serializedObject.targetObject as XNode.Node; var indexedPorts = node.DynamicPorts.Select(x => { @@ -300,7 +301,7 @@ namespace XNodeEditor { // If a ReorderableList isn't cached for this array, do so. if (list == null) { SerializedProperty arrayData = serializedObject.FindProperty(fieldName); - list = CreateReorderableList(fieldName, dynamicPorts, arrayData, type, serializedObject, io, connectionType, typeConstraint, onCreation); + list = CreateReorderableList(fieldName, dynamicPorts, arrayData, type, serializedObject, io, connectionType, typeConstraint, inputTypeConstraintBaseType,onCreation); if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) rlc.Add(fieldName, list); else reorderableListCache.Add(serializedObject.targetObject, new Dictionary() { { fieldName, list } }); } @@ -308,7 +309,7 @@ namespace XNodeEditor { list.DoLayoutList(); } - private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint, Action onCreation) { + private static ReorderableList CreateReorderableList(string fieldName, List dynamicPorts, SerializedProperty arrayData, Type type, SerializedObject serializedObject, XNode.NodePort.IO io, XNode.Node.ConnectionType connectionType, XNode.Node.TypeConstraint typeConstraint,Type inputTypeConstraintBaseType, Action onCreation) { bool hasArrayData = arrayData != null && arrayData.isArray; XNode.Node node = serializedObject.targetObject as XNode.Node; ReorderableList list = new ReorderableList(dynamicPorts, null, true, true, true, true); @@ -398,7 +399,7 @@ namespace XNodeEditor { while (node.HasPort(newName)) newName = fieldName + " " + (++i); if (io == XNode.NodePort.IO.Output) node.AddDynamicOutput(type, connectionType, XNode.Node.TypeConstraint.None, newName); - else node.AddDynamicInput(type, connectionType, typeConstraint, newName); + else node.AddDynamicInput(type, connectionType, typeConstraint,inputTypeConstraintBaseType, newName); serializedObject.Update(); EditorUtility.SetDirty(node); if (hasArrayData) { @@ -471,8 +472,8 @@ namespace XNodeEditor { string newName = arrayData.name + " 0"; int i = 0; while (node.HasPort(newName)) newName = arrayData.name + " " + (++i); - if (io == XNode.NodePort.IO.Output) node.AddDynamicOutput(type, connectionType, typeConstraint, newName); - else node.AddDynamicInput(type, connectionType, typeConstraint, newName); + if (io == XNode.NodePort.IO.Output) node.AddDynamicOutput(type, connectionType, typeConstraint,newName); + else node.AddDynamicInput(type, connectionType, typeConstraint, inputTypeConstraintBaseType,newName); EditorUtility.SetDirty(node); dynamicPortCount++; } diff --git a/Scripts/Node.cs b/Scripts/Node.cs index 27e32c7..7fbf4c1 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -73,7 +73,7 @@ namespace XNode { [Obsolete("Use AddDynamicPort instead")] private NodePort AddInstancePort(Type type, NodePort.IO direction, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) { - return AddDynamicPort(type, direction, connectionType, typeConstraint, fieldName); + return AddDynamicPort(type, direction, connectionType, typeConstraint, null,fieldName); } [Obsolete("Use RemoveDynamicPort instead")] @@ -138,21 +138,21 @@ namespace XNode { /// Convenience function. /// /// - public NodePort AddDynamicInput(Type type, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) { - return AddDynamicPort(type, NodePort.IO.Input, connectionType, typeConstraint, fieldName); + public NodePort AddDynamicInput(Type type, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None,Type baseType = null, string fieldName = null) { + return AddDynamicPort(type, NodePort.IO.Input, connectionType, typeConstraint,baseType, fieldName); } /// Convenience function. /// /// public NodePort AddDynamicOutput(Type type, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) { - return AddDynamicPort(type, NodePort.IO.Output, connectionType, typeConstraint, fieldName); + return AddDynamicPort(type, NodePort.IO.Output, connectionType, typeConstraint, null,fieldName); } /// Add a dynamic, serialized port to this node. /// /// - private NodePort AddDynamicPort(Type type, NodePort.IO direction, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None, string fieldName = null) { + private NodePort AddDynamicPort(Type type, NodePort.IO direction, Node.ConnectionType connectionType = Node.ConnectionType.Multiple, Node.TypeConstraint typeConstraint = TypeConstraint.None,Type baseType = null, string fieldName = null) { if (fieldName == null) { fieldName = "dynamicInput_0"; int i = 0; @@ -161,7 +161,7 @@ namespace XNode { Debug.LogWarning("Port '" + fieldName + "' already exists in " + name, this); return ports[fieldName]; } - NodePort port = new NodePort(fieldName, type, direction, connectionType, typeConstraint, this); + NodePort port = new NodePort(fieldName, type, direction, connectionType, typeConstraint,baseType, this); ports.Add(fieldName, port); return port; } @@ -268,17 +268,20 @@ namespace XNode { public bool instancePortList { get { return dynamicPortList; } set { dynamicPortList = value; } } public bool dynamicPortList; public TypeConstraint typeConstraint; + public Type BaseType { get; } /// Mark a serializable field as an input port. You can access this through + /// 指定更准确的父类类型,只有当参数为才可用 /// Should we display the backing value for this port as an editor field? /// Should we allow multiple connections? /// Constrains which input connections can be made to this port /// If true, will display a reorderable list of inputs instead of a single port. Will automatically add and display values for lists and arrays - public InputAttribute(ShowBackingValue backingValue = ShowBackingValue.Unconnected, ConnectionType connectionType = ConnectionType.Multiple, TypeConstraint typeConstraint = TypeConstraint.None, bool dynamicPortList = false) { + public InputAttribute(ShowBackingValue backingValue = ShowBackingValue.Unconnected, ConnectionType connectionType = ConnectionType.Multiple, TypeConstraint typeConstraint = TypeConstraint.None,Type baseType = null, bool dynamicPortList = false) { this.backingValue = backingValue; this.connectionType = connectionType; this.dynamicPortList = dynamicPortList; this.typeConstraint = typeConstraint; + BaseType = baseType; } } diff --git a/Scripts/NodeDataCache.cs b/Scripts/NodeDataCache.cs index 886f128..5ea5c5a 100644 --- a/Scripts/NodeDataCache.cs +++ b/Scripts/NodeDataCache.cs @@ -36,7 +36,12 @@ namespace XNode { 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 + { + port.ValueType = staticPort.ValueType; + port.TypeConstraintBaseType = staticPort.TypeConstraintBaseType; + } } // If port doesn't exist anymore, remove it else if (port.IsStatic) { diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index 1000b23..5b62296 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -42,11 +42,28 @@ namespace XNode { if (value != null) _typeQualifiedName = value.AssemblyQualifiedName; } } + + public Type TypeConstraintBaseType { + get { + if (_typeConstraintBaseType == null && !string.IsNullOrEmpty(_typeConstraintBaseTypeQualifiedName)) + _typeConstraintBaseType = Type.GetType(_typeConstraintBaseTypeQualifiedName, false); + return _typeConstraintBaseType; + } + set { + _typeConstraintBaseType = value; + if (value != null) + { + _typeConstraintBaseTypeQualifiedName = value.AssemblyQualifiedName; + } + } + } + private Type valueType; - + private Type _typeConstraintBaseType; [SerializeField] private string _fieldName; [SerializeField] private Node _node; [SerializeField] private string _typeQualifiedName; + [SerializeField] private string _typeConstraintBaseTypeQualifiedName; [SerializeField] private List connections = new List(); [SerializeField] private IO _direction; [SerializeField] private Node.ConnectionType _connectionType; @@ -64,6 +81,7 @@ namespace XNode { _direction = IO.Input; _connectionType = (attribs[i] as Node.InputAttribute).connectionType; _typeConstraint = (attribs[i] as Node.InputAttribute).typeConstraint; + TypeConstraintBaseType = (attribs[i] as Node.InputAttribute).BaseType; } else if (attribs[i] is Node.OutputAttribute) { _direction = IO.Output; _connectionType = (attribs[i] as Node.OutputAttribute).connectionType; @@ -84,9 +102,10 @@ namespace XNode { } /// Construct a dynamic port. Dynamic ports are not forgotten on reimport, and is ideal for runtime-created ports. - public NodePort(string fieldName, Type type, IO direction, Node.ConnectionType connectionType, Node.TypeConstraint typeConstraint, Node node) { + public NodePort(string fieldName, Type type, IO direction, Node.ConnectionType connectionType, Node.TypeConstraint typeConstraint,Type baseType, Node node) { _fieldName = fieldName; this.ValueType = type; + TypeConstraintBaseType = baseType; _direction = direction; _node = node; _dynamic = true; @@ -257,7 +276,20 @@ namespace XNode { // If there isn't one of each, they can't connect if (input == null || output == null) return false; // Check input type constraints - if (input.typeConstraint == XNode.Node.TypeConstraint.Inherited && !input.ValueType.IsAssignableFrom(output.ValueType)) return false; + if (input.typeConstraint == XNode.Node.TypeConstraint.Inherited) + { + //无法分配,失败 + if (!input.ValueType.IsAssignableFrom(output.ValueType)) + { + return false; + } + + //如果存在指定基类,同时无法分配,失败 + if (input.TypeConstraintBaseType != null && !input.TypeConstraintBaseType.IsAssignableFrom(output.ValueType)) + { + return false; + } + } if (input.typeConstraint == XNode.Node.TypeConstraint.Strict && input.ValueType != output.ValueType) return false; // Check output type constraints if (output.typeConstraint == XNode.Node.TypeConstraint.Inherited && !output.ValueType.IsAssignableFrom(input.ValueType)) return false;