/// <summary> /// Adds a new node to the tree, automatically handles undo, dirty flag and save node. /// <param name="tree">The tree to add a new node.</param> /// <param name="nodeType">The type of the new node.</param> /// <returns>The new node.</returns> /// </summary> public static ActionNode AddNode(InternalBehaviourTree tree, System.Type nodeType) { // Validate parameters if (tree != null && nodeType != null && nodeType.IsSubclassOf(typeof(ActionNode)) && !nodeType.IsAbstract) { // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(tree, "Add New Node"); #else Undo.RecordObject(tree, "Add New Node"); #endif // Create new node var newNode = tree.AddNode(nodeType); if (newNode != null) { // Saves node and sets dirty flag StateUtility.SetDirty(tree); return(newNode); } } return(null); }
/// <summary> /// Shows the node generic menu. /// </summary> void ShowNodeContextMenu() { var menu = new GenericMenu(); menu.AddItem(new GUIContent("Reset"), false, delegate { StateUtility.ResetNode(target); m_SerializedNode = new SerializedNode(target); m_SerializedNode.Update(); }); menu.AddSeparator(string.Empty); menu.AddItem(new GUIContent("Copy"), false, delegate { BehaviourTreeUtility.nodeToPaste = target; }); menu.AddSeparator(string.Empty); if (target != null) { menu.AddItem(new GUIContent("Find Script"), false, delegate { MonoScript script = BehaviourTreeUtility.GetNodeScript(target.GetType()); if (script != null) { EditorGUIUtility.PingObject(script); } }); menu.AddItem(new GUIContent("Edit Script"), false, delegate { MonoScript script = BehaviourTreeUtility.GetNodeScript(target.GetType()); if (script != null) { AssetDatabase.OpenAsset(script); } }); } else { menu.AddDisabledItem(new GUIContent("Find Script")); menu.AddDisabledItem(new GUIContent("Edit Script")); } menu.ShowAsContext(); }
/// <summary> /// Adds a new node to the parent, automatically handles undo, dirty flag and save node. /// <param name="parent">The branch to add the child.</param> /// <param name="childType">The type of the new node.</param> /// <returns>The new node.</returns> /// </summary> public static ActionNode AddNode(BranchNode parent, System.Type childType) { // Validate parameters if (parent != null && parent.tree != null && childType != null) { // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(parent.tree, "Add New Node"); #else Undo.RecordObject(parent.tree, "Add New Node"); #endif var newNode = parent.tree.AddNode(childType); if (newNode != null) { // Adds new node as child of parent parent.Add(newNode); // Call OnValidate on the parent parent.OnValidate(); // Saves node and sets dirty flag StateUtility.SetDirty(parent.tree); return(newNode); } } return(null); }
/// <summary> /// Destroys the suplied node and its hierarchy from the tree. /// <param name="node">The node to be destroyed.</param> /// <returns>True if the node was successfully destroyed; false otherwise.</returns> /// </summary> public static bool DestroyNode(ActionNode node) { // Validate parameters if (node != null && node.tree != null) { // Get parent and tree var tree = node.tree; var parent = node.branch; // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(tree, "Delete " + node.name); #else Undo.RecordObject(tree, "Delete " + node.name); #endif // Removes node from parent if (parent != null) { parent.Remove(node); // Call OnValidate on the parent parent.OnValidate(); } // Removes node from tree tree.RemoveNode(node, true); // Saves tree and marks dirty flag StateUtility.SetDirty(tree); return(true); } return(false); }
/// <summary> /// Destroys the suplied node and its hierarchy from the tree. /// <param name="node">The node to be destroyed.</param> /// <returns>True if the node was successfully destroyed; false otherwise.</returns> /// </summary> public static bool DestroyNode(ActionNode node) { // Get the tree action state var actionState = node != null ? node.owner as InternalActionState : null; // Validate parameters if (actionState != null) { // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(actionState, "Delete " + node.name); #else Undo.RecordObject(actionState, "Delete " + node.name); #endif // Removes node from actionState actionState.RemoveNode(node); // Saves actionState and marks dirty flag StateUtility.SetDirty(actionState); return(true); } return(false); }
/// <summary> /// Inserts a new node to the supplied branch, automatically handles undo, dirty flag and save node. /// <param name="branch">The branch to add a new node.</param> /// <param name="index">The index of the new node.</param> /// <param name="nodeType">The type of the new node.</param> /// <returns>The new node.</returns> /// </summary> public static ActionNode InsertNode(BranchNode branch, int index, System.Type nodeType) { // Validate parameters if (branch != null && branch.tree != null && nodeType != null && index >= 0 && index <= branch.children.Length) { // Get the tree var tree = branch.tree; // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(tree, "Insert New Node"); #else Undo.RecordObject(tree, "Insert New Node"); #endif // Create new node var newNode = tree.AddNode(nodeType); if (newNode != null) { // Insert new node branch.Insert(index, newNode); // Call OnValidate on the parent branch.OnValidate(); // Saves node and sets dirty flag StateUtility.SetDirty(tree); return(newNode); } } return(null); }
/// <summary> /// Shows a context menu callback to add a new state. /// </summary> void OnAddContextMenu() { var menu = new GenericMenu(); var stateScripts = FileUtility.GetScripts <InternalStateBehaviour>(); for (int i = 0; i < stateScripts.Length; i++) { System.Type childStateType = stateScripts[i].GetClass(); // Get the component path string componentPath; AddComponentMenu componentMenu = AttributeUtility.GetAttribute <AddComponentMenu>(childStateType, true); if (componentMenu != null && componentMenu.componentMenu != string.Empty) { componentPath = componentMenu.componentMenu; } else { componentPath = childStateType.ToString().Replace('.', '/'); } menu.AddItem(new GUIContent(componentPath), false, delegate() { StateUtility.AddState(m_Parent, childStateType); }); } // Shows the context menu menu.ShowAsContext(); }
/// <summary> /// Shows a context menu to add a new parent. /// </summary> void OnContextMenu() { var menu = new UnityEditor.GenericMenu(); // Get the active game object var gameObject = Selection.activeGameObject; // The selected game object is not null? if (gameObject != null) { // Gets all scripts that inherits from ParentBehaviour class MonoScript[] scripts = FileUtility.GetScripts <ParentBehaviour>(); for (int i = 0; i < scripts.Length; i++) { var type = scripts[i].GetClass(); // Get the component path string componentPath = "Add Parent/"; AddComponentMenu componentMenu = AttributeUtility.GetAttribute <AddComponentMenu>(type, false); if (componentMenu == null || componentMenu.componentMenu != string.Empty) { componentMenu = AttributeUtility.GetAttribute <AddComponentMenu>(type, true); if (componentMenu != null && componentMenu.componentMenu != string.Empty) { componentPath += componentMenu.componentMenu; } else { componentPath += type.ToString().Replace('.', '/'); } // Add to menu menu.AddItem(new GUIContent(componentPath), false, delegate() { BehaviourWindow.activeParent = StateUtility.AddState(gameObject, type) as ParentBehaviour; }); } } } else { menu.AddDisabledItem(new GUIContent("Add Parent/")); ShowNotification(new GUIContent("Select a Game Object and right click in this window!")); } // Add option to paste states if (Selection.activeGameObject != null && StateUtility.statesToPaste != null && StateUtility.statesToPaste.Length > 0) { menu.AddItem(new GUIContent("Paste State"), false, delegate() { StateUtility.CloneStates(Selection.activeGameObject, StateUtility.statesToPaste, null); }); } else { menu.AddDisabledItem(new GUIContent("Paste State")); } // Refresh window? // menu.AddSeparator(""); // menu.AddItem(new GUIContent("Refresh"), false ,Refresh); // Shows the controller menu menu.ShowAsContext(); }
/// <summary> /// Adds a state to the supplied parent. /// Automatically handles undo. /// <param name="parent">The ParentBehaviour to add the new state.</param> /// <param name="type">The new state type.</param> /// <returns>The new created state.<returns> /// </summary> public static InternalStateBehaviour AddState(ParentBehaviour parent, System.Type type) { // Validate parameters if (type != null && parent != null) { // Create and register undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterSceneUndo("Create State (" + type.Name + ")"); #endif InternalStateBehaviour newState = parent.AddState(type); if (newState != null) { // Register undo #if !UNITY_4_0_0 && !UNITY_4_1 && !UNITY_4_2 Undo.RegisterCreatedObjectUndo(newState, "Create State (" + type.Name + ")"); #endif // The parent is a FSM? var fsm = parent as InternalStateMachine; if (fsm != null) { // The newState is an AnyState? if (newState is InternalAnyState) { // The fsm already has an AnyState? if (fsm.anyState != null) { // Destroy the curren anyState StateUtility.Destroy(fsm.anyState); } // Add the new anyState fsm.anyState = newState as InternalAnyState; } // The start state is null? else if (fsm.startState == null) { // Set the new state as the start state fsm.startState = newState; EditorUtility.SetDirty(fsm); } } // Sets dirty flag EditorUtility.SetDirty(parent.gameObject); } return(newState); } return(null); }
/// <summary> /// Remove the suplied branch from the tree. /// <param name="branch">The node to be removed.</param> /// <returns>True if the node was successfully removed; false otherwise.</returns> /// </summary> public static bool RemoveBranch(BranchNode branch) { // The Branch is not a decorator if (branch != null && branch.tree != null) { // Gets parent's children and node's id var parent = branch.branch; ActionNode[] children = branch.children; var tree = branch.tree; // The parent is a decorator or null (node is root) and the branch has more than one child? if (children.Length >= 2 && (parent == null || parent is DecoratorNode) || parent == null) { EditorApplication.Beep(); return(false); } // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(branch.tree, "Remove Branch"); #else Undo.RecordObject(branch.tree, "Remove Branch"); #endif // Removes children from branch and adds to parent if (parent != null) { // Get the branch index int branchIndex = (new List <ActionNode>(parent.children)).IndexOf(branch); parent.Remove(branch); for (int i = 0; i < children.Length; i++) { branch.Remove(children[i]); parent.Insert(branchIndex++, children[i]); } // Call OnValidate on the parent parent.OnValidate(); } // Removes node from tree tree.RemoveNode(branch, false); // Saves tree and marks dirty flag StateUtility.SetDirty(tree); return(true); } return(false); }
/// <summary> /// Callback called whenever the supplied list changes its order. /// <param name="list">The target list.</param> /// </summary> private void OnReorderNode(ReorderableList list) { // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(m_ActionState, "Move Node"); #else Undo.RecordObject(m_ActionState, "Move Node"); #endif // Set dirty flag StateUtility.SetDirty(m_ActionState); m_ActionState.HierarchyChanged(); RegisterEditorOnGUI(); }
/// <summary> /// Apply property modifications. /// </summary> public void ApplyModifiedProperties() { if (m_Target != null && m_PropertiesChanged.Count > 0) { var ownerUnityObj = m_Target.owner as UnityEngine.Object; var variableModified = new List <Variable>(); // Register undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(ownerUnityObj, "Inspector"); #else Undo.RecordObject(ownerUnityObj, "Inspector"); #endif // Call ApplyModifiedValue in all change properties for (int i = 0; i < m_PropertiesChanged.Count; i++) { // Apply changes in the property data m_PropertiesChanged[i].ApplyModifiedValue(); // Recreate data? m_RecreateData = m_RecreateData || m_PropertiesChanged[i].hasChildren || m_PropertiesChanged[i].propertyType == NodePropertyType.Variable; // It is a variable? var variable = m_PropertiesChanged[i].target as Variable; if (variable != null && !variableModified.Contains(variable)) { variableModified.Add(variable); } } // Clear changed properties m_PropertiesChanged.Clear(); // Call OnValidate foreach (var variable in variableModified) { variable.OnValidate(); } m_Target.OnValidate(); // Set tree dirty flag StateUtility.SetDirty(m_Target.owner); } }
/// <summary> /// Reset the supplied node properties. /// <param name="node">The node to be reseted.</param> /// </summary> public static void ResetNode(ActionNode node) { // Get the owner as an Uniyt object var ownerUnityObj = node != null ? node.owner as UnityEngine.Object : null; // Validate parameters if (ownerUnityObj != null) { // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(ownerUnityObj, "Reset Node"); #else Undo.RecordObject(ownerUnityObj, "Reset Node"); #endif node.name = node.GetType().Name; node.Reset(); node.OnValidate(); StateUtility.SetDirty(node.owner); } }
/// <summary> /// Shows a context menu for the supplied transition. /// <param name="transition">The target transition.</param> /// </summary> void ShowContextMenu(StateTransition transition) { var menu = new UnityEditor.GenericMenu(); var currentEventID = transition.eventID; // Add none menu.AddItem(new GUIContent("None"), currentEventID == 0, delegate() { StateUtility.SetNewEvent(m_State, transition, 0); this.Refresh(); }); // Add blackboard events var blackboard = m_State.blackboard; if (blackboard != null) { foreach (var fsmEvent in blackboard.fsmEvents) { int eventId = fsmEvent.id; menu.AddItem(new GUIContent(fsmEvent.name), currentEventID == fsmEvent.id, delegate() { StateUtility.SetNewEvent(m_State, transition, eventId); this.Refresh(); }); } } // Add GlobalBlackboard events // This is not The GlobalBlackboard? if (InternalGlobalBlackboard.Instance != null && blackboard != InternalGlobalBlackboard.Instance) { foreach (var globalEvent in InternalGlobalBlackboard.Instance.fsmEvents) { int eventId = globalEvent.id; var eventName = globalEvent.isSystem ? "System/" + globalEvent.name : "Global/" + globalEvent.name; menu.AddItem(new GUIContent(eventName), currentEventID == globalEvent.id, delegate() { StateUtility.SetNewEvent(m_State, transition, eventId); this.Refresh(); }); } } menu.AddSeparator(""); // Separator menu.AddItem(new GUIContent("Delete"), false, delegate() { StateUtility.RemoveTransition(m_State, transition); this.Refresh(); }); // Shows the context menu menu.ShowAsContext(); }
/// <summary> /// Paste the node in BehaviourTreeUtility.nodeToPaste in the supplied tree. /// <param name="tree">The target tree.</param> /// <param name="parent">Optional parent to paste the node; or null to paste as a root node.</param> /// <returns>The pasted node.</returns> /// </summary> public static ActionNode PasteNode(InternalBehaviourTree tree, BranchNode parent = null) { // Get the node to be pasted var node = BehaviourTreeUtility.nodeToPaste; // Validate parameters if (node != null && tree != null) { // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(tree, "Paste Node"); #else Undo.RecordObject(tree, "Paste Node"); #endif var newNode = node.Copy(tree); if (newNode != null) { // Add to parent branch? if (parent != null) { parent.Add(newNode); // Call OnValidate on the parent parent.OnValidate(); } // Saves node and sets dirty flag StateUtility.SetDirty(tree); // Reload tree to update variables tree.LoadNodes(); } return(newNode); } return(null); }
/// <summary> /// Paste the supplied nodes in the supplied ActionState. /// <param name="actionState">The ActionState to paste the node.</param> /// <param name="nodes">The nodes to be pasted.</param> /// <returns>The pasted nodes.</returns> /// </summary> public static ActionNode[] PasteNodes(InternalActionState actionState, ActionNode[] nodes) { var newNodes = new List <ActionNode>(); // Validate parameters if (nodes != null && actionState != null) { // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(actionState, "Paste Node"); #else Undo.RecordObject(actionState, "Paste Node"); #endif // Copy nodes for (int i = 0; i < nodes.Length; i++) { if (nodes[i] != null && !(nodes[i] is BranchNode)) { ActionNode newNode = nodes[i].Copy(actionState); if (newNode != null) { newNodes.Add(newNode); } } } if (newNodes.Count > 0) { // Saves node and sets dirty flag StateUtility.SetDirty(actionState); // Reload actionState to update variables actionState.LoadNodes(); } } return(newNodes.ToArray()); }
/// <summary> /// Inserts a new node to the supplied branch, automatically handles undo, dirty flag and save node. /// <param name="node">The branch to add a new node.</param> /// <param name="newNodePosition">Move the node to the position of this node.</param> /// <param name="branch">The branch to drop the node or null.</param> /// </summary> public static bool MoveNode(ActionNode node, ActionNode newNodePosition, BranchNode branch) { // Validate parameters if (node != null && node.tree != null) { // Get the tree var tree = node.tree; // The node does not belongs to the tree? if (!tree.GetNodes().Contains(node)) { return(false); } // Register Undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(tree, "Move Node"); #else Undo.RecordObject(tree, "Move Node"); #endif // The node will be a root node? if (branch == null) { // Remove from old branch if (node.branch != null) { BranchNode oldBranch = node.branch; node.branch.Remove(node); // Call OnValidate on old branch oldBranch.OnValidate(); } if (newNodePosition == null) { var newIndex = node.tree.GetNodes().Count - 1; node.tree.MoveNode(node.GetIndex(), newIndex); } else { var newIndex = newNodePosition.root.GetIndex(); node.tree.MoveNode(node.GetIndex(), newIndex); } } // The new node position is null? else if (newNodePosition == null) { // node.branch = branch; // Store old branch var oldBranch = node.branch; // Remove from old branch if (oldBranch != null) { oldBranch.Remove(node); } // Add to drop if (!branch.Add(node)) { // Restore old branch if (oldBranch != null) { oldBranch.Add(node); } return(false); } // Call OnValidate on branches branch.OnValidate(); if (oldBranch != null && oldBranch != branch) { oldBranch.OnValidate(); } node.tree.HierarchyChanged(); } else { // Cache the oldBranch BranchNode oldBranch = node.branch; // Get index var index = -1; var children = branch.children; for (int i = 0; i < children.Length; i++) { if (children[i] == newNodePosition) { index = i; break; } } // The index is invalid? if (index < 0 || !branch.Insert(index, node)) { return(false); } else { // Call OnValidate on the branches if (oldBranch != null) { oldBranch.OnValidate(); } branch.OnValidate(); node.tree.HierarchyChanged(); } } // Save move opration StateUtility.SetDirty(tree); return(true); } return(false); }
/// <summary> /// Shows the context menu. /// </summary> void OnContextMenu() { var menu = new GenericMenu(); var activeFsm = BehaviourWindow.activeFsm; if (activeFsm != null) { m_LastMousePos = Event.current.mousePosition; // Get the states scripts MonoScript[] stateScripts = FileUtility.GetScripts <InternalStateBehaviour>(); for (int i = 0; i < stateScripts.Length; i++) { System.Type type = stateScripts[i].GetClass(); // Get the component path string componentPath = "Add State/"; AddComponentMenu componentMenu = AttributeUtility.GetAttribute <AddComponentMenu>(type, false); if (componentMenu == null || componentMenu.componentMenu != string.Empty) { componentMenu = AttributeUtility.GetAttribute <AddComponentMenu>(type, true); if (componentMenu != null && componentMenu.componentMenu != string.Empty) { componentPath += componentMenu.componentMenu; } else { componentPath += type.ToString().Replace('.', '/'); } menu.AddItem(new GUIContent(componentPath), false, delegate() { InternalStateBehaviour newState = StateUtility.AddState(activeFsm, type); // Sets the newState position and dirty flag if (newState != null) { newState.position = m_LastMousePos - new Vector2(StateGUI.defaultWidth, StateGUI.defaultHeight) * .5f; EditorUtility.SetDirty(newState); } }); } } } else { menu.AddDisabledItem(new GUIContent("Add State")); } // Separator menu.AddSeparator(""); menu.AddItem(new GUIContent("Copy FSM"), false, delegate() { StateUtility.statesToPaste = new InternalStateBehaviour[] { activeFsm }; }); if (StateUtility.statesToPaste != null && StateUtility.statesToPaste.Length > 0 && activeFsm != null) { menu.AddItem(new GUIContent("Paste State"), false, delegate() { StateUtility.PasteStates(activeFsm); }); } else { menu.AddDisabledItem(new GUIContent("Paste State")); } // Separator menu.AddSeparator(""); menu.AddItem(new GUIContent("Delete FSM"), false, delegate() { StateUtility.Destroy(activeFsm); }); // menu.AddSeparator(""); // Separator // if (BehaviourWindow.Instance != null) // menu.AddItem(new GUIContent("Refresh"), false, BehaviourWindow.Instance.Refresh); // else // menu.AddDisabledItem(new GUIContent("Refresh")); // Shows the context menu menu.ShowAsContext(); }
/// <summary> /// Destroys the supplied state. /// Automatically handles undo. /// <param name="state">The state to be destroyed.</param> /// </summary> public static void Destroy(InternalStateBehaviour state) { // its a valid state if (state != null) { var gameObject = state.gameObject; // stores the gameObject to set dirty flag var isPrefab = FileUtility.IsPrefab(gameObject); var monoState = state as InternalMonoState; // its a mono state? // It is a fsm? if (state is ParentBehaviour) { var parent = state as ParentBehaviour; foreach (var child in parent.states) { StateUtility.Destroy(child); } } if (Application.isPlaying && !isPrefab) { // Its a MonoState and the user wants to destroy mono behaviour to? if (monoState != null && monoState.monoBehaviour != null && EditorUtility.DisplayDialog("Destroy MonoBehaviour?", "Do you want to destroy the " + monoState.monoBehaviour.GetType().ToString() + "?", "Ok", "Cancel")) { var monoStateGO = monoState.gameObject; Object.Destroy(monoState.monoBehaviour); EditorUtility.SetDirty(monoStateGO); } Object.Destroy(state); } else { // Register scene undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterSceneUndo("Delete"); #endif // Its a MonoState and the user wants to destroy the MonoBehaviour to? if (monoState != null && monoState.monoBehaviour != null && EditorUtility.DisplayDialog("Destroy MonoBehaviour?", "Do you want to destroy the " + monoState.monoBehaviour.GetType().ToString() + "?", "Ok", "Cancel")) { // Gets the MonoState game object var monoStateGO = monoState.gameObject; // Destroy the mono behaviour #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Object.DestroyImmediate(monoState.monoBehaviour, true); #else Undo.DestroyObjectImmediate(monoState.monoBehaviour); #endif // Set game object dirty flag EditorUtility.SetDirty(monoStateGO); } // Destroys the state #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Object.DestroyImmediate(state, true); #else Undo.DestroyObjectImmediate(state); #endif } EditorUtility.SetDirty(gameObject); } }
/// <summary> /// Paste the state in StateUtility.stateToPaste in the supplied fsm. /// <param name="gameObject">The target gameObject.</param> /// <param name="originalStates">The original states.</param> /// <param name="parent">Optionally parent for the cloned states.</param> /// </summary> public static void CloneStates(GameObject gameObject, InternalStateBehaviour[] originalStates, ParentBehaviour parent) { if (gameObject != null && originalStates != null && originalStates.Length > 0) { var orginalClone = new Dictionary <InternalStateBehaviour, InternalStateBehaviour>(); var originalFsm = parent != null ? originalStates[0].parent as InternalStateMachine : null; var newFsm = parent as InternalStateMachine; InternalStateBehaviour startState = null, concurrentState = null; InternalAnyState anyState = null; // Copy blackboard data? var newBlackboard = gameObject.GetComponent <InternalBlackboard>(); if (newBlackboard == null) { // Get the original blackboard InternalBlackboard originalBlackboard = originalStates[0].GetComponent <InternalBlackboard>(); #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterSceneUndo("Paste State"); // Create the new blacbkoard newBlackboard = gameObject.AddComponent(originalBlackboard.GetType()) as InternalBlackboard; #else // Create the new blacbkoard newBlackboard = gameObject.AddComponent(originalBlackboard.GetType()) as InternalBlackboard; if (newBlackboard != null) { Undo.RegisterCreatedObjectUndo(newBlackboard, "Paste State"); } #endif // Copy serialized values EditorUtility.CopySerialized(originalBlackboard, newBlackboard); } foreach (InternalStateBehaviour state in originalStates) { // Don't clone AnyState in StateMachines if (state != null && (newFsm == null || !(state is InternalAnyState) || newFsm.anyState == null)) { #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterSceneUndo("Paste State"); // Create a new state var newState = gameObject.AddComponent(state.GetType()) as InternalStateBehaviour; #else // Create a new state var newState = gameObject.AddComponent(state.GetType()) as InternalStateBehaviour; if (newState != null) { Undo.RegisterCreatedObjectUndo(newState, "Paste State"); } #endif if (newState != null) { // Store state orginalClone.Add(state, newState); // Copy serialized values EditorUtility.CopySerialized(state, newState); // Update blackboard if (state.gameObject != newState.gameObject) { var serialObj = new SerializedObject(newState); serialObj.FindProperty("m_Blackboard").objectReferenceValue = newBlackboard; serialObj.ApplyModifiedProperties(); serialObj.Dispose(); } // Update the AnyState, StartState and ConcurrentState if (newState is InternalStateMachine) { var fsm = newState as InternalStateMachine; fsm.startState = null; fsm.concurrentState = null; fsm.anyState = null; } EditorUtility.SetDirty(newState); // Set new parent if (parent != null) { newState.parent = parent; // Update position if (parent == state.parent) { newState.position += new Vector2(20f, 20f); } } else { newState.parent = null; } // Saves state and sets dirty flag INodeOwner nodeOwner = newState as INodeOwner; if (nodeOwner != null) { nodeOwner.LoadNodes(); StateUtility.SetDirty(nodeOwner); } else { EditorUtility.SetDirty(newState); } // Try to get the StartState, AnyState and ConcurrentState if (originalFsm != null) { if (originalFsm.startState == state) { startState = newState; } if (anyState == null) { anyState = newState as InternalAnyState; } if (originalFsm.concurrentState == state) { concurrentState = newState; } } } } } // Set StartState, AnyState and ConcurrentState if (newFsm != null) { if (newFsm.startState == null) { newFsm.startState = startState; } if (newFsm.anyState == null) { newFsm.anyState = anyState; } if (newFsm.concurrentState == null) { newFsm.concurrentState = concurrentState; } EditorUtility.SetDirty(newFsm); } // Try to update the transitions' destination foreach (KeyValuePair <InternalStateBehaviour, InternalStateBehaviour> pair in orginalClone) { InternalStateBehaviour state = pair.Key; InternalStateBehaviour newState = pair.Value; // Update the newState transition for (int i = 0; i < newState.transitions.Length && i < state.transitions.Length; i++) { // The original destination is valid? if (state.transitions[i].destination != null && orginalClone.ContainsKey(state.transitions[i].destination)) { newState.transitions[i].destination = orginalClone[state.transitions[i].destination]; } } if (newState is ParentBehaviour) { var stateAsParent = state as ParentBehaviour; // Removes the newState from the children state to avoid an infinite loop List <InternalStateBehaviour> children = stateAsParent.states; if (children.Contains(newState)) { children.Remove(newState); } StateUtility.CloneStates(newState.gameObject, children.ToArray(), newState as ParentBehaviour); } EditorUtility.SetDirty(newState); } EditorUtility.SetDirty(gameObject); } }
/// <summary> /// Draw the transition arrows. /// </summary> public override void OnGUIAfterWindows() { if (m_StatesGUI != null) { // Trying to create connections? if (TransitionDragAndDrop.dragging != null) { switch (Event.current.type) { // Mouse Up? Let's try to connect a transition destination... case EventType.MouseUp: // Mouse button left? if (Event.current.button == 0) { var mousePos = Event.current.mousePosition; // The mouse is over a StateGUI? foreach (var guiState in m_StatesGUI) { if (guiState.rect.Contains(mousePos)) { StateUtility.SetNewDestination(TransitionDragAndDrop.state, TransitionDragAndDrop.dragging, guiState.state); Refresh(); Event.current.Use(); break; } } // Ignores the Transition drag'n & drop operation TransitionDragAndDrop.AcceptDrag(); Repaint(); } break; // Cancel drag case EventType.Used: goto case EventType.Ignore; case EventType.Ignore: TransitionDragAndDrop.AcceptDrag(); Event.current.Use(); Repaint(); break; // Draws the transition destination when dragging. case EventType.Repaint: // Is dragging? if (TransitionDragAndDrop.isDragging) { // Validate dragging members var guiState = TransitionDragAndDrop.guiState; if (guiState != null && guiState.state != null) { // Gets the mouse position and creates a Rect var mousePos = Event.current.mousePosition; var destRect = new Rect(mousePos.x, mousePos.y, 0, 0); var destYOffset = 0f; // The mouse is over a StateGUI? foreach (var _guiState in m_StatesGUI) { if (_guiState.rect.Contains(mousePos)) { // Updates the destRect and destYOffset. destRect = _guiState.rect; destYOffset = StateGUI.defaultHeight * .5f; break; } } // Draws the bezier line TransitionDragAndDrop.transitionGUI.DrawArrow(guiState.rect, destRect, destYOffset); } } break; case EventType.MouseDrag: if (TransitionDragAndDrop.isDragging) { Repaint(); } break; } } } // Get the current event Event current = Event.current; var activeFsm = BehaviourWindow.activeFsm; // cached activeFsm switch (current.type) { // Show context menu? case EventType.ContextClick: OnContextMenu(); Event.current.Use(); break; case EventType.MouseDown: // If the left mouse button is down then unselect the state and start the dragging rect if (current.button == 0) { BehaviourWindow.activeState = null; EditorGUIUtility.hotControl = 0; GUIUtility.keyboardControl = 0; if (!current.alt && !current.shift) { m_SelectionStartPoint = current.mousePosition; m_SelectionRect = true; } current.Use(); } break; // Event ignored? case EventType.Ignore: // Cancel selection rect? if (m_SelectionRect) { goto case EventType.MouseUp; } break; // Cancel selection rect? case EventType.MouseUp: if (m_SelectionRect) { m_SelectionRect = false; GUIUtility.hotControl = 0; SelectNodesInRect(GetRectFromPoints(m_SelectionStartPoint, current.mousePosition)); current.Use(); } break; // Selection rect? case EventType.MouseDrag: if (m_SelectionRect) { current.Use(); } else if (current.button == 2) { m_ScrollView -= current.delta; current.Use(); } break; // Delete selected states? case EventType.ValidateCommand: // Use event to call event ExecuteCommand if (current.commandName == "Paste" && activeFsm != null && StateUtility.statesToPaste != null && StateUtility.statesToPaste.Length > 0) { current.Use(); } else if (current.commandName == "Copy" && activeFsm != null && Selection.objects.Length > 0) { current.Use(); } else if (current.commandName == "Duplicate" && activeFsm != null) { // Is there a selected state? foreach (var obj in Selection.objects) { if (obj is InternalStateBehaviour) { current.Use(); break; } } } else if ((current.commandName == "Delete" || current.commandName == "SoftDelete") && BehaviourWindow.activeState != null) { current.Use(); } break; case EventType.ExecuteCommand: if (current.commandName == "Paste") { StateUtility.PasteStates(activeFsm); } else if (current.commandName == "Copy") { StateUtility.CopySelectedStates(); } else if (current.commandName == "Duplicate") { StateUtility.CopySelectedStates(); StateUtility.PasteStates(activeFsm); } else if (current.commandName == "Delete" || current.commandName == "SoftDelete") { foreach (InternalStateBehaviour state in Selection.objects) { StateUtility.Destroy(state); } Refresh(); current.Use(); } break; // Dragging? case EventType.DragUpdated: if (DragAndDrop.objectReferences.Length > 0 && DragAndDrop.objectReferences[0] is MonoScript && BehaviourWindow.activeFsm != null) { DragAndDrop.visualMode = DragAndDropVisualMode.Copy; } break; // Drag perform? case EventType.DragPerform: if (DragAndDrop.objectReferences.Length > 0 && DragAndDrop.objectReferences[0] is MonoScript && BehaviourWindow.activeFsm != null) { var index = 0; // index of added states var mousePosition = current.mousePosition - new Vector2(StateGUI.defaultWidth, StateGUI.defaultHeight) * .5f; // Register undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterSceneUndo("Add States"); #endif // Get all GetScripts var scripts = new List <MonoScript>(); foreach (var obj in DragAndDrop.objectReferences) { var script = obj as MonoScript; if (script != null) { scripts.Add(script); } } // Go trough all monoscripts foreach (MonoScript monoScript in scripts) { var type = monoScript.GetClass(); // The type is a valid InternalStateBehaviour instance? if (type != null && !type.IsAbstract) { // Add state behaviour if (type.IsSubclassOf(typeof(InternalStateBehaviour))) { var newState = StateUtility.AddState(activeFsm, type); // The state is valid? if (newState != null) { // Set the newState position newState.position = mousePosition + new Vector2(StateGUI.defaultWidth * index * .5f, 20f * index); index++; } } // Add mono state else if (type.IsSubclassOf(typeof(MonoBehaviour))) { var newMonoState = StateUtility.AddState(activeFsm, typeof(InternalMonoState)) as InternalMonoState; // Set the newMonoState position if (newMonoState != null) { // Set mono state position newMonoState.position = mousePosition + new Vector2(StateGUI.defaultWidth * index * .5f, 20f * index); index++; newMonoState.monoBehaviour = newMonoState.gameObject.AddComponent(type) as MonoBehaviour; // Register undo #if !UNITY_4_0_0 && !UNITY_4_1 && !UNITY_4_2 if (newMonoState.monoBehaviour != null) { Undo.RegisterCreatedObjectUndo(newMonoState.monoBehaviour, "Add Component"); } #endif } } } } // Sets dirty flag if (activeFsm.gameObject != null) { EditorUtility.SetDirty(activeFsm.gameObject); } // Accept drag DragAndDrop.AcceptDrag(); current.Use(); Refresh(); } break; // Draw the selection rect? case EventType.Repaint: if (m_SelectionRect) { s_Styles.selectionRect.Draw(GetRectFromPoints(m_SelectionStartPoint, current.mousePosition), false, false, false, false); } break; } GUI.EndScrollView(); // Close scroolView. GUI.EndGroup(); // Close group }
/// <summary> /// Shows the context menu. /// </summary> protected virtual void OnContextMenu() { // Create the menu var menu = new UnityEditor.GenericMenu(); if (m_State != null) { // Set as start state if (m_State.fsm != null && !(m_State is InternalAnyState)) { menu.AddItem(new GUIContent("Set as Start"), false, delegate() { StateUtility.SetAsStart(m_State); this.Refresh(); }); } else { menu.AddDisabledItem(new GUIContent("Set as Start")); } // Set as concurrent state if (m_State.fsm != null && !(m_State is InternalAnyState)) { if (m_IsConcurrent) { menu.AddItem(new GUIContent("Set as Not Concurrent"), false, delegate() { StateUtility.RemoveConcurrentState(m_State.fsm); this.Refresh(); }); } else { menu.AddItem(new GUIContent("Set as Concurrent"), false, delegate() { StateUtility.SetAsConcurrent(m_State); this.Refresh(); }); } } else { menu.AddDisabledItem(new GUIContent("Set as Concurrent")); } // Set as enabled if (m_State.fsm != null /*&& m_State.fsm.enabled*/ && Application.isPlaying && !(m_State is InternalAnyState)) { menu.AddItem(new GUIContent("Set as Enabled"), false, delegate() { m_State.enabled = true; }); } else { menu.AddDisabledItem(new GUIContent("Set as Enabled")); } // Add Transitions // Add none menu.AddItem(new GUIContent("Add Transition/None"), false, delegate() { StateUtility.AddTransition(m_State, 0); CreateTransitionGUIs(); }); // Add blackboard events var blackboard = m_State.blackboard; if (blackboard != null) { foreach (var fsmEvent in blackboard.fsmEvents) { int eventId = fsmEvent.id; menu.AddItem(new GUIContent("Add Transition/" + fsmEvent.name), false, delegate() { StateUtility.AddTransition(m_State, eventId); CreateTransitionGUIs(); }); } } // Add GlobalBlackboard events // This is not The GlobalBlackboard? if (InternalGlobalBlackboard.Instance != null && blackboard != InternalGlobalBlackboard.Instance) { foreach (var globalEvent in InternalGlobalBlackboard.Instance.fsmEvents) { int eventId = globalEvent.id; var eventName = globalEvent.isSystem ? "Add Transition/System/" + globalEvent.name : "Add Transition/Global/" + globalEvent.name; menu.AddItem(new GUIContent(eventName), false, delegate() { StateUtility.AddTransition(m_State, eventId); CreateTransitionGUIs(); }); } } // Separator menu.AddSeparator(""); // Copy menu.AddItem(new GUIContent("Copy State"), false, delegate() { StateUtility.CopySelectedStates(); }); // Paste if (StateUtility.statesToPaste != null && StateUtility.statesToPaste.Length > 0 && m_State.fsm != null) { menu.AddItem(new GUIContent("Paste State"), false, delegate() { StateUtility.PasteStates(m_State.fsm); }); } else { menu.AddDisabledItem(new GUIContent("Paste State")); } // Duplicate if (m_State.fsm != null) { menu.AddItem(new GUIContent("Duplicate State"), false, delegate() { var statesToPaste = new List <InternalStateBehaviour>(BehaviourWindow.activeStates); if (!statesToPaste.Contains(m_State)) { statesToPaste.Add(m_State); } StateUtility.CloneStates(m_State.gameObject, statesToPaste.ToArray(), m_State.fsm); } ); } else { menu.AddDisabledItem(new GUIContent("Duplicate State")); } // Separator menu.AddSeparator(""); // Delete menu.AddItem(new GUIContent("Delete"), false, delegate() { StateUtility.Destroy(m_State); this.Refresh(); }); } else { menu.AddDisabledItem(new GUIContent("Set as Start")); menu.AddDisabledItem(new GUIContent("Set as Enabled")); menu.AddDisabledItem(new GUIContent("Add Transition")); menu.AddSeparator(""); menu.AddDisabledItem(new GUIContent("Copy State")); menu.AddDisabledItem(new GUIContent("Paste State")); menu.AddSeparator(""); menu.AddDisabledItem(new GUIContent("Delete")); } // Show the context menu menu.ShowAsContext(); }