using System; using UnityEngine.Playables; using UnityEngine.SceneManagement; namespace UnityEngine.Timeline { /// <summary> /// Playable that controls and instantiates a Prefab. /// </summary> public class PrefabControlPlayable : PlayableBehaviour { GameObject m_Instance; #if UNITY_EDITOR private bool m_IsActiveCached; #endif /// <summary> /// Creates a Playable with a PrefabControlPlayable behaviour attached /// </summary> /// <param name="graph">The PlayableGraph to inject the Playable into.</param> /// <param name="prefabGameObject">The prefab to instantiate from</param> /// <param name="parentTransform">Transform to parent instance to. Can be null.</param> /// <returns>Returns a Playabe with PrefabControlPlayable behaviour attached.</returns> public static ScriptPlayable<PrefabControlPlayable> Create(PlayableGraph graph, GameObject prefabGameObject, Transform parentTransform) { if (prefabGameObject == null) return ScriptPlayable<PrefabControlPlayable>.Null; var handle = ScriptPlayable<PrefabControlPlayable>.Create(graph); handle.GetBehaviour().Initialize(prefabGameObject, parentTransform); return handle; } /// <summary> /// The instance of the prefab created by this behaviour /// </summary> public GameObject prefabInstance { get { return m_Instance; } } /// <summary> /// Initializes the behaviour with a prefab and parent transform /// </summary> /// <param name="prefabGameObject">The prefab to instantiate from</param> /// <param name="parentTransform">Transform to parent instance to. Can be null.</param> /// <returns>The created instance</returns> public GameObject Initialize(GameObject prefabGameObject, Transform parentTransform) { if (prefabGameObject == null) throw new ArgumentNullException("Prefab cannot be null"); if (m_Instance != null) { Debug.LogWarningFormat("Prefab Control Playable ({0}) has already been initialized with a Prefab ({1}).", prefabGameObject.name, m_Instance.name); } else { #if UNITY_EDITOR if (!Application.isPlaying) { m_Instance = (GameObject)UnityEditor.PrefabUtility.InstantiatePrefab(prefabGameObject, parentTransform); UnityEditor.PrefabUtility.prefabInstanceUpdated += OnPrefabUpdated; } else #endif { m_Instance = Object.Instantiate(prefabGameObject, parentTransform, false); } m_Instance.name = prefabGameObject.name + " [Timeline]"; m_Instance.SetActive(false); SetHideFlagsRecursive(m_Instance); } return m_Instance; } /// <summary> /// This function is called when the Playable that owns the PlayableBehaviour is destroyed. /// </summary> /// <param name="playable">The playable this behaviour is attached to.</param> public override void OnPlayableDestroy(Playable playable) { if (m_Instance) { if (Application.isPlaying) Object.Destroy(m_Instance); else Object.DestroyImmediate(m_Instance); } #if UNITY_EDITOR UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabUpdated; #endif } /// <summary> /// This function is called when the Playable play state is changed to Playables.PlayState.Playing. /// </summary> /// <param name="playable">The Playable that owns the current PlayableBehaviour.</param> /// <param name="info">A FrameData structure that contains information about the current frame context.</param> public override void OnBehaviourPlay(Playable playable, FrameData info) { if (m_Instance == null) return; m_Instance.SetActive(true); #if UNITY_EDITOR m_IsActiveCached = true; #endif } /// <summary> /// This function is called when the Playable play state is changed to PlayState.Paused. /// </summary> /// <param name="playable">The playable this behaviour is attached to.</param> /// <param name="info">A FrameData structure that contains information about the current frame context.</param> public override void OnBehaviourPause(Playable playable, FrameData info) { // OnBehaviourPause can be called if the graph is stopped for a variety of reasons // the effectivePlayState will test if the pause is due to the clip being out of bounds if (m_Instance != null && info.effectivePlayState == PlayState.Paused) { m_Instance.SetActive(false); #if UNITY_EDITOR m_IsActiveCached = false; #endif } } #if UNITY_EDITOR void OnPrefabUpdated(GameObject go) { if (go == m_Instance) { SetHideFlagsRecursive(go); go.SetActive(m_IsActiveCached); } } #endif static void SetHideFlagsRecursive(GameObject gameObject) { if (gameObject == null) return; gameObject.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor; if (!Application.isPlaying) gameObject.hideFlags |= HideFlags.HideInHierarchy; foreach (Transform child in gameObject.transform) { SetHideFlagsRecursive(child.gameObject); } } } }