A game about forced loneliness, made by TACStudios
at master 206 lines 8.2 kB view raw
1using System; 2using System.Linq; 3using UnityEngine; 4using UnityEngine.Timeline; 5 6namespace UnityEditor.Timeline 7{ 8 /// <summary> 9 /// Extension Methods for AnimationTracks that require the Unity Editor, and may require the Timeline containing the Animation Track to be currently loaded in the Timeline Editor Window. 10 /// </summary> 11 public static class AnimationTrackExtensions 12 { 13 /// <summary> 14 /// Determines whether the Timeline window can enable recording mode on an AnimationTrack. 15 /// For a track to support recording, it needs to have a valid scene binding, 16 /// its offset mode should not be Auto and needs to be currently visible in the Timeline Window. 17 /// </summary> 18 /// <param name="track">The track to query.</param> 19 /// <returns>True if recording can start, False otherwise.</returns> 20 public static bool CanStartRecording(this AnimationTrack track) 21 { 22 if (track == null) 23 { 24 throw new ArgumentNullException(nameof(track)); 25 } 26 if (TimelineEditor.state == null) 27 { 28 return false; 29 } 30 31 var director = TimelineEditor.inspectedDirector; 32 var animTrack = TimelineUtility.GetSceneReferenceTrack(track) as AnimationTrack; 33 return animTrack != null && animTrack.trackOffset != TrackOffset.Auto && 34 TimelineEditor.inspectedAsset == animTrack.timelineAsset && 35 director != null && TimelineUtility.GetSceneGameObject(director, animTrack) != null; 36 } 37 38 /// <summary> 39 /// Method that allows querying if a track is current enabled for animation recording. 40 /// </summary> 41 /// <param name="track">The track to query.</param> 42 /// <returns>True if currently recording and False otherwise.</returns> 43 public static bool IsRecording(this AnimationTrack track) 44 { 45 if (track == null) 46 { 47 throw new ArgumentNullException(nameof(track)); 48 } 49 return TimelineEditor.state != null && TimelineEditor.state.IsArmedForRecord(track); 50 } 51 52 /// <summary> 53 /// Method that enables animation recording for an AnimationTrack. 54 /// </summary> 55 /// <param name="track">The AnimationTrack which will be put in recording mode.</param> 56 /// <returns>True if track was put successfully in recording mode, False otherwise. </returns> 57 public static bool StartRecording(this AnimationTrack track) 58 { 59 if (!CanStartRecording(track)) 60 { 61 return false; 62 } 63 TimelineEditor.state.ArmForRecord(track); 64 return true; 65 } 66 67 /// <summary> 68 /// Disables recording mode of an AnimationTrack. 69 /// </summary> 70 /// <param name="track">The AnimationTrack which will be taken out of recording mode.</param> 71 public static void StopRecording(this AnimationTrack track) 72 { 73 if (!IsRecording(track) || TimelineEditor.state == null) 74 { 75 return; 76 } 77 78 TimelineEditor.state.UnarmForRecord(track); 79 } 80 81 internal static void ConvertToClipMode(this AnimationTrack track) 82 { 83 if (!track.CanConvertToClipMode()) 84 return; 85 86 UndoExtensions.RegisterTrack(track, L10n.Tr("Convert To Clip")); 87 88 if (!track.infiniteClip.empty) 89 { 90 var animClip = track.infiniteClip; 91 TimelineUndo.PushUndo(animClip, L10n.Tr("Convert To Clip")); 92 UndoExtensions.RegisterTrack(track, L10n.Tr("Convert To Clip")); 93 var start = AnimationClipCurveCache.Instance.GetCurveInfo(animClip).keyTimes.FirstOrDefault(); 94 animClip.ShiftBySeconds(-start); 95 96 track.infiniteClip = null; 97 var clip = track.CreateClip(animClip); 98 99 clip.start = start; 100 clip.preExtrapolationMode = track.infiniteClipPreExtrapolation; 101 clip.postExtrapolationMode = track.infiniteClipPostExtrapolation; 102 clip.recordable = true; 103 if (Mathf.Abs(animClip.length) < TimelineClip.kMinDuration) 104 { 105 clip.duration = 1; 106 } 107 108 var animationAsset = clip.asset as AnimationPlayableAsset; 109 if (animationAsset) 110 { 111 animationAsset.position = track.infiniteClipOffsetPosition; 112 animationAsset.eulerAngles = track.infiniteClipOffsetEulerAngles; 113 114 // going to / from infinite mode should reset this. infinite mode 115 animationAsset.removeStartOffset = track.infiniteClipRemoveOffset; 116 animationAsset.applyFootIK = track.infiniteClipApplyFootIK; 117 animationAsset.loop = track.infiniteClipLoop; 118 119 track.infiniteClipOffsetPosition = Vector3.zero; 120 track.infiniteClipOffsetEulerAngles = Vector3.zero; 121 } 122 123 track.CalculateExtrapolationTimes(); 124 } 125 126 track.infiniteClip = null; 127 128 EditorUtility.SetDirty(track); 129 } 130 131 internal static void ConvertFromClipMode(this AnimationTrack track, TimelineAsset timeline) 132 { 133 if (!track.CanConvertFromClipMode()) 134 return; 135 136 UndoExtensions.RegisterTrack(track, L10n.Tr("Convert From Clip")); 137 138 var clip = track.clips[0]; 139 var delta = (float)clip.start; 140 track.infiniteClipTimeOffset = 0.0f; 141 track.infiniteClipPreExtrapolation = clip.preExtrapolationMode; 142 track.infiniteClipPostExtrapolation = clip.postExtrapolationMode; 143 144 var animAsset = clip.asset as AnimationPlayableAsset; 145 if (animAsset) 146 { 147 track.infiniteClipOffsetPosition = animAsset.position; 148 track.infiniteClipOffsetEulerAngles = animAsset.eulerAngles; 149 track.infiniteClipRemoveOffset = animAsset.removeStartOffset; 150 track.infiniteClipApplyFootIK = animAsset.applyFootIK; 151 track.infiniteClipLoop = animAsset.loop; 152 } 153 154 // clone it, it may not be in the same asset 155 var animClip = clip.animationClip; 156 157 float scale = (float)clip.timeScale; 158 if (!Mathf.Approximately(scale, 1.0f)) 159 { 160 if (!Mathf.Approximately(scale, 0.0f)) 161 scale = 1.0f / scale; 162 animClip.ScaleTime(scale); 163 } 164 165 TimelineUndo.PushUndo(animClip, L10n.Tr("Convert From Clip")); 166 animClip.ShiftBySeconds(delta); 167 168 // manually delete the clip 169 var asset = clip.asset; 170 clip.asset = null; 171 172 // Remove the clip, remove old assets 173 ClipModifier.Delete(timeline, clip); 174 TimelineUndo.PushDestroyUndo(null, track, asset); 175 176 track.infiniteClip = animClip; 177 178 EditorUtility.SetDirty(track); 179 } 180 181 internal static bool CanConvertToClipMode(this AnimationTrack track) 182 { 183 if (track == null || track.inClipMode) 184 return false; 185 return (track.infiniteClip != null && !track.infiniteClip.empty); 186 } 187 188 // Requirements to go from clip mode 189 // - one clip, recordable, and animation clip belongs to the same asset as the track 190 internal static bool CanConvertFromClipMode(this AnimationTrack track) 191 { 192 if ((track == null) || 193 (!track.inClipMode) || 194 (track.clips.Length != 1) || 195 (track.clips[0].start < 0) || 196 (!track.clips[0].recordable)) 197 return false; 198 199 var asset = track.clips[0].asset as AnimationPlayableAsset; 200 if (asset == null) 201 return false; 202 203 return TimelineHelpers.HaveSameContainerAsset(track, asset.clip); 204 } 205 } 206}