/// <summary> /// Callback to destroy a node. /// <param name="node">The node to be destroyed.</param> /// </summary> void OnDestroyNode(ActionNode node) { // It's not the Update node? if (node != null && !(node is Update)) { ActionStateUtility.DestroyNode(node); } RegisterEditorOnGUI(); }
/// <summary> /// Returns action and condition nodes in the supplied nodes; branch nodes are ignored but their hierarchy will be included. /// <returns>The actions and conditions in the supplied nodes.</returns> /// </summary> public static ActionNode[] GetActionsAndConditions(ActionNode[] nodes) { var actionCondition = new List <ActionNode>(); if (nodes != null) { for (int i = 0; i < nodes.Length; i++) { if (nodes[i] is BranchNode) { var branch = nodes[i] as BranchNode; actionCondition.AddRange(ActionStateUtility.GetActionsAndConditions(branch.children)); } else { actionCondition.Add(nodes[i]); } } } return(actionCondition.ToArray()); }
/// <summary> /// Show the node menu. /// <param name="node">The target node.</param> /// </summary> void NodeContextMenu(ActionNode node) { // Validate Paramenters if (node == null) { return; } // Create the menu var menu = new UnityEditor.GenericMenu(); // Copy/Cut/Paste/Duplicate menu.AddItem(new GUIContent("Copy"), false, delegate() { BehaviourTreeUtility.nodeToPaste = node; }); menu.AddItem(new GUIContent("Cut"), false, delegate() { BehaviourTreeUtility.nodeToPaste = node; OnDestroyNode(node); }); ActionNode[] nodesToPaste = ActionStateUtility.GetActionsAndConditions(BehaviourTreeUtility.nodeToPaste != null ? new ActionNode[] { BehaviourTreeUtility.nodeToPaste } : new ActionNode[0]); if (nodesToPaste.Length > 0) { menu.AddItem(new GUIContent("Paste"), false, delegate() { ActionStateUtility.PasteNodes(m_ActionState, nodesToPaste); }); } else { menu.AddDisabledItem(new GUIContent("Paste")); } menu.AddItem(new GUIContent("Duplicate"), false, delegate() { ActionStateUtility.PasteNodes(m_ActionState, new ActionNode[] { node }); }); // Separator menu.AddSeparator(""); // Delete menu.AddItem(new GUIContent("Delete"), false, delegate() { this.OnDestroyNode(node); }); // Show the context menu menu.ShowAsContext(); }
/// <summary> /// Draws the script details in the gui. /// <param name="script">The script to be drawn.</param> /// </summary> void DrawScriptDetail(Script script) { GUILayout.BeginVertical(s_Styles.scriptDetailBox); // Draw disabled inspector if (m_NodeEditor != null && m_SelectedNodeSample != null) { #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 EditorGUIUtility.LookLikeInspector(); #endif // Store gui enabled var guiEnabled = GUI.enabled; GUI.enabled = false; GUILayout.Space(-1f); m_NodeEditor.DrawNode(m_SelectedNodeSample); GUILayout.Space(8f); // Restore gui enabled GUI.enabled = guiEnabled; #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 EditorGUIUtility.LookLikeControls(); #endif } // Draws description? if (script.description != string.Empty) { EditorGUILayout.LabelField(string.Empty, "Description: " + script.description, s_Styles.description); } // Draw parent // Update active node var activeNode = BehaviourWindow.activeNode; UpdateActiveNode(activeNode); // Add to a tree InternalBehaviourTree activeTree = BehaviourWindow.activeTree; if (activeTree != null) { GUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel("Parent"); if (activeNode != null && m_ActiveNodeType != null) { EditorGUILayout.LabelField(new GUIContent(activeNode.name + m_ActiveNodeTypeName, m_ActiveNodeIcon, m_ActiveNodeInfo.description), s_Styles.titleText); } else { EditorGUILayout.LabelField("null", s_Styles.errorLabel); } GUILayout.EndHorizontal(); var guiEnabled2 = GUI.enabled; var activeBranch = BehaviourWindow.activeNode as BranchNode; GUI.enabled = activeTree != null; if (GUILayout.Button(activeBranch != null ? "Add as Child of " + activeBranch.name : "Add as Root", GUILayout.ExpandWidth(false))) { ActionNode newNode = null; if (activeBranch != null) { newNode = BehaviourTreeUtility.AddNode(activeBranch, script.type); } else { newNode = BehaviourTreeUtility.AddNode(activeTree, script.type); } // Select the new node if (newNode != null) { BehaviourWindow.activeNodeID = newNode.instanceID; } } GUI.enabled = guiEnabled2; } else { // Add to an ActionState var actionState = BehaviourWindow.activeState as InternalActionState; if (actionState != null && GUILayout.Button("Add Node", GUILayout.ExpandWidth(false))) { ActionNode newNode = ActionStateUtility.AddNode(actionState, script.type); // Select the new node if (newNode != null) { BehaviourWindow.activeNodeID = newNode.instanceID; } } } GUILayout.EndVertical(); GUILayout.Space(18f); // Workaround to avoid to close the GUIPropertyField on click if (Event.current.type == EventType.Repaint) { m_LastRect = GUILayoutUtility.GetLastRect(); } if (Event.current.type == EventType.MouseDown && m_LastRect.Contains(Event.current.mousePosition)) { Event.current.Use(); } }
/// <summary> /// Unity callback to draw a custom inspector. /// </summary> public override void OnInspectorGUI() { // Create styles? if (s_Styles == null) { s_Styles = new InternalActionStateEditor.Styles(); } // Workaround to update nodes if (Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed") { GUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; m_ActionState.LoadNodes(); UpdateActiveNode(); return; } // Reload nodes? if (m_ActionState.isDirty) { m_ActionState.LoadNodes(); UpdateActiveNode(); } // Register OnGUI node? if (!Application.isPlaying && m_ActionState.onGUINode != null && !GUICallback.HasCallbacks()) { this.RegisterEditorOnGUI(); } #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 EditorGUIUtility.LookLikeInspector(); #endif // Draw default inspector DrawDefaultInspector(); // Shows the node editor? bool showNodeEditor = m_ActionState.parent == null || BehaviourWindow.activeState == m_ActionState; // Draw Action List if (m_NodeList == null) { // m_NodeList = new ReorderableList(this.serializedObject, this.serializedObject.FindProperty("m_UpdateActions")); m_NodeList = new ReorderableList(m_ActionState.GetNodes(), typeof(ActionNode)); m_NodeList.drawHeaderCallback += delegate(Rect rect) { EditorGUI.LabelField(rect, "Nodes"); }; m_NodeList.drawElementCallback += DrawNode; m_NodeList.onAddCallback += this.OnAddNode; m_NodeList.onRemoveCallback += this.OnRemoveSelectedNode; m_NodeList.onSelectCallback += this.OnSelectNode; m_NodeList.onReorderCallback += this.OnReorderNode; // Select the active node UpdateActiveNode(); #if !UNITY_4_0_0 && !UNITY_4_1 && !UNITY_4_2 && !UNITY_4_3 m_NodeList.list = m_ActionState.GetNodes(); m_NodeList.DoLayoutList(); #else this.Repaint(); #endif } else if (showNodeEditor) { m_NodeList.list = m_ActionState.GetNodes(); m_NodeList.DoLayoutList(); } if (showNodeEditor) { GUILayout.Space(6f); #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 EditorGUIUtility.LookLikeControls(); #endif // Get the active node ActionNode activeNode = m_ActionState.isRoot ? this.GetActiveNode() : BehaviourWindow.activeNode; // Draw node properties if (m_NodeEditor != null) { // Is there an active node? if (activeNode != null && activeNode.owner as InternalActionState == m_ActionState) { // It's an Update node var oldGUIEnabled = GUI.enabled; GUI.enabled = !(activeNode is Update); m_NodeEditor.DrawNode(activeNode); GUI.enabled = oldGUIEnabled; GUILayout.Space(4f); } } // Copy/Paste/Cut/Duplicate/Delete keyboard shortcuts Event current = Event.current; if (current.type == EventType.ValidateCommand) { // Use event to call event ExecuteCommand if (current.commandName == "Paste") { ActionNode[] nodesToPaste = ActionStateUtility.GetActionsAndConditions(BehaviourTreeUtility.nodeToPaste != null ? new ActionNode[] { BehaviourTreeUtility.nodeToPaste } : new ActionNode[0]); if (nodesToPaste.Length > 0) { current.Use(); } } if (activeNode != null) { if (current.commandName == "Copy") { current.Use(); } else if (current.commandName == "Duplicate") { current.Use(); } else if (current.commandName == "Delete") { current.Use(); } else if (current.commandName == "Cut") { current.Use(); } } } else if (Event.current.type == EventType.ExecuteCommand) { if (current.commandName == "Paste") { ActionNode[] nodesToPaste = ActionStateUtility.GetActionsAndConditions(BehaviourTreeUtility.nodeToPaste != null ? new ActionNode[] { BehaviourTreeUtility.nodeToPaste } : new ActionNode[0]); ActionStateUtility.PasteNodes(m_ActionState, nodesToPaste); } else if (current.commandName == "Copy") { BehaviourTreeUtility.nodeToPaste = activeNode; } else if (current.commandName == "Duplicate") { ActionStateUtility.PasteNodes(m_ActionState, new ActionNode[] { activeNode }); } else if (current.commandName == "Delete") { this.OnDestroyNode(activeNode); } else if (current.commandName == "Cut") { BehaviourTreeUtility.nodeToPaste = activeNode; this.OnDestroyNode(activeNode); } } } }
/// <summary> /// Callback to add a new node. /// </summary> private void OnAddNode(ReorderableList list) { // Get all node scripts var nodeTypes = new List <System.Type>(); foreach (System.Type type in BehaviourTreeUtility.GetNodeTypes()) { if (!type.IsSubclassOf(typeof(BranchNode))) { nodeTypes.Add(type); } } // Create the menu var menu = new UnityEditor.GenericMenu(); // Add node types to the menu for (int i = 0; i < nodeTypes.Count; i++) { var nodeType = nodeTypes[i]; var nodeInfo = AttributeUtility.GetAttribute <NodeInfoAttribute>(nodeType, false) ?? new NodeInfoAttribute(); menu.AddItem(new GUIContent(nodeInfo.category + nodeType.Name), false, delegate() { ActionStateUtility.AddNode(m_ActionState, nodeType); RegisterEditorOnGUI(); }); } // Show the context menu menu.ShowAsContext(); }