using System; using UnityEngine.Playables; using UnityEngine.SceneManagement; namespace UnityEngine.Timeline { /// /// Playable that controls and instantiates a Prefab. /// public class PrefabControlPlayable : PlayableBehaviour { GameObject m_Instance; #if UNITY_EDITOR private bool m_IsActiveCached; #endif /// /// Creates a Playable with a PrefabControlPlayable behaviour attached /// /// The PlayableGraph to inject the Playable into. /// The prefab to instantiate from /// Transform to parent instance to. Can be null. /// Returns a Playabe with PrefabControlPlayable behaviour attached. public static ScriptPlayable Create(PlayableGraph graph, GameObject prefabGameObject, Transform parentTransform) { if (prefabGameObject == null) return ScriptPlayable.Null; var handle = ScriptPlayable.Create(graph); handle.GetBehaviour().Initialize(prefabGameObject, parentTransform); return handle; } /// /// The instance of the prefab created by this behaviour /// public GameObject prefabInstance { get { return m_Instance; } } /// /// Initializes the behaviour with a prefab and parent transform /// /// The prefab to instantiate from /// Transform to parent instance to. Can be null. /// The created instance 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; } /// /// This function is called when the Playable that owns the PlayableBehaviour is destroyed. /// /// The playable this behaviour is attached to. 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 } /// /// This function is called when the Playable play state is changed to Playables.PlayState.Playing. /// /// The Playable that owns the current PlayableBehaviour. /// A FrameData structure that contains information about the current frame context. public override void OnBehaviourPlay(Playable playable, FrameData info) { if (m_Instance == null) return; m_Instance.SetActive(true); #if UNITY_EDITOR m_IsActiveCached = true; #endif } /// /// This function is called when the Playable play state is changed to PlayState.Paused. /// /// The playable this behaviour is attached to. /// A FrameData structure that contains information about the current frame context. 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); } } } }