SequenceHierarchy.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using UnityEngine;
  4. using UnityEngine.Playables;
  5. using UnityEngine.Timeline;
  6. namespace UnityEditor.Timeline
  7. {
  8. class SequenceHierarchy : ScriptableObject
  9. {
  10. readonly List<ISequenceState> m_Sequences = new List<ISequenceState>();
  11. WindowState m_WindowState;
  12. [SerializeField]
  13. SequencePath m_SerializedPath;
  14. public ISequenceState masterSequence
  15. {
  16. get { return m_Sequences.FirstOrDefault(); }
  17. }
  18. public ISequenceState editSequence
  19. {
  20. get { return m_Sequences.LastOrDefault(); }
  21. }
  22. public int count
  23. {
  24. get { return m_Sequences.Count; }
  25. }
  26. public IEnumerable<ISequenceState> allSequences
  27. {
  28. get { return m_Sequences; }
  29. }
  30. public static SequenceHierarchy CreateInstance()
  31. {
  32. var hierarchy = ScriptableObject.CreateInstance<SequenceHierarchy>();
  33. hierarchy.hideFlags = HideFlags.HideAndDontSave;
  34. return hierarchy;
  35. }
  36. public void Init(WindowState owner)
  37. {
  38. m_WindowState = owner;
  39. }
  40. // This is called when performing Undo operations.
  41. // It needs to be called here since some operations are not
  42. // allowed (EditorUtility.InstanceIDToObject, for example)
  43. // during the ISerializationCallbackReceiver methods.
  44. void OnValidate()
  45. {
  46. if (m_SerializedPath == null || m_WindowState == null || m_WindowState.GetWindow() == null)
  47. return;
  48. m_WindowState.SetCurrentSequencePath(m_SerializedPath, true);
  49. }
  50. public void Add(TimelineAsset asset, PlayableDirector director, TimelineClip hostClip)
  51. {
  52. if (hostClip == null)
  53. AddToCurrentUndoGroup(this); // Merge with selection undo
  54. else
  55. TimelineUndo.PushUndo(this, "Edit Sub-Timeline");
  56. Add_Internal(asset, director, hostClip);
  57. UpdateSerializedPath();
  58. }
  59. public void Remove()
  60. {
  61. if (m_Sequences.Count == 0) return;
  62. TimelineUndo.PushUndo(this, "Go to Sub-Timeline");
  63. Remove_Internal();
  64. UpdateSerializedPath();
  65. }
  66. public ISequenceState GetStateAtIndex(int index)
  67. {
  68. return m_Sequences[index];
  69. }
  70. public void RemoveUntilCount(int expectedCount)
  71. {
  72. if (expectedCount < 0 || m_Sequences.Count <= expectedCount) return;
  73. TimelineUndo.PushUndo(this, "Go to Sub-Timeline");
  74. RemoveUntilCount_Internal(expectedCount);
  75. UpdateSerializedPath();
  76. }
  77. public void Clear()
  78. {
  79. if (m_Sequences.Count == 0) return;
  80. AddToCurrentUndoGroup(this);
  81. Clear_Internal();
  82. UpdateSerializedPath();
  83. }
  84. public SequencePath ToSequencePath()
  85. {
  86. var path = new SequencePath();
  87. if (m_Sequences.Count == 0)
  88. return path;
  89. var rootSequence = m_Sequences[0];
  90. var root = 0;
  91. if (rootSequence.director != null && rootSequence.director.gameObject != null)
  92. root = rootSequence.director.gameObject.GetInstanceID();
  93. else if (rootSequence.asset != null)
  94. root = rootSequence.asset.GetInstanceID();
  95. path.SetSelectionRoot(root);
  96. var resolver = rootSequence.director;
  97. if (m_Sequences.Count > 1)
  98. {
  99. for (int i = 1, n = m_Sequences.Count; i < n; ++i)
  100. {
  101. path.AddSubSequence(m_Sequences[i], resolver);
  102. resolver = m_Sequences[i].director;
  103. }
  104. }
  105. return path;
  106. }
  107. public bool NeedsUpdate(SequencePath path, bool forceRebuild)
  108. {
  109. return forceRebuild || !SequencePath.AreEqual(m_SerializedPath, path);
  110. }
  111. public void FromSequencePath(SequencePath path, bool forceRebuild)
  112. {
  113. if (!NeedsUpdate(path, forceRebuild))
  114. return;
  115. Clear_Internal();
  116. var rootObject = EditorUtility.InstanceIDToObject(path.selectionRoot);
  117. if (rootObject == null)
  118. {
  119. UpdateSerializedPath();
  120. return;
  121. }
  122. var candidateAsset = rootObject as TimelineAsset;
  123. if (candidateAsset != null)
  124. {
  125. Add_Internal(candidateAsset, null, null);
  126. UpdateSerializedPath();
  127. return;
  128. }
  129. var candidateGameObject = rootObject as GameObject;
  130. if (candidateGameObject == null)
  131. {
  132. UpdateSerializedPath();
  133. return;
  134. }
  135. var director = TimelineUtility.GetDirectorComponentForGameObject(candidateGameObject);
  136. var asset = TimelineUtility.GetTimelineAssetForDirectorComponent(director);
  137. Add_Internal(asset, director, null);
  138. if (!path.subElements.Any())
  139. {
  140. UpdateSerializedPath();
  141. return;
  142. }
  143. List<SequenceBuildingBlock> buildingBlocks;
  144. if (ValidateSubElements(path.subElements, director, out buildingBlocks))
  145. {
  146. foreach (var buildingBlock in buildingBlocks)
  147. Add_Internal(buildingBlock.asset, buildingBlock.director, buildingBlock.hostClip);
  148. }
  149. UpdateSerializedPath();
  150. }
  151. void Add_Internal(TimelineAsset asset, PlayableDirector director, TimelineClip hostClip)
  152. {
  153. if (hostClip == null)
  154. Clear_Internal();
  155. var parent = m_Sequences.Count > 0 ? editSequence : null;
  156. m_Sequences.Add(new SequenceState(m_WindowState, asset, director, hostClip, (SequenceState)parent));
  157. }
  158. void Remove_Internal()
  159. {
  160. m_Sequences.Last().Dispose();
  161. m_Sequences.RemoveAt(m_Sequences.Count - 1);
  162. }
  163. void RemoveUntilCount_Internal(int expectedCount)
  164. {
  165. while (m_Sequences.Count > expectedCount)
  166. {
  167. Remove_Internal();
  168. }
  169. }
  170. void Clear_Internal()
  171. {
  172. RemoveUntilCount_Internal(0);
  173. }
  174. void UpdateSerializedPath()
  175. {
  176. m_SerializedPath = ToSequencePath();
  177. }
  178. static bool ValidateSubElements(List<SequencePathSubElement> subElements, PlayableDirector director, out List<SequenceBuildingBlock> buildingBlocks)
  179. {
  180. buildingBlocks = new List<SequenceBuildingBlock>(subElements.Count);
  181. var currentDirector = director;
  182. foreach (var element in subElements)
  183. {
  184. var timeline = currentDirector.playableAsset as TimelineAsset;
  185. if (timeline == null)
  186. return false;
  187. if (timeline.trackObjects == null)
  188. return false;
  189. var track = timeline.GetOutputTracks().FirstOrDefault(t => t.GetInstanceID() == element.trackInstanceID);
  190. if (track == null)
  191. return false;
  192. if (track.Hash() != element.trackHash)
  193. return false;
  194. if (track.clips == null)
  195. return false;
  196. if (track.clips.Length <= element.clipIndex)
  197. return false;
  198. var clip = track.clips[element.clipIndex];
  199. if (clip == null)
  200. return false;
  201. if (clip.Hash() != element.clipHash)
  202. return false;
  203. var candidateDirectors = TimelineUtility.GetSubTimelines(clip, director);
  204. if (element.subDirectorIndex < 0 || element.subDirectorIndex >= candidateDirectors.Count)
  205. return false;
  206. var candidateDirector = candidateDirectors[element.subDirectorIndex];
  207. if (candidateDirector == null || !(candidateDirector.playableAsset is TimelineAsset))
  208. return false;
  209. currentDirector = candidateDirector;
  210. buildingBlocks.Add(
  211. new SequenceBuildingBlock
  212. {
  213. asset = currentDirector.playableAsset as TimelineAsset,
  214. director = currentDirector,
  215. hostClip = clip
  216. });
  217. }
  218. return true;
  219. }
  220. struct SequenceBuildingBlock
  221. {
  222. public TimelineAsset asset;
  223. public PlayableDirector director;
  224. public TimelineClip hostClip;
  225. }
  226. static void AddToCurrentUndoGroup(Object target)
  227. {
  228. if (target == null) return;
  229. var group = Undo.GetCurrentGroup();
  230. var groupName = Undo.GetCurrentGroupName();
  231. EditorUtility.SetDirty(target);
  232. Undo.RegisterCompleteObjectUndo(target, groupName);
  233. Undo.CollapseUndoOperations(group);
  234. }
  235. }
  236. }