/// <summary> /// Shows a context menu to delete or unparent a state. /// <param name="state">The state to delete or unparent.</param> /// </summary> void OnDeleteUnparentContextMenu(InternalStateBehaviour state) { var menu = new GenericMenu(); menu.AddItem(new GUIContent("Unparent"), false, this.OnUnparentState, state); menu.AddItem(new GUIContent("Delete"), false, this.OnDeleteState, state); menu.ShowAsContext(); }
/// <summary> /// Refresh the window content. /// </summary> public void Refresh() { OnSelectionChange(); if (m_ParentGUI != null) { m_ParentGUI.Refresh(); } InternalStateBehaviour.ResetRefresh(); }
/// <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); }
public static int GetStateMachineDepth(this InternalStateBehaviour state) { int depth = 0; InternalStateMachine fsm = state.fsm; while (fsm != null) { fsm = fsm.fsm; depth++; } return(depth); }
/// <summary> /// Adds a new transition to the state. /// Automatically handles undo. /// <param name="state">The state to add a new transition.</param> /// <param name="eventID">The event id of the new transition.</param> /// </summary> public static void AddTransition(InternalStateBehaviour state, int eventID) { // Register undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(state, "Create Transition"); #else Undo.RecordObject(state, "Create Transition"); #endif state.AddTransition(eventID); EditorUtility.SetDirty(state); }
public static int GetBehaviourTreeDepth(this InternalStateBehaviour state) { int depth = 0; InternalBehaviourTree tree = state.tree; while (tree != null) { tree = tree.tree; depth++; } return(depth); }
/// <summary> /// Draw the state pop-up. /// </summary> public override void OnGUI(SerializedNodeProperty property, ActionNode node, GUIContent guiContent) { // InternalStateBehaviour if (property.propertyType == NodePropertyType.UnityObject && property.type == typeof(InternalStateBehaviour)) { var rect = GUILayoutUtility.GetRect(GUIContent.none, EditorStyles.popup); var id = GUIUtility.GetControlID(FocusType.Passive); var popupRect = EditorGUI.PrefixLabel(rect, id, guiContent); var state = property.value as InternalStateBehaviour; // Set the pop-up color var oldGUIColor = GUI.color; if (state == null) { GUI.color = Color.red; } if (GUI.Button(popupRect, state != null ? state.stateName : "Null", EditorStyles.popup)) { // Get states var states = node.tree.states; // Create the pop-up menu var menu = new GenericMenu(); // Add null menu.AddItem(new GUIContent("Null"), state == null, delegate() { property.value = (InternalStateBehaviour)null; }); // Add states for (int i = 0; i < states.Count; i++) { InternalStateBehaviour currentState = states[i]; //setField = field != null ? new SetField(this.target, target, states[i], field, m_Tree) : new SetField(this.target, arrayField, index, states[i], m_Tree); menu.AddItem(new GUIContent(states[i].stateName), state == states[i], delegate() { property.value = currentState; }); } // Show menu menu.ShowAsContext(); } // Restore GUI.color GUI.color = oldGUIColor; } else { EditorGUILayout.LabelField(guiContent, new GUIContent("Use TreeState with InternalStateBehaviour.")); } }
/// <summary> /// Sets a new destination state to the supplied transition. /// <param name="state">The state that owns the target transition.</param> /// <param name="transition">The transition to set the new destination.</param> /// <param name="destination">The new destination state.</param> /// </summary> public static void SetNewDestination(InternalStateBehaviour state, StateTransition transition, InternalStateBehaviour destination) { // Validate members if (state != null && transition != null) { // Register undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(state, "Transition Destination"); #else Undo.RecordObject(state, "Transition Destination"); #endif // Create a connection to the destination transition.destination = destination; // Set state dirty flag EditorUtility.SetDirty(state); } }
/// <summary> /// Sets a new event id in the supplied transition. /// <param name="state">The state that owns the target transition.</param> /// <param name="transition">The transition to set the new event id.</param> /// <param name="eventId">The new event id.</param> /// </summary> public static void SetNewEvent(InternalStateBehaviour state, StateTransition transition, int eventId) { // Validate members if (state != null && transition != null) { // Register undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(state, "Transition Event"); #else Undo.RecordObject(state, "Transition Event"); #endif // Set transition event transition.eventID = eventId; // Set state dirty flag EditorUtility.SetDirty(state); } }
/// <summary> /// The constructor. /// <param name="id">A unique id for the window.</param> /// <param name="state">The state that this object will draw.</param> /// </summary> public StateGUI(int id, InternalStateBehaviour state) { m_State = state; m_Id = id; // Get state data if (m_State != null) { m_Icon = IconUtility.GetIcon(m_State.GetType(), m_State); // It's the start state? m_IsStart = m_State.fsm != null && m_State.fsm.startState == m_State; // It's the concurrent state? m_IsConcurrent = m_State.fsm != null && m_State.fsm.concurrentState == m_State; // Transition visual debugging m_State.onTransitionPerformed += OnTransitionPerformed; // Build gui transitions CreateTransitionGUIs(); } }
/// <summary> /// Removes the transition from the state. /// <param name="state">The state that owns the target transition.</param> /// <param name="transition">The transition to be removed.</param> /// </summary> public static void RemoveTransition(InternalStateBehaviour state, StateTransition transition) { // Validate members if (state != null && transition != null) { // Register undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(state, "Delete Transition"); #else Undo.RecordObject(state, "Delete Transition"); #endif // Remove transition state.RemoveTransition(transition); // Set state dirty flag EditorUtility.SetDirty(state); } }
/// <summary> /// Update the hideFlags of the supplied state. /// <param name ="state">The target state.</param> /// </summary> static void OnStateHideFlag(InternalStateBehaviour state) { // It's a valid state? if (state != null) { // It is not a prefab? if (!UnityEditor.AssetDatabase.Contains(state.gameObject)) { // The state is not a root parent and the hide flag is True? if (state.hideFlag && !state.isRoot) { state.hideFlags = HideFlags.HideInInspector; } else { state.hideFlags = (HideFlags)0; } } } }
/// <summary> /// Returns the destination of a transition. /// </summary> protected InternalStateBehaviour GetDestination(InternalStateMachine fsm, InternalStateBehaviour destination) { if (fsm == null || destination == null) { return(null); } InternalStateMachine currentFsm = destination.fsm; if (currentFsm == fsm) { return(destination); } while (currentFsm != null && fsm != currentFsm.fsm) { currentFsm = currentFsm.fsm; } return(currentFsm); }
/// <summary> /// Context menu callback to delete a state. /// <param name="userData">The state to be deleted.</param> /// </summary> void OnDeleteState(object userData) { var state = userData as InternalStateBehaviour; if (state != null) { if (Application.isPlaying && !AssetDatabase.Contains(state.gameObject)) { InternalStateBehaviour.Destroy(state); } else { #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterSceneUndo("Delete"); Object.DestroyImmediate(state, true); #else Undo.DestroyObjectImmediate(state); #endif } } }
/// <summary> /// Sets the state as the concurrent state of the fsm. /// Automatically handles undo. /// <param name="state">The new fsm concurrent state.</param> /// </summary> public static void SetAsConcurrent(InternalStateBehaviour state) { // Get the fsm var fsm = state.fsm; // The fsm is valid and the state is not the start state? if (fsm != null && fsm.concurrentState != state) { // Register undo #if UNITY_4_0_0 || UNITY_4_1 || UNITY_4_2 Undo.RegisterUndo(fsm, "Concurrent State"); #else Undo.RecordObject(fsm, "Concurrent State"); #endif fsm.concurrentState = state; EditorUtility.SetDirty(fsm); // EditorUtility.SetDirty(state); // Repaint state inspector } }
/// <summary> /// Class constructor. /// <param name="stateTransition">The target transition.</param> /// <param name="destination">The target destination.</param> /// <param name="index">The transition index.</param> /// <param name="blackboard">The state blackboard.</param> /// </summary> public TransitionGUI(StateTransition stateTransition, InternalStateBehaviour destination, int index, InternalBlackboard blackboard) { m_Transition = stateTransition; m_Destination = destination; // It's a global event? if (m_Transition.eventID < 0) { if (InternalGlobalBlackboard.Instance != null) { m_FsmEvent = InternalGlobalBlackboard.Instance.GetFsmEvent(m_Transition.eventID); } } // It's a local variable and the blackboard is not null? else if (m_Transition.eventID > 0 && blackboard != null) { m_FsmEvent = blackboard.GetFsmEvent(m_Transition.eventID); } // Get the transition arrow vertical offset m_VerticalOffset = StateGUI.defaultHeight + TransitionGUI.defaultHeight * (index + .35f); }
/// <summary> /// Shows a menu to select states that are in the same parent as the supplied state. /// <param name="state">The target state.</param> /// </summary> void ShowStateSelectionMenu(InternalStateBehaviour state) { var menu = new GenericMenu(); var states = state.GetComponents <InternalStateBehaviour>(); var uniqueNames = new List <string>(); // Build the menu for (int i = 0; i < states.Length; i++) { // Get the current state var currentState = states[i]; // The current state has the same parent as the supplied state? if (currentState.parent == state.parent) { string currentName = StringHelper.GetUniqueNameInList(uniqueNames, currentState.stateName); uniqueNames.Add(currentName); menu.AddItem(new GUIContent(currentName), state == currentState, delegate() { activeParent = currentState as ParentBehaviour ?? currentState.parent; Selection.objects = new UnityEngine.Object[] { currentState }; }); } } menu.ShowAsContext(); }
/// <summary> /// Creates the TransitionGUIs list to display the transition event name. /// </summary> protected virtual void CreateTransitionGUIs() { m_TransitionGUIs.Clear(); if (m_State != null) { // Get the blackobard var blackboard = m_State.blackboard; // Get the transitions list var transitions = m_State.transitions; for (int i = 0; i < transitions.Length; i++) { // Get the destination InternalStateBehaviour destination = GetDestination(m_State.fsm, transitions[i].destination); if (destination == null && transitions[i].destination != null && GetDestination(transitions[i].destination.fsm, m_State) != null) { destination = m_State.fsm; } m_TransitionGUIs.Add(new TransitionGUI(transitions[i], destination, i, blackboard)); } } }
/// <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> /// Returns true if he supplied state is not null and is not a start state. /// <param name="state">The state to test.</param> /// <returns>False if the state is a start state; True otherwise.</returns> /// </summary> protected bool IsNotStart(InternalStateBehaviour state) { return(state != null && state.fsm != null && state.fsm.startState != state); }
public SetParent(InternalStateBehaviour state, ParentBehaviour newParent) { this.state = state; this.newParent = newParent; }
/// <summary> /// Returns true if the supplied state is not null and is not a root. /// <param name="state">The state to test.</param> /// <returns>False if the state is a root; True otherwise.</returns> /// </summary> protected bool IsNotRoot(InternalStateBehaviour state) { return(state != null && !state.isRoot); }
/// <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> /// 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); } }