/// <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> /// 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> /// 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(); }