Nav apraksta
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

DirectorControlPlayable.cs 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. using System;
  2. using UnityEngine;
  3. using UnityEngine.Playables;
  4. namespace UnityEngine.Timeline
  5. {
  6. /// <summary>
  7. /// Playable Behaviour used to control a PlayableDirector.
  8. /// </summary>
  9. /// <remarks>
  10. /// This playable is used to control other PlayableDirector components from a Timeline sequence.
  11. /// </remarks>
  12. public class DirectorControlPlayable : PlayableBehaviour
  13. {
  14. /// <summary>
  15. /// Represents the action taken when the DirectorControlPlayable is stopped.
  16. /// </summary>
  17. public enum PauseAction
  18. {
  19. /// <summary>
  20. /// Stop the <see cref="PlayableDirector"/> on pause.
  21. /// </summary>
  22. StopDirector,
  23. /// <summary>
  24. /// Pause the <see cref="PlayableDirector"/> on pause.
  25. /// </summary>
  26. PauseDirector,
  27. }
  28. /// <summary>
  29. /// The PlayableDirector being controlled by this PlayableBehaviour
  30. /// </summary>
  31. public PlayableDirector director;
  32. /// <summary>
  33. /// Sets the action to perform when the playable is paused. <see cref="PauseAction"/>
  34. /// </summary>
  35. public PauseAction pauseAction;
  36. bool m_SyncTime = false;
  37. double m_AssetDuration = double.MaxValue;
  38. /// <summary>
  39. /// Creates a Playable with a DirectorControlPlayable attached
  40. /// </summary>
  41. /// <param name="graph">The graph to inject the playable into</param>
  42. /// <param name="director">The director to control</param>
  43. /// <returns>Returns a Playable with a DirectorControlPlayable attached</returns>
  44. public static ScriptPlayable<DirectorControlPlayable> Create(PlayableGraph graph, PlayableDirector director)
  45. {
  46. if (director == null)
  47. return ScriptPlayable<DirectorControlPlayable>.Null;
  48. var handle = ScriptPlayable<DirectorControlPlayable>.Create(graph);
  49. handle.GetBehaviour().director = director;
  50. #if UNITY_EDITOR
  51. if (!Application.isPlaying && UnityEditor.PrefabUtility.IsPartOfPrefabInstance(director))
  52. UnityEditor.PrefabUtility.prefabInstanceUpdated += handle.GetBehaviour().OnPrefabUpdated;
  53. #endif
  54. return handle;
  55. }
  56. /// <summary>
  57. /// This function is called when this PlayableBehaviour is destroyed.
  58. /// </summary>
  59. /// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
  60. public override void OnPlayableDestroy(Playable playable)
  61. {
  62. #if UNITY_EDITOR
  63. if (!Application.isPlaying)
  64. UnityEditor.PrefabUtility.prefabInstanceUpdated -= OnPrefabUpdated;
  65. #endif
  66. if (director != null && director.playableAsset != null)
  67. director.Stop();
  68. }
  69. /// <summary>
  70. /// This function is called during the PrepareFrame phase of the PlayableGraph.
  71. /// </summary>
  72. /// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
  73. /// <param name="info">A FrameData structure that contains information about the current frame context.</param>
  74. public override void PrepareFrame(Playable playable, FrameData info)
  75. {
  76. if (director == null || !director.isActiveAndEnabled || director.playableAsset == null)
  77. return;
  78. // resync the time on an evaluate or a time jump (caused by loops, or some setTime calls)
  79. m_SyncTime |= (info.evaluationType == FrameData.EvaluationType.Evaluate) ||
  80. DetectDiscontinuity(playable, info);
  81. SyncSpeed(info.effectiveSpeed);
  82. SyncStart(playable.GetGraph(), playable.GetTime());
  83. #if !UNITY_2021_2_OR_NEWER
  84. SyncStop(playable.GetGraph(), playable.GetTime());
  85. #endif
  86. }
  87. /// <summary>
  88. /// This function is called when the Playable play state is changed to Playables.PlayState.Playing.
  89. /// </summary>
  90. /// <param name="playable">The Playable that owns the current PlayableBehaviour.</param>
  91. /// <param name="info">A FrameData structure that contains information about the current frame context.</param>
  92. public override void OnBehaviourPlay(Playable playable, FrameData info)
  93. {
  94. m_SyncTime = true;
  95. if (director != null && director.playableAsset != null)
  96. m_AssetDuration = director.playableAsset.duration;
  97. }
  98. /// <summary>
  99. /// This function is called when the Playable play state is changed to PlayState.Paused.
  100. /// </summary>
  101. /// <param name="playable">The playable this behaviour is attached to.</param>
  102. /// <param name="info">A FrameData structure that contains information about the current frame context.</param>
  103. public override void OnBehaviourPause(Playable playable, FrameData info)
  104. {
  105. if (director != null && director.playableAsset != null)
  106. {
  107. if (info.effectivePlayState == PlayState.Playing ||
  108. info.effectivePlayState == PlayState.Paused && pauseAction == PauseAction.PauseDirector) // graph was paused
  109. {
  110. director.Pause();
  111. }
  112. else
  113. {
  114. director.Stop();
  115. }
  116. }
  117. }
  118. /// <summary>
  119. /// This function is called during the ProcessFrame phase of the PlayableGraph.
  120. /// </summary>
  121. /// <param name="playable">The playable this behaviour is attached to.</param>
  122. /// <param name="info">A FrameData structure that contains information about the current frame context.</param>
  123. /// <param name="playerData">unused</param>
  124. public override void ProcessFrame(Playable playable, FrameData info, object playerData)
  125. {
  126. if (director == null || !director.isActiveAndEnabled || director.playableAsset == null)
  127. return;
  128. if (m_SyncTime || DetectOutOfSync(playable))
  129. {
  130. UpdateTime(playable);
  131. if (director.playableGraph.IsValid())
  132. {
  133. director.playableGraph.Evaluate();
  134. #if TIMELINE_FRAMEACCURATE
  135. director.playableGraph.SynchronizeEvaluation(playable.GetGraph());
  136. #endif
  137. }
  138. else
  139. {
  140. director.Evaluate();
  141. }
  142. }
  143. m_SyncTime = false;
  144. #if UNITY_2021_2_OR_NEWER
  145. SyncStop(playable.GetGraph(), playable.GetTime());
  146. #endif
  147. }
  148. #if UNITY_EDITOR
  149. void OnPrefabUpdated(GameObject go)
  150. {
  151. // When the prefab asset is updated, we rebuild the graph to reflect the changes in editor
  152. if (UnityEditor.PrefabUtility.GetRootGameObject(director) == go)
  153. director.RebuildGraph();
  154. }
  155. #endif
  156. void SyncSpeed(double speed)
  157. {
  158. if (director.playableGraph.IsValid())
  159. {
  160. int roots = director.playableGraph.GetRootPlayableCount();
  161. for (int i = 0; i < roots; i++)
  162. {
  163. var rootPlayable = director.playableGraph.GetRootPlayable(i);
  164. if (rootPlayable.IsValid())
  165. {
  166. rootPlayable.SetSpeed(speed);
  167. }
  168. }
  169. }
  170. }
  171. void SyncStart(PlayableGraph graph, double time)
  172. {
  173. if (director.state == PlayState.Playing
  174. || !graph.IsPlaying()
  175. || (director.extrapolationMode == DirectorWrapMode.None && time > m_AssetDuration))
  176. return;
  177. #if TIMELINE_FRAMEACCURATE
  178. if (graph.IsMatchFrameRateEnabled())
  179. director.Play(graph.GetFrameRate());
  180. else
  181. director.Play();
  182. #else
  183. director.Play();
  184. #endif
  185. }
  186. void SyncStop(PlayableGraph graph, double time)
  187. {
  188. if (director.state == PlayState.Paused
  189. || (graph.IsPlaying() && (director.extrapolationMode != DirectorWrapMode.None || time < m_AssetDuration)))
  190. return;
  191. if (director.state == PlayState.Paused)
  192. return;
  193. bool expectedFinished = director.extrapolationMode == DirectorWrapMode.None && time > m_AssetDuration;
  194. if (expectedFinished || !graph.IsPlaying())
  195. director.Pause();
  196. }
  197. bool DetectDiscontinuity(Playable playable, FrameData info)
  198. {
  199. return Math.Abs(playable.GetTime() - playable.GetPreviousTime() - info.m_DeltaTime * info.m_EffectiveSpeed) > DiscreteTime.tickValue;
  200. }
  201. bool DetectOutOfSync(Playable playable)
  202. {
  203. double expectedTime = playable.GetTime();
  204. if (playable.GetTime() >= m_AssetDuration)
  205. {
  206. switch (director.extrapolationMode)
  207. {
  208. case DirectorWrapMode.None:
  209. expectedTime = m_AssetDuration;
  210. break;
  211. case DirectorWrapMode.Hold:
  212. expectedTime = m_AssetDuration;
  213. break;
  214. case DirectorWrapMode.Loop:
  215. expectedTime %= m_AssetDuration;
  216. break;
  217. }
  218. }
  219. if (!Mathf.Approximately((float)expectedTime, (float)director.time))
  220. {
  221. #if UNITY_EDITOR
  222. double lastDelta = playable.GetTime() - playable.GetPreviousTime();
  223. if (UnityEditor.Unsupported.IsDeveloperBuild())
  224. Debug.LogWarningFormat("Internal Warning - Control track desync detected on {2} ({0:F10} vs {1:F10} with delta {3:F10}). Time will be resynchronized. Known to happen with nested control tracks", playable.GetTime(), director.time, director.name, lastDelta);
  225. #endif
  226. return true;
  227. }
  228. return false;
  229. }
  230. // We need to handle loop modes explicitly since we are setting the time directly
  231. void UpdateTime(Playable playable)
  232. {
  233. double duration = Math.Max(0.1, director.playableAsset.duration);
  234. switch (director.extrapolationMode)
  235. {
  236. case DirectorWrapMode.Hold:
  237. director.time = Math.Min(duration, Math.Max(0, playable.GetTime()));
  238. break;
  239. case DirectorWrapMode.Loop:
  240. director.time = Math.Max(0, playable.GetTime() % duration);
  241. break;
  242. case DirectorWrapMode.None:
  243. director.time = Math.Min(duration, Math.Max(0, playable.GetTime()));
  244. break;
  245. }
  246. }
  247. }
  248. }