A game about forced loneliness, made by TACStudios
1using System;
2using System.Globalization;
3using System.Collections.Generic;
4using System.Linq;
5#if UNITY_2021_2_OR_NEWER
6using UnityEditor.SceneManagement;
7#else
8using UnityEditor.Experimental.SceneManagement;
9#endif
10using UnityEngine;
11using UnityEngine.Timeline;
12using UnityEngine.Playables;
13using Object = UnityEngine.Object;
14
15
16namespace UnityEditor.Timeline
17{
18 static class TimelineUtility
19 {
20 public static void ReorderTracks(List<ScriptableObject> allTracks, List<TrackAsset> tracks, ScriptableObject insertAfterAsset, bool up)
21 {
22 foreach (var i in tracks)
23 allTracks.Remove(i);
24
25 int index = allTracks.IndexOf(insertAfterAsset);
26
27 index = up ? Math.Max(index, 0) : index + 1;
28
29 allTracks.InsertRange(index, tracks.OfType<ScriptableObject>());
30 }
31
32 // Gets the track that holds the game object reference for this track.
33 public static TrackAsset GetSceneReferenceTrack(TrackAsset asset)
34 {
35 if (asset == null)
36 return null;
37 if (asset.isSubTrack)
38 return GetSceneReferenceTrack(asset.parent as TrackAsset);
39 return asset;
40 }
41
42 public static bool TrackHasAnimationCurves(TrackAsset track)
43 {
44 if (track.hasCurves)
45 return true;
46
47 var animTrack = track as AnimationTrack;
48 if (animTrack != null && animTrack.infiniteClip != null && !animTrack.infiniteClip.empty)
49 return true;
50
51 for (int i = 0; i < track.clips.Length; i++)
52 {
53 var curveClip = track.clips[i].curves;
54 var animationClip = track.clips[i].animationClip;
55
56 // prune out clip with zero curves
57 if (curveClip != null && curveClip.empty)
58 curveClip = null;
59
60 if (animationClip != null && animationClip.empty)
61 animationClip = null;
62
63 // prune out clips coming from FBX
64 if (animationClip != null && ((animationClip.hideFlags & HideFlags.NotEditable) != 0))
65 animationClip = null;
66
67 if (!track.clips[i].recordable)
68 animationClip = null;
69
70 if ((curveClip != null) || (animationClip != null))
71 return true;
72 }
73
74 return false;
75 }
76
77 // get the game object reference associated with this
78 public static GameObject GetSceneGameObject(PlayableDirector director, TrackAsset asset)
79 {
80 if (director == null || asset == null)
81 return null;
82
83 asset = GetSceneReferenceTrack(asset);
84
85 var gameObject = director.GetGenericBinding(asset) as GameObject;
86 var component = director.GetGenericBinding(asset) as Component;
87 if (component != null)
88 gameObject = component.gameObject;
89 return gameObject;
90 }
91
92 public static PlayableDirector[] GetDirectorsInSceneUsingAsset(PlayableAsset asset)
93 {
94 const HideFlags hideFlags =
95 HideFlags.HideInHierarchy | HideFlags.HideInInspector |
96 HideFlags.DontSaveInEditor | HideFlags.NotEditable;
97
98 var prefabMode = PrefabStageUtility.GetCurrentPrefabStage();
99
100 var inScene = new List<PlayableDirector>();
101 var allDirectors = Resources.FindObjectsOfTypeAll(typeof(PlayableDirector)) as PlayableDirector[];
102 foreach (var director in allDirectors)
103 {
104 if ((director.hideFlags & hideFlags) != 0)
105 continue;
106
107 string assetPath = AssetDatabase.GetAssetPath(director.transform.root.gameObject);
108 if (!String.IsNullOrEmpty(assetPath))
109 continue;
110
111 if (prefabMode != null && !prefabMode.IsPartOfPrefabContents(director.gameObject))
112 continue;
113
114 if (asset == null || (asset != null && director.playableAsset == asset))
115 {
116 inScene.Add(director);
117 }
118 }
119 return inScene.ToArray();
120 }
121
122 public static PlayableDirector GetDirectorComponentForGameObject(GameObject gameObject)
123 {
124 return gameObject != null ? gameObject.GetComponent<PlayableDirector>() : null;
125 }
126
127 public static TimelineAsset GetTimelineAssetForDirectorComponent(PlayableDirector director)
128 {
129 return director != null ? director.playableAsset as TimelineAsset : null;
130 }
131
132 public static bool IsPrefabOrAsset(Object obj)
133 {
134 return EditorUtility.IsPersistent(obj) || (obj.hideFlags & HideFlags.NotEditable) != 0;
135 }
136
137 // TODO -- Need to add this to SerializedProperty so we can get replicate the accuracy that exists
138 // in the undo system
139 internal static string PropertyToString(SerializedProperty property)
140 {
141 switch (property.propertyType)
142 {
143 case SerializedPropertyType.Integer:
144 return property.intValue.ToString(CultureInfo.InvariantCulture);
145 case SerializedPropertyType.Float:
146 return property.floatValue.ToString(CultureInfo.InvariantCulture);
147 case SerializedPropertyType.String:
148 return property.stringValue;
149 case SerializedPropertyType.Boolean:
150 return property.boolValue ? "1" : "0";
151 case SerializedPropertyType.Color:
152 return property.colorValue.ToString();
153 case SerializedPropertyType.ArraySize:
154 return property.intValue.ToString(CultureInfo.InvariantCulture);
155 case SerializedPropertyType.Enum:
156 return property.intValue.ToString(CultureInfo.InvariantCulture);
157 case SerializedPropertyType.ObjectReference:
158 return string.Empty;
159 case SerializedPropertyType.LayerMask:
160 return property.intValue.ToString(CultureInfo.InvariantCulture);
161 case SerializedPropertyType.Character:
162 return property.intValue.ToString(CultureInfo.InvariantCulture);
163 case SerializedPropertyType.AnimationCurve:
164 return property.animationCurveValue.ToString();
165 case SerializedPropertyType.Gradient:
166 return property.gradientValue.ToString();
167 case SerializedPropertyType.Vector3:
168 return property.vector3Value.ToString();
169 case SerializedPropertyType.Vector4:
170 return property.vector4Value.ToString();
171 case SerializedPropertyType.Vector2:
172 return property.vector2Value.ToString();
173 case SerializedPropertyType.Rect:
174 return property.rectValue.ToString();
175 case SerializedPropertyType.Bounds:
176 return property.boundsValue.ToString();
177 case SerializedPropertyType.Quaternion:
178 return property.quaternionValue.ToString();
179 case SerializedPropertyType.Generic:
180 return string.Empty;
181 default:
182 Debug.LogWarning("Unknown Property Type: " + property.propertyType);
183 return string.Empty;
184 }
185 }
186
187 // Is this a recordable clip on an animation track.
188 internal static bool IsRecordableAnimationClip(TimelineClip clip)
189 {
190 if (!clip.recordable)
191 return false;
192
193 AnimationPlayableAsset asset = clip.asset as AnimationPlayableAsset;
194 if (asset == null)
195 return false;
196
197 return true;
198 }
199
200 public static bool HasCustomEditor(TimelineClip clip)
201 {
202 var editor = CustomTimelineEditorCache.GetClipEditor(clip);
203 return editor != CustomTimelineEditorCache.GetDefaultClipEditor();
204 }
205
206 public static IList<PlayableDirector> GetSubTimelines(TimelineClip clip, IExposedPropertyTable director)
207 {
208 var editor = CustomTimelineEditorCache.GetClipEditor(clip);
209 List<PlayableDirector> directors = new List<PlayableDirector>();
210 try
211 {
212 editor.GetSubTimelines(clip, director as PlayableDirector, directors);
213 }
214 catch (Exception e)
215 {
216 Debug.LogException(e);
217 }
218
219 return directors;
220 }
221
222 public static bool IsAllSubTrackMuted(TrackAsset asset)
223 {
224 if (asset is GroupTrack)
225 return asset.mutedInHierarchy;
226
227 foreach (TrackAsset t in asset.GetChildTracks())
228 {
229 if (!t.muted)
230 return false;
231
232 var childMuted = IsAllSubTrackMuted(t);
233
234 if (!childMuted)
235 return false;
236 }
237 return true;
238 }
239
240 public static bool IsParentMuted(TrackAsset asset)
241 {
242 TrackAsset p = asset.parent as TrackAsset;
243 if (p == null) return false;
244 return p is GroupTrack ? p.mutedInHierarchy : IsParentMuted(p);
245 }
246
247 public static IEnumerable<PlayableDirector> GetAllDirectorsInHierarchy(PlayableDirector mainDirector)
248 {
249 var directors = new HashSet<PlayableDirector> { mainDirector };
250 GetAllDirectorsInHierarchy(mainDirector, directors);
251 return directors;
252 }
253
254 static void GetAllDirectorsInHierarchy(PlayableDirector director, ISet<PlayableDirector> directors)
255 {
256 var timelineAsset = director.playableAsset as TimelineAsset;
257 if (timelineAsset == null)
258 return;
259
260 foreach (var track in timelineAsset.GetOutputTracks())
261 {
262 foreach (var clip in track.clips)
263 {
264 foreach (var subDirector in GetSubTimelines(clip, director))
265 {
266 if (!directors.Contains(subDirector))
267 {
268 directors.Add(subDirector);
269 GetAllDirectorsInHierarchy(subDirector, directors);
270 }
271 }
272 }
273 }
274 }
275
276 public static bool IsLockedFromGroup(TrackAsset asset)
277 {
278 TrackAsset p = asset.parent as TrackAsset;
279 if (p == null) return false;
280 return p is GroupTrack ? p.lockedInHierarchy : IsLockedFromGroup(p);
281 }
282
283 internal static bool IsCurrentSequenceValid()
284 {
285 return TimelineWindow.instance != null
286 && TimelineWindow.instance.state != null
287 && TimelineWindow.instance.state.editSequence != null;
288 }
289
290 public static TimelineAsset CreateAndSaveTimelineAsset(string path)
291 {
292 var newAsset = ScriptableObject.CreateInstance<TimelineAsset>();
293 newAsset.editorSettings.frameRate = TimelineProjectSettings.instance.defaultFrameRate;
294 AssetDatabase.CreateAsset(newAsset, path);
295 return newAsset;
296 }
297 }
298}