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