/// <summary> Create a node and save it in the graph asset </summary> public virtual Node CreateNode(Type type, Vector2 position) { XNode.Node node = target.AddNode(type); node.position = position; if (string.IsNullOrEmpty(node.name)) { // Automatically remove redundant 'Node' postfix string typeName = type.Name; if (typeName.EndsWith("Node")) { typeName = typeName.Substring(0, typeName.LastIndexOf("Node")); } node.name = UnityEditor.ObjectNames.NicifyVariableName(typeName); } AssetDatabase.AddObjectToAsset(node, target); if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } NodeEditorWindow.RepaintAll(); return(node); }
public static void DrawPorts(XNode.Node target) { EditorGUILayout.BeginHorizontal(); NodeEditorGUILayout.PortField(new GUIContent("Input"), target.GetInputPort("input"), GUILayout.MinWidth(0)); NodeEditorGUILayout.PortField(new GUIContent("Output"), target.GetOutputPort("output"), GUILayout.MinWidth(0)); EditorGUILayout.EndHorizontal(); }
public void OnUpdateNode(XNode.Node n) { //Disconnect destroy all connections if node is OperatorNode and operator type changed if (n is OperatorNode) { var opNode = (OperatorNode)n; if (opNode.opType != opNode.opType_old) { opNode.opType_old = opNode.opType; foreach (XNode.NodePort port in opNode.Inputs) { port.ClearConnections(); } if (opNode.opType == OperatorType.Remap) { opNode.parameters = new float[] { 1, 1, 1, 0, 0, 0 } } ; } } //Update preview images of nodes affected by given node on other thread System.Threading.ThreadPool.QueueUserWorkItem(delegate { RippleUpdate(n); }, null); } }
public ConversationIterator(ConversationGraph graph) { this.graph = graph; this.finished = false; this.last = graph.textStart; this.characters = graph.characterMetadata.GetCharacters().ToList(); }
private bool ShouldBeCulled(XNode.Node node) { Vector2 nodePos = GridToWindowPositionNoClipped(node.position); if (nodePos.x / _zoom > position.width) { return(true); // Right } else if (nodePos.y / _zoom > position.height) { return(true); // Bottom } else if (nodeSizes.ContainsKey(node)) { Vector2 size = nodeSizes[node]; if (nodePos.x + size.x < 0) { return(true); // Left } else if (nodePos.y + size.y < 0) { return(true); // Top } } return(false); }
/// <summary> Add items for the context menu when right-clicking this node. Override to add custom menu items. </summary> public override void AddContextMenuItems(GenericMenu menu) { menu.AddItem(new GUIContent("Init State Machines"), false, () => (target as StateMachineGraph).parentMachine.InitStateMachines(false)); menu.AddSeparator(""); menu.AddItem(new GUIContent("Expand All"), false, () => (target as StateMachineGraph).ToggleExpandAll(false)); menu.AddItem(new GUIContent("Collapse All"), false, () => (target as StateMachineGraph).ToggleExpandAll(true)); menu.AddSeparator(""); Vector2 pos = NodeEditorWindow.current.WindowToGridPosition(Event.current.mousePosition); var nodeTypes = NodeEditorReflection.nodeTypes.OrderBy(type => GetNodeMenuOrder(type)).ToArray(); for (int i = 0; i < nodeTypes.Length; i++) { Type type = nodeTypes[i]; //Get node context menu path string path = GetNodeMenuName(type); if (string.IsNullOrEmpty(path)) { continue; } // Check if user is allowed to add more of given node type XNode.Node.DisallowMultipleNodesAttribute disallowAttrib; bool disallowed = false; if (NodeEditorUtilities.GetAttrib(type, out disallowAttrib)) { int typeCount = target.nodes.Count(x => x.GetType() == type); if (typeCount >= disallowAttrib.max) { disallowed = true; } } // Add node entry to context menu if (disallowed) { menu.AddItem(new GUIContent(path), false, null); } else { menu.AddItem(new GUIContent(path), false, () => { XNode.Node node = CreateNode(type, pos); NodeEditorWindow.current.AutoConnect(node); }); } } menu.AddSeparator(""); if (NodeEditorWindow.copyBuffer != null && NodeEditorWindow.copyBuffer.Length > 0) { menu.AddItem(new GUIContent("Paste"), false, () => NodeEditorWindow.current.PasteNodes(pos)); } else { menu.AddDisabledItem(new GUIContent("Paste")); } menu.AddItem(new GUIContent("Preferences"), false, () => NodeEditorReflection.OpenPreferences()); menu.AddCustomContextMenuItems(target); }
public override void AddContextMenuItems(GenericMenu menu) { if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { XNode.Node node = Selection.activeObject as XNode.Node; menu.AddItem(new GUIContent("Move To Top"), false, () => NodeEditorWindow.current.MoveNodeToTop(node)); } }
/// <summary> Safely remove a node and all its connections. </summary> public virtual void RemoveNode(XNode.Node node) { target.RemoveNode(node); UnityEngine.Object.DestroyImmediate(node, true); if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } }
/// <summary> Duplicate selected nodes and select the duplicates </summary> public void DuplicateSelectedNodes() { UnityEngine.Object[] newNodes = new UnityEngine.Object[Selection.objects.Length]; Dictionary <XNode.Node, XNode.Node> substitutes = new Dictionary <XNode.Node, XNode.Node>(); for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is XNode.Node) { XNode.Node srcNode = Selection.objects[i] as XNode.Node; if (srcNode.graph != graph) { continue; // ignore nodes selected in another graph } XNode.Node newNode = graphEditor.CopyNode(srcNode); substitutes.Add(srcNode, newNode); newNode.position = srcNode.position + new Vector2(30, 30); newNodes[i] = newNode; } } // Walk through the selected nodes again, recreate connections, using the new nodes for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is XNode.Node) { XNode.Node srcNode = Selection.objects[i] as XNode.Node; if (srcNode.graph != graph) { continue; // ignore nodes selected in another graph } foreach (XNode.NodePort port in srcNode.Ports) { for (int c = 0; c < port.ConnectionCount; c++) { XNode.NodePort inputPort = port.direction == XNode.NodePort.IO.Input ? port : port.GetConnection(c); XNode.NodePort outputPort = port.direction == XNode.NodePort.IO.Output ? port : port.GetConnection(c); XNode.Node newNodeIn, newNodeOut; if (substitutes.TryGetValue(inputPort.node, out newNodeIn) && substitutes.TryGetValue(outputPort.node, out newNodeOut)) { newNodeIn.UpdateStaticPorts(); newNodeOut.UpdateStaticPorts(); inputPort = newNodeIn.GetInputPort(inputPort.fieldName); outputPort = newNodeOut.GetOutputPort(outputPort.fieldName); } if (!inputPort.IsConnectedTo(outputPort)) { inputPort.Connect(outputPort); } } } } } Selection.objects = newNodes; }
/// <summary> Draw this node on top of other nodes by placing it last in the graph.nodes list </summary> public void MoveNodeToTop(XNode.Node node) { int index; while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) { graph.nodes[index] = graph.nodes[index + 1]; graph.nodes[index + 1] = node; } }
/// <summary> Make a field for a serialized property. Automatically displays relevant node port. </summary> public static void PropertyField(SerializedProperty property, GUIContent label, bool includeChildren = true, params GUILayoutOption[] options) { if (property == null) { throw new NullReferenceException(); } XNode.Node node = property.serializedObject.targetObject as XNode.Node; XNode.NodePort port = node.GetPort(property.name); PropertyField(property, label, port, includeChildren); }
/// <summary> Creates a copy of the original node in the graph </summary> public XNode.Node CopyNode(XNode.Node original) { XNode.Node node = target.CopyNode(original); node.name = original.name; AssetDatabase.AddObjectToAsset(node, target); if (NodeEditorPreferences.GetSettings().autoSave) { AssetDatabase.SaveAssets(); } return(node); }
public void InstantiateOrOpenConditionEditor(XNode.Node targetNode) { TransitionNode node = targetNode as TransitionNode; ConditionalGraph condGraph = node.conditionGraph; if (condGraph == null) { condGraph = CreateNewConditionalGraph(node); } NodeEditorWindow.Open(condGraph); }
public static void DrawOutputs(XNode.Node target, SerializedObject serializedObject) { NodePort outputPort = target.GetOutputPort("output"); serializedObject.FindProperty("outputFoldout").boolValue = EditorGUILayout.Foldout(serializedObject.FindProperty("outputFoldout").boolValue, "Outputs [" + outputPort.ConnectionCount + "]", true); bool outputFoldout = serializedObject.FindProperty("outputFoldout").boolValue; GUIStyle labelStyle = new GUIStyle(EditorStyles.label); GUIStyle buttonStyle = new GUIStyle(EditorStyles.miniButton); labelStyle.wordWrap = true; if (outputFoldout) { for (int i = 0; i < outputPort.ConnectionCount; i++) { EditorGUILayout.BeginHorizontal(); NodePort outputConnectionPort = outputPort.GetConnection(i); Node outputConnectionNode = outputPort.GetConnection(i).node; if (outputConnectionNode is State) { string label = outputConnectionNode.name; label = label.Replace("\n", ""); label = label.Substring(0, Mathf.Min(32, label.Length)); EditorGUILayout.LabelField(string.Format("[{0}] {1}", "Answer", label, labelStyle)); } if (GUILayout.Button("^", GUILayout.MaxHeight(20), GUILayout.MaxWidth(20))) { if (i <= 0) { return; } outputPort.SwitchConnections(i, i - 1); } if (GUILayout.Button("v", GUILayout.MaxHeight(20), GUILayout.MaxWidth(20))) { if (i >= outputPort.ConnectionCount - 1) { return; } outputPort.SwitchConnections(i, i + 1); } EditorGUILayout.EndHorizontal(); } } }
bool IsHoveringTitle(XNode.Node node) { Vector2 mousePos = Event.current.mousePosition; //Get node position Vector2 nodePos = GridToWindowPosition(node.position); float width = 200; if (nodeWidths.ContainsKey(node)) { width = nodeWidths[node]; } Rect windowRect = new Rect(nodePos, new Vector2(width / zoom, 30 / zoom)); return(windowRect.Contains(mousePos)); }
/// <summary> Draw an editable list of instance ports. Port names are named as "[fieldName] [index]" </summary> /// <param name="fieldName">Supply a list for editable values</param> /// <param name="type">Value type of added instance ports</param> /// <param name="serializedObject">The serializedObject of the node</param> /// <param name="connectionType">Connection type of added instance ports</param> /// <param name="onCreation">Called on the list on creation. Use this if you want to customize the created ReorderableList</param> 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 <ReorderableList> onCreation = null) { XNode.Node node = serializedObject.targetObject as XNode.Node; Predicate <string> isMatchingInstancePort = x => { string[] split = x.Split(' '); if (split != null && split.Length == 2) { return(split[0] == fieldName); } else { return(false); } }; List <XNode.NodePort> instancePorts = node.InstancePorts.Where(x => isMatchingInstancePort(x.fieldName)).OrderBy(x => x.fieldName).ToList(); ReorderableList list = null; Dictionary <string, ReorderableList> rlc; if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) { if (!rlc.TryGetValue(fieldName, out list)) { list = null; } } // If a ReorderableList isn't cached for this array, do so. if (list == null) { SerializedProperty arrayData = serializedObject.FindProperty(fieldName); list = CreateReorderableList(fieldName, instancePorts, arrayData, type, serializedObject, io, connectionType, typeConstraint, onCreation); if (reorderableListCache.TryGetValue(serializedObject.targetObject, out rlc)) { rlc.Add(fieldName, list); } else { reorderableListCache.Add(serializedObject.targetObject, new Dictionary <string, ReorderableList>() { { fieldName, list } }); } } list.list = instancePorts; list.DoLayoutList(); }
/// <summary> /// Shows all the property fields from the node /// </summary> protected virtual void ShowBodyFields() { string[] excludes = { "m_Script", "graph", "position", "ports" }; // Iterate through serialized properties and draw them like the Inspector (But with ports) SerializedProperty iterator = serializedObject.GetIterator(); bool enterChildren = true; EditorGUIUtility.labelWidth = LabelWidth; GUILayout.Space(m_PortRect.height * 0.5f); while (iterator.NextVisible(enterChildren)) { enterChildren = false; if (excludes.Contains(iterator.name)) { continue; } XNode.Node node = iterator.serializedObject.targetObject as XNode.Node; XNode.NodePort port = node.GetPort(iterator.name); // Don't allow to draw ports (// TO DO, add ports to the list now?) if (port != null) { continue; } // Save original editorStyle Color originalContentColor = GUI.contentColor; Color originalColor = EditorStyles.label.normal.textColor; Font originalFont = EditorStyles.label.font; int origianlFontSize = EditorStyles.label.fontSize; // Replace skin for entire editorStyle with custom EditorStyles.label.normal.textColor = Color.white; EditorStyles.label.font = m_NodeSkin.label.font; EditorStyles.label.fontSize = 13; // Draw property NodeEditorGUILayout.PropertyField(iterator, (NodePort)null, true); // Place original skin back to not mess other nodes EditorStyles.label.normal.textColor = originalColor; EditorStyles.label.font = originalFont; EditorStyles.label.fontSize = origianlFontSize; } }
/// <summary> /// 简单渲染 /// </summary> /// <param name="list"></param> public void SimpleDrawList(ReorderableList list) { if (chatNode == null) { chatNode = target as ChatNode; } // Debug.Log("简单渲染中"); //获取序列化后的chatslist数据 SerializedProperty arrayData = serializedObject.FindProperty(fieldName); bool hasArrayData = arrayData != null && arrayData.isArray; list.drawHeaderCallback = (Rect rect) => { string title = "对话列表"; GUI.Label(rect, title); }; list.drawElementCallback = (Rect rect, int index, bool selected, bool focused) => { #region 自定义渲染动态节点 XNode.Node node = serializedObject.targetObject as XNode.Node; XNode.NodePort port = node.GetPort(fieldName + " " + index); EditorGUI.LabelField(rect, ""); if (port != null && chatNode.chatslist[index].chatType == ChatType.option || chatNode.chatslist[index].chatType == ChatType.jump) { Vector2 pos = rect.position + (port.IsOutput ? new Vector2(rect.width + 6, 0) : new Vector2(-36, 0)); NodeEditorGUILayout.PortField(pos, port); } #endregion }; list.elementHeightCallback = (int index) => { if (chatNode.chatslist[index].chatType == ChatType.option || chatNode.chatslist[index].chatType == ChatType.jump) { SerializedProperty itemData = arrayData.GetArrayElementAtIndex(index); return(EditorGUI.GetPropertyHeight(itemData)); } else { return(0); } }; }
/// <summary> Initiate a rename on the currently selected node </summary> public void RenameSelectedNode() { if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { XNode.Node node = Selection.activeObject as XNode.Node; Vector2 size; if (nodeSizes.TryGetValue(node, out size)) { RenamePopup.Show(Selection.activeObject, size.x); } else { RenamePopup.Show(Selection.activeObject); } } }
private static void AddRequired(NodeGraph graph, Type type, ref Vector2 position) { if (!graph.nodes.Any(x => x.GetType() == type)) { XNode.Node node = graph.AddNode(type); graph.SetNodePosition(node, position); position.x += 200; if (node.name == null || node.name.Trim() == "") { node.name = NodeEditorUtilities.NodeDefaultName(type); } if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(graph))) { AssetDatabase.AddObjectToAsset(node, graph); } } }
/// <summary> Remove nodes in the graph in Selection.objects</summary> public void RemoveSelectedNodes() { // We need to delete reroutes starting at the highest point index to avoid shifting indices selectedReroutes = selectedReroutes.OrderByDescending(x => x.pointIndex).ToList(); for (int i = 0; i < selectedReroutes.Count; i++) { selectedReroutes[i].RemovePoint(); } selectedReroutes.Clear(); foreach (UnityEngine.Object item in Selection.objects) { if (item is XNode.Node) { XNode.Node node = item as XNode.Node; graphEditor.RemoveNode(node); } } }
public void InitGraph(FSMBehaviour fsm) { currentState_elapsedTime = 0; if (RootState == null) { XNode.Node node = nodes.FirstOrDefault(r => r is NodeBase_State); if (node != null) { _rootState = (NodeBase_State)node; _currentState = _rootState; } } if (RootState != null) { _currentState = RootState; } }
private void RecalculateDragOffsets(Event current) { dragOffset = new Vector2[Selection.objects.Length + selectedReroutes.Count]; // Selected nodes for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is XNode.Node) { XNode.Node node = Selection.objects[i] as XNode.Node; dragOffset[i] = node.position - WindowToGridPosition(current.mousePosition); } } // Selected reroutes for (int i = 0; i < selectedReroutes.Count; i++) { dragOffset[Selection.objects.Length + i] = selectedReroutes[i].GetPoint() - WindowToGridPosition(current.mousePosition); } }
/// <summary> Automatically delete Node sub-assets before deleting their script. /// <para/> This is important to do, because you can't delete null sub assets. </summary> private static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions options) { // Get the object that is requested for deletion UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath <UnityEngine.Object> (path); // If we aren't deleting a script, return if (!(obj is UnityEditor.MonoScript)) { return(AssetDeleteResult.DidNotDelete); } // Check script type. Return if deleting a non-node script UnityEditor.MonoScript script = obj as UnityEditor.MonoScript; System.Type scriptType = script.GetClass(); if (scriptType == null || (scriptType != typeof(XNode.Node) && !scriptType.IsSubclassOf(typeof(XNode.Node)))) { return(AssetDeleteResult.DidNotDelete); } // Find all ScriptableObjects using this script string[] guids = AssetDatabase.FindAssets("t:" + scriptType); for (int i = 0; i < guids.Length; i++) { string assetpath = AssetDatabase.GUIDToAssetPath(guids[i]); Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetpath); for (int k = 0; k < objs.Length; k++) { XNode.Node node = objs[k] as XNode.Node; if (node.GetType() == scriptType) { if (node != null && node.graph != null) { // Delete the node and notify the user Debug.LogWarning(node.name + " of " + node.graph + " depended on deleted script and has been removed automatically.", node.graph); node.graph.RemoveNode(node); } } } } // We didn't actually delete the script. Tell the internal system to carry on with normal deletion procedure return(AssetDeleteResult.DidNotDelete); }
bool IsHoveringTitle(XNode.Node node) { Vector2 mousePos = Event.current.mousePosition; //Get node position Vector2 nodePos = GridToWindowPosition(node.position); float width; Vector2 size; if (nodeSizes.TryGetValue(node, out size)) { width = size.x; } else { width = 200; } Rect windowRect = new Rect(nodePos, new Vector2(width / zoom, 30 / zoom)); return(windowRect.Contains(mousePos)); }
/// <summary> Show right-click context menu for a node </summary> public void ShowNodeContextMenu(XNode.Node node) { GenericMenu contextMenu = new GenericMenu(); contextMenu.AddItem(new GUIContent("Move To Top"), false, () => { int index; while ((index = graph.nodes.IndexOf(node)) != graph.nodes.Count - 1) { graph.nodes[index] = graph.nodes[index + 1]; graph.nodes[index + 1] = node; } }); contextMenu.AddItem(new GUIContent("Duplicate"), false, () => { XNode.Node n = graph.CopyNode(node); n.position = node.position + new Vector2(30, 30); }); contextMenu.AddItem(new GUIContent("Remove"), false, () => graph.RemoveNode(node)); AddCustomContextMenuItems(contextMenu, node); contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); }
public override void AddContextMenuItems(GenericMenu menu) { // Actions if only one node is selected if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { XNode.Node node = Selection.activeObject as XNode.Node; menu.AddItem(new GUIContent("Move To Top"), false, () => NodeEditorWindow.current.MoveNodeToTop(node)); menu.AddItem(new GUIContent("Rename"), false, NodeEditorWindow.current.RenameSelectedNode); // Add Mark option for main model menu.AddItem(new GUIContent("Mark as main model"), false, () => ((PCGGraphEditor)NodeEditorWindow.current.graphEditor).MarkModelAsMain(_pcgModelNode)); } // Remove Duplicate menu.AddItem(new GUIContent("Remove"), false, NodeEditorWindow.current.RemoveSelectedNodes); // Custom sctions if only one node is selected if (Selection.objects.Length == 1 && Selection.activeObject is XNode.Node) { XNode.Node node = Selection.activeObject as XNode.Node; NodeEditorWindow.AddCustomContextMenuItems(menu, node); } }
private void CheckMasterNode() { if (m_MasterNode == null) { foreach (Node n in nodes) { MasterNode mn = n as MasterNode; if (mn != null) { m_MasterNode = mn; return; } } if (m_MasterNode == null) { NodeGraphEditor nge = NodeGraphEditor.GetEditor(this, NodeEditorWindow.current); XNode.Node node = nge.CreateNode(typeof(MasterNode), Vector2.zero); NodeEditorWindow.current.AutoConnect(node); m_MasterNode = (MasterNode)node; } } }
private IConversationStep Analyze(NodePort portToNext) { //Port Connected to nothing -> End if (portToNext.Connection == null) { this.finished = true; return(EndStep.Get); } var next = portToNext.Connection.node; if (next == null) { Debug.LogError("Somehow has a valid connection but no node...."); return(ErrorStep.Other); } //Evaluate depending on Node IConversationStep result; switch (next) { case TextNode tn: result = EvaluateTextNode(tn, this.characters); break; case ChoiceNode cn: result = EvaluateChoiceNode(cn, this.characters); break; default: Debug.LogError("Wrong type of node"); result = ErrorStep.Other; break; } this.last = next; return(result); }
public static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions options) { UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath <UnityEngine.Object>(path); if (!(obj is UnityEditor.MonoScript)) { return(AssetDeleteResult.DidNotDelete); } UnityEditor.MonoScript script = obj as UnityEditor.MonoScript; System.Type scriptType = script.GetClass(); if (scriptType != typeof(XNode.Node) && !scriptType.IsSubclassOf(typeof(XNode.Node))) { return(AssetDeleteResult.DidNotDelete); } //Find ScriptableObjects using this script string[] guids = AssetDatabase.FindAssets("t:" + scriptType); for (int i = 0; i < guids.Length; i++) { string assetpath = AssetDatabase.GUIDToAssetPath(guids[i]); Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetpath); for (int k = 0; k < objs.Length; k++) { XNode.Node node = objs[k] as XNode.Node; if (node.GetType() == scriptType) { if (node != null && node.graph != null) { Debug.LogWarning(node.name + " of " + node.graph + " depended on deleted script and has been removed automatically.", node.graph); node.graph.RemoveNode(node); } } } } return(AssetDeleteResult.DidNotDelete); }