//... static void FinalizeConnection(Port sourcePort, FlowNode targetNode) { if (sourcePort == null || targetNode == null) { return; } Port source = null; Port target = null; if (sourcePort is ValueOutput || sourcePort is FlowOutput) { source = sourcePort; target = targetNode.GetFirstInputOfType(sourcePort.type); } else { source = targetNode.GetFirstOutputOfType(sourcePort.type); target = sourcePort; } BinderConnection.Create(source, target); }
///---------------------------------------------------------------------------------------------- ///---------------------------------------------------------------------------------------------- ///Convert nodes to macro (with a bit of hocus pocus) public static void ConvertNodesToMacro(List <Node> originalNodes) { if (originalNodes == null || originalNodes.Count == 0) { return; } if (!UnityEditor.EditorUtility.DisplayDialog("Convert to Macro", "This will create a new Macro out of the nodes.\nPlease note that since Macros are assets, Scene Object references will not be saved.\nThe Macro can NOT be unpacked later on.\nContinue?", "Yes", "No!")) { return; } //create asset var newMacro = EditorUtils.CreateAsset <Macro>(); if (newMacro == null) { return; } //undo var graph = (FlowScriptBase)originalNodes[0].graph; graph.RecordUndo("Convert To Macro"); //clone nodes var cloned = Graph.CloneNodes(originalNodes, newMacro, -newMacro.translation); //clear initial "example" ports newMacro.inputDefinitions.Clear(); newMacro.outputDefinitions.Clear(); //cache used ports var inputMergeMapSource = new Dictionary <Port, Port>(); var inputMergeMapTarget = new Dictionary <Port, Port>(); var outputMergeMapTarget = new Dictionary <Port, Port>(); var outputMergeMapSource = new Dictionary <Port, Port>(); //relink copied nodes to inside macro entry/exit for (var i = 0; i < originalNodes.Count; i++) { var originalNode = originalNodes[i]; //create macro entry node port definitions and link those to input ports of cloned nodes inside foreach (var originalInputConnection in originalNode.inConnections.OfType <BinderConnection>()) { //only do stuff if link source node is not part of the clones if (originalNodes.Contains(originalInputConnection.sourceNode)) { continue; } Port defSourcePort = null; //merge same input ports and same target ports if (!inputMergeMapSource.TryGetValue(originalInputConnection.sourcePort, out defSourcePort)) { if (!inputMergeMapTarget.TryGetValue(originalInputConnection.targetPort, out defSourcePort)) { //remark: we use sourcePort.type instead of target port type, so that connections remain assignable var def = new DynamicPortDefinition(originalInputConnection.targetPort.name, originalInputConnection.sourcePort.type); defSourcePort = newMacro.AddInputDefinition(def); inputMergeMapTarget[originalInputConnection.targetPort] = defSourcePort; } inputMergeMapSource[originalInputConnection.sourcePort] = defSourcePort; } if (defSourcePort.CanAcceptConnections()) //check this for case of merged FlowPorts { var targetPort = (cloned[i] as FlowNode).GetInputPort(originalInputConnection.targetPortID); BinderConnection.Create(defSourcePort, targetPort); } } //create macro exit node port definitions and link those to output ports of cloned nodes inside foreach (var originalOutputConnection in originalNode.outConnections.OfType <BinderConnection>()) { //only do stuff if link target node is not part of the clones if (originalNodes.Contains(originalOutputConnection.targetNode)) { continue; } Port defTargetPort = null; //merge same input ports and same target ports if (!outputMergeMapTarget.TryGetValue(originalOutputConnection.targetPort, out defTargetPort)) { if (!outputMergeMapSource.TryGetValue(originalOutputConnection.sourcePort, out defTargetPort)) { var def = new DynamicPortDefinition(originalOutputConnection.sourcePort.name, originalOutputConnection.sourcePort.type); defTargetPort = newMacro.AddOutputDefinition(def); outputMergeMapSource[originalOutputConnection.sourcePort] = defTargetPort; } outputMergeMapTarget[originalOutputConnection.targetPort] = defTargetPort; } if (defTargetPort.CanAcceptConnections()) //check this for case of merged ValuePorts { var sourcePort = (cloned[i] as FlowNode).GetOutputPort(originalOutputConnection.sourcePortID); BinderConnection.Create(sourcePort, defTargetPort); } } } //Delete originals var originalBounds = RectUtils.GetBoundRect(originalNodes.Select(n => n.rect).ToArray()); foreach (var node in originalNodes.ToArray()) { graph.RemoveNode(node, false); } //Create MacroWrapper. Relink macro wrapper to outside nodes var wrapperPos = originalBounds.center; wrapperPos.x = (int)wrapperPos.x; wrapperPos.y = (int)wrapperPos.y; var wrapper = graph.AddMacroNode(newMacro, wrapperPos, null, null); wrapper.GatherPorts(); foreach (var pair in inputMergeMapSource) { var source = pair.Key; var target = wrapper.GetInputPort(pair.Value.ID); BinderConnection.Create(source, target); } foreach (var pair in outputMergeMapTarget) { var source = wrapper.GetOutputPort(pair.Value.ID); var target = pair.Key; BinderConnection.Create(source, target); } //organize a bit var clonedBounds = RectUtils.GetBoundRect(cloned.Select(n => n.rect).ToArray()); newMacro.entry.position = new Vector2((int)(clonedBounds.xMin - 300), (int)clonedBounds.yMin); newMacro.exit.position = new Vector2((int)(clonedBounds.xMax + 300), (int)clonedBounds.yMin); newMacro.translation = -newMacro.entry.position + new Vector2(300, 300); // //validate and save newMacro.Validate(); UnityEditor.AssetDatabase.SaveAssets(); }
//Seal it... sealed protected override void DrawNodeConnections(Rect drawCanvas, bool fullDrawPass, Vector2 canvasMousePos, float zoomFactor) { var e = Event.current; //Port container graphics if (fullDrawPass || drawCanvas.Overlaps(rect)) { GUI.Box(new Rect(rect.x - 8, rect.y + 2, 10, rect.height), string.Empty, CanvasStyles.nodePortContainer); GUI.Box(new Rect(rect.xMax - 2, rect.y + 2, 10, rect.height), string.Empty, CanvasStyles.nodePortContainer); } /// if (fullDrawPass || drawCanvas.Overlaps(rect)) { var portRect = new Rect(0, 0, 10, 10); //INPUT Ports if (orderedInputs != null) { Port instancePort = null; for (var i = 0; i < orderedInputs.Length; i++) { var port = orderedInputs[i]; var canConnect = true; if (clickedPort != null) { if ((port == clickedPort) || (clickedPort.parent == port.parent) || (clickedPort.IsInputPort()) || (port.isConnected && port is ValueInput) || (!TypeConverter.HasConvertion(clickedPort.type, port.type))) { canConnect = false; } } port.pos = new Vector2(rect.x - 5, rect.y + port.posOffsetY); portRect.width = port.isConnected? 12:10; portRect.height = portRect.width; portRect.center = port.pos; //first gameobject or component port is considered to be the 'instance' port by convention if (port == firstValuePort) { if (port.IsUnitySceneObject()) { instancePort = port; } } //Port graphic if (clickedPort != null && !canConnect && clickedPort != port) { GUI.color = new Color(1, 1, 1, 0.3f); } GUI.Box(portRect, string.Empty, port.isConnected? CanvasStyles.nodePortConnected : CanvasStyles.nodePortEmpty); GUI.color = Color.white; //Tooltip if (portRect.Contains(e.mousePosition)) { var labelString = (canConnect || port.isConnected || port == clickedPort)? port.type.FriendlyName() : "Can't Connect Here"; var size = CanvasStyles.box.CalcSize(new GUIContent(labelString)); var rect = new Rect(0, 0, size.x + 10, size.y + 5); rect.x = portRect.x - size.x - 10; rect.y = portRect.y - size.y / 2; GUI.Box(rect, labelString, CanvasStyles.box); //Or value } else { if (port is ValueInput && !port.isConnected) { //Only these types are shown their value if (port.type.IsValueType || port.type == typeof(Type) || port.type == typeof(string) || port.IsUnityObject()) { var value = (port as ValueInput).serializedValue; string labelString = null; if (!(port as ValueInput).isDefaultValue) { if (value is UnityEngine.Object && value as UnityEngine.Object != null) { labelString = string.Format("<b><color=#66ff33>{0}</color></b>", (value as UnityEngine.Object).name); } else { labelString = value.ToStringAdvanced(); } } else if (port == instancePort) { var exists = true; if (graphAgent != null && typeof(Component).IsAssignableFrom(port.type)) { exists = graphAgent.GetComponent(port.type) != null; } var color = exists? "66ff33" : "ff3300"; labelString = string.Format("<color=#{0}><b>♟ <i>Self</i></b></color>", color); } else { GUI.color = new Color(1, 1, 1, 0.15f); labelString = value.ToStringAdvanced(); } var size = CanvasStyles.label.CalcSize(new GUIContent(labelString)); var rect = new Rect(0, 0, size.x, size.y); rect.x = portRect.x - size.x - 5; rect.y = portRect.y - size.y * 0.3f; //*0.3? something's wrong here. FIX GUI.Label(rect, labelString, CanvasStyles.label); GUI.color = Color.white; } } } if (GraphEditorUtility.allowClick) { //Right click removes connections if (port.isConnected && e.type == EventType.ContextClick && portRect.Contains(e.mousePosition)) { foreach (var c in GetInPortConnections(port)) { graph.RemoveConnection(c); } e.Use(); return; } //Click initialize new drag & drop connection if (e.type == EventType.MouseDown && e.button == 0 && portRect.Contains(e.mousePosition)) { if (port.CanAcceptConnections()) { dragDropMisses = 0; clickedPort = port; e.Use(); } } //Drop on creates connection if (e.type == EventType.MouseUp && e.button == 0 && clickedPort != null) { if (portRect.Contains(e.mousePosition) && port.CanAcceptConnections()) { BinderConnection.Create(clickedPort, port); clickedPort = null; e.Use(); } } } } } //OUTPUT Ports if (orderedOutputs != null) { for (var i = 0; i < orderedOutputs.Length; i++) { var port = orderedOutputs[i]; var canConnect = true; if (clickedPort != null) { if ((port == clickedPort) || (clickedPort.parent == port.parent) || (clickedPort.IsOutputPort()) || (port.isConnected && port is FlowOutput) || (!TypeConverter.HasConvertion(port.type, clickedPort.type))) { canConnect = false; } } port.pos = new Vector2(rect.xMax + 5, rect.y + port.posOffsetY); portRect.width = port.isConnected? 12:10; portRect.height = portRect.width; portRect.center = port.pos; //Port graphic if (clickedPort != null && !canConnect && clickedPort != port) { GUI.color = new Color(1, 1, 1, 0.3f); } GUI.Box(portRect, string.Empty, port.isConnected? CanvasStyles.nodePortConnected : CanvasStyles.nodePortEmpty); GUI.color = Color.white; //Tooltip if (portRect.Contains(e.mousePosition)) { var labelString = (canConnect || port.isConnected || port == clickedPort)? port.type.FriendlyName() : "Can't Connect Here"; var size = CanvasStyles.box.CalcSize(new GUIContent(labelString)); var rect = new Rect(0, 0, size.x + 10, size.y + 5); rect.x = portRect.x + 15; rect.y = portRect.y - portRect.height / 2; GUI.Box(rect, labelString, CanvasStyles.box); } if (GraphEditorUtility.allowClick) { //Right click removes connections if (e.type == EventType.ContextClick && portRect.Contains(e.mousePosition)) { foreach (var c in GetOutPortConnections(port)) { graph.RemoveConnection(c); } e.Use(); return; } //Click initialize new drag & drop connection if (e.type == EventType.MouseDown && e.button == 0 && portRect.Contains(e.mousePosition)) { if (port.CanAcceptConnections()) { dragDropMisses = 0; clickedPort = port; e.Use(); } } //Drop on creates connection if (e.type == EventType.MouseUp && e.button == 0 && clickedPort != null) { if (portRect.Contains(e.mousePosition) && port.CanAcceptConnections()) { BinderConnection.Create(port, clickedPort); clickedPort = null; e.Use(); } } } } } } ///ACCEPT CONNECTION if (clickedPort != null && e.type == EventType.MouseUp) { ///ON NODE if (rect.Contains(e.mousePosition)) { var cachePort = clickedPort; var menu = new GenericMenu(); if (cachePort is ValueOutput || cachePort is FlowOutput) { if (orderedInputs != null) { foreach (var _port in orderedInputs.Where(p => p.CanAcceptConnections() && TypeConverter.HasConvertion(cachePort.type, p.type))) { var port = _port; menu.AddItem(new GUIContent(string.Format("To: '{0}'", port.name)), false, () => { BinderConnection.Create(cachePort, port); }); } } } else { if (orderedOutputs != null) { foreach (var _port in orderedOutputs.Where(p => p.CanAcceptConnections() && TypeConverter.HasConvertion(p.type, cachePort.type))) { var port = _port; menu.AddItem(new GUIContent(string.Format("From: '{0}'", port.name)), false, () => { BinderConnection.Create(port, cachePort); }); } } } //append menu items menu = OnDragAndDropPortContextMenu(menu, cachePort); //if there is only 1 option, just do it if (menu.GetItemCount() == 1) { EditorUtils.GetMenuItems(menu)[0].func(); } else { GraphEditorUtility.PostGUI += () => { menu.ShowAsContext(); }; } clickedPort = null; e.Use(); /// ///ON CANVAS } else { dragDropMisses++; if (dragDropMisses == graph.allNodes.Count && clickedPort != null) { var cachePort = clickedPort; clickedPort = null; DoContextPortConnectionMenu(cachePort, e.mousePosition, zoomFactor); e.Use(); } } } //Temp connection line when linking if (clickedPort != null && clickedPort.parent == this) { var from = clickedPort.pos; var to = e.mousePosition; var xDiff = (from.x - to.x) * 0.8f; xDiff = to.x > from.x? xDiff : -xDiff; var tangA = clickedPort is FlowInput || clickedPort is ValueInput? new Vector2(xDiff, 0) : new Vector2(-xDiff, 0); var tangB = tangA * -1; Handles.DrawBezier(from, to, from + tangA, to + tangB, new Color(0.5f, 0.5f, 0.8f, 0.8f), null, 3); } //Actualy draw the existing connections for (var i = 0; i < outConnections.Count; i++) { var binder = outConnections[i] as BinderConnection; if (binder != null) //for in case it's MissingConnection { var sourcePort = binder.sourcePort; var targetPort = binder.targetPort; if (sourcePort != null && targetPort != null) { if (fullDrawPass || drawCanvas.Overlaps(RectUtils.GetBoundRect(sourcePort.pos, targetPort.pos))) { binder.DrawConnectionGUI(sourcePort.pos, targetPort.pos); } } } } }