A game about forced loneliness, made by TACStudios
at master 397 lines 13 kB view raw
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEngine.Timeline; 5using UnityEngine.Playables; 6 7namespace UnityEditor.Timeline 8{ 9 static class ClipModifier 10 { 11 public static bool Delete(TimelineAsset timeline, TimelineClip clip) 12 { 13 return timeline.DeleteClip(clip); 14 } 15 16 public static bool Tile(IEnumerable<TimelineClip> clips) 17 { 18 if (clips.Count() < 2) 19 return false; 20 21 var clipsByTracks = clips.GroupBy(x => x.GetParentTrack()) 22 .Select(track => new { track.Key, Items = track.OrderBy(c => c.start) }); 23 24 foreach (var track in clipsByTracks) 25 { 26 UndoExtensions.RegisterTrack(track.Key, L10n.Tr("Tile")); 27 } 28 29 foreach (var track in clipsByTracks) 30 { 31 double newStart = track.Items.First().start; 32 foreach (var c in track.Items) 33 { 34 c.start = newStart; 35 newStart += c.duration; 36 } 37 } 38 39 return true; 40 } 41 42 public static bool TrimStart(IEnumerable<TimelineClip> clips, double trimTime) 43 { 44 var result = false; 45 46 foreach (var clip in clips) 47 result |= TrimStart(clip, trimTime); 48 49 return result; 50 } 51 52 public static bool TrimStart(TimelineClip clip, double trimTime) 53 { 54 if (clip.asset == null) 55 return false; 56 57 if (clip.start > trimTime) 58 return false; 59 60 if (clip.end < trimTime) 61 return false; 62 63 UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip Start")); 64 65 // Note: We are NOT using edit modes in this case because we want the same result 66 // regardless of the selected EditMode: split at cursor and delete left part 67 SetStart(clip, trimTime, false); 68 clip.ConformEaseValues(); 69 70 return true; 71 } 72 73 public static bool TrimEnd(IEnumerable<TimelineClip> clips, double trimTime) 74 { 75 var result = false; 76 77 foreach (var clip in clips) 78 result |= TrimEnd(clip, trimTime); 79 80 return result; 81 } 82 83 public static bool TrimEnd(TimelineClip clip, double trimTime) 84 { 85 if (clip.asset == null) 86 return false; 87 88 if (clip.start > trimTime) 89 return false; 90 91 if (clip.end < trimTime) 92 return false; 93 94 UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip End")); 95 TrimClipWithEditMode(clip, TrimEdge.End, trimTime); 96 97 return true; 98 } 99 100 public static bool MatchDuration(IEnumerable<TimelineClip> clips) 101 { 102 double referenceDuration = clips.First().duration; 103 UndoExtensions.RegisterClips(clips, L10n.Tr("Match Clip Duration")); 104 foreach (var clip in clips) 105 { 106 var newEnd = clip.start + referenceDuration; 107 TrimClipWithEditMode(clip, TrimEdge.End, newEnd); 108 } 109 110 return true; 111 } 112 113 public static bool Split(IEnumerable<TimelineClip> clips, double splitTime, PlayableDirector director) 114 { 115 var result = false; 116 117 foreach (var clip in clips) 118 { 119 if (clip.start >= splitTime) 120 continue; 121 122 if (clip.end <= splitTime) 123 continue; 124 125 UndoExtensions.RegisterClip(clip, L10n.Tr("Split Clip")); 126 127 TimelineClip newClip = TimelineHelpers.Clone(clip, director, director, clip.start); 128 129 clip.easeInDuration = 0; 130 newClip.easeOutDuration = 0; 131 132 SetStart(clip, splitTime, false); 133 SetEnd(newClip, splitTime, false); 134 135 // Sort produced by cloning clips on top of each other is unpredictable (it varies between mono runtimes) 136 clip.GetParentTrack().SortClips(); 137 138 result = true; 139 } 140 141 return result; 142 } 143 144 public static void SetStart(TimelineClip clip, double time, bool affectTimeScale) 145 { 146 var supportsClipIn = clip.SupportsClipIn(); 147 var supportsPadding = TimelineUtility.IsRecordableAnimationClip(clip); 148 bool calculateTimeScale = (affectTimeScale && clip.SupportsSpeedMultiplier()); 149 150 // treat empty recordable clips as not supporting clip in (there are no keys to modify) 151 if (supportsPadding && (clip.animationClip == null || clip.animationClip.empty)) 152 { 153 supportsClipIn = false; 154 } 155 156 if (supportsClipIn && !supportsPadding && !calculateTimeScale) 157 { 158 var minStart = clip.FromLocalTimeUnbound(0.0); 159 if (time < minStart) 160 time = minStart; 161 } 162 163 var maxStart = clip.end - TimelineClip.kMinDuration; 164 if (time > maxStart) 165 time = maxStart; 166 167 var timeOffset = time - clip.start; 168 var duration = clip.duration - timeOffset; 169 170 if (calculateTimeScale) 171 { 172 var f = clip.duration / duration; 173 clip.timeScale *= f; 174 } 175 176 177 if (supportsClipIn && !calculateTimeScale) 178 { 179 if (supportsPadding) 180 { 181 double clipInGlobal = clip.clipIn / clip.timeScale; 182 double keyShift = -timeOffset; 183 if (timeOffset < 0) // left drag, eliminate clipIn before shifting 184 { 185 double clipInDelta = Math.Max(-clipInGlobal, timeOffset); 186 keyShift = -Math.Min(0, timeOffset - clipInDelta); 187 clip.clipIn += clipInDelta * clip.timeScale; 188 } 189 else if (timeOffset > 0) // right drag, elimate padding in animation clip before adding clip in 190 { 191 var clipInfo = AnimationClipCurveCache.Instance.GetCurveInfo(clip.animationClip); 192 double keyDelta = clip.FromLocalTimeUnbound(clipInfo.keyTimes.Min()) - clip.start; 193 keyShift = -Math.Max(0, Math.Min(timeOffset, keyDelta)); 194 clip.clipIn += Math.Max(timeOffset + keyShift, 0) * clip.timeScale; 195 } 196 if (keyShift != 0) 197 { 198 AnimationTrackRecorder.ShiftAnimationClip(clip.animationClip, (float)(keyShift * clip.timeScale)); 199 } 200 } 201 else 202 { 203 clip.clipIn += timeOffset * clip.timeScale; 204 } 205 } 206 207 clip.start = time; 208 clip.duration = duration; 209 } 210 211 public static void SetEnd(TimelineClip clip, double time, bool affectTimeScale) 212 { 213 var duration = Math.Max(time - clip.start, TimelineClip.kMinDuration); 214 215 if (affectTimeScale && clip.SupportsSpeedMultiplier()) 216 { 217 var f = clip.duration / duration; 218 clip.timeScale *= f; 219 } 220 221 clip.duration = duration; 222 } 223 224 public static bool ResetEditing(IEnumerable<TimelineClip> clips) 225 { 226 var result = false; 227 228 foreach (var clip in clips) 229 result = result || ResetEditing(clip); 230 231 return result; 232 } 233 234 public static bool ResetEditing(TimelineClip clip) 235 { 236 if (clip.asset == null) 237 return false; 238 239 UndoExtensions.RegisterClip(clip, L10n.Tr("Reset Clip Editing")); 240 241 clip.clipIn = 0.0; 242 243 if (clip.clipAssetDuration < double.MaxValue) 244 { 245 var duration = clip.clipAssetDuration / clip.timeScale; 246 TrimClipWithEditMode(clip, TrimEdge.End, clip.start + duration); 247 } 248 249 return true; 250 } 251 252 public static bool MatchContent(IEnumerable<TimelineClip> clips) 253 { 254 var result = false; 255 256 foreach (var clip in clips) 257 result |= MatchContent(clip); 258 259 return result; 260 } 261 262 public static bool MatchContent(TimelineClip clip) 263 { 264 if (clip.asset == null) 265 return false; 266 267 UndoExtensions.RegisterClip(clip, L10n.Tr("Match Clip Content")); 268 269 var newStartCandidate = clip.start - clip.clipIn / clip.timeScale; 270 var newStart = newStartCandidate < 0.0 ? 0.0 : newStartCandidate; 271 272 TrimClipWithEditMode(clip, TrimEdge.Start, newStart); 273 274 // In case resetting the start was blocked by edit mode or timeline start, we do the best we can 275 clip.clipIn = (clip.start - newStartCandidate) * clip.timeScale; 276 if (clip.clipAssetDuration > 0 && TimelineHelpers.HasUsableAssetDuration(clip)) 277 { 278 var duration = TimelineHelpers.GetLoopDuration(clip); 279 var offset = (clip.clipIn / clip.timeScale) % duration; 280 TrimClipWithEditMode(clip, TrimEdge.End, clip.start - offset + duration); 281 } 282 283 return true; 284 } 285 286 public static void TrimClipWithEditMode(TimelineClip clip, TrimEdge edge, double time) 287 { 288 var clipItem = ItemsUtils.ToItem(clip); 289 EditMode.BeginTrim(clipItem, edge); 290 if (edge == TrimEdge.Start) 291 EditMode.TrimStart(clipItem, time, false); 292 else 293 EditMode.TrimEnd(clipItem, time, false); 294 EditMode.FinishTrim(); 295 } 296 297 public static bool CompleteLastLoop(IEnumerable<TimelineClip> clips) 298 { 299 foreach (var clip in clips) 300 { 301 CompleteLastLoop(clip); 302 } 303 304 return true; 305 } 306 307 public static void CompleteLastLoop(TimelineClip clip) 308 { 309 FixLoops(clip, true); 310 } 311 312 public static bool TrimLastLoop(IEnumerable<TimelineClip> clips) 313 { 314 foreach (var clip in clips) 315 { 316 TrimLastLoop(clip); 317 } 318 319 return true; 320 } 321 322 public static void TrimLastLoop(TimelineClip clip) 323 { 324 FixLoops(clip, false); 325 } 326 327 static void FixLoops(TimelineClip clip, bool completeLastLoop) 328 { 329 if (!TimelineHelpers.HasUsableAssetDuration(clip)) 330 return; 331 332 var loopDuration = TimelineHelpers.GetLoopDuration(clip); 333 var firstLoopDuration = loopDuration - clip.clipIn * (1.0 / clip.timeScale); 334 335 // Making sure we don't trim to zero 336 if (!completeLastLoop && firstLoopDuration > clip.duration) 337 return; 338 339 var numLoops = (clip.duration - firstLoopDuration) / loopDuration; 340 var numCompletedLoops = Math.Floor(numLoops); 341 342 if (!(numCompletedLoops < numLoops)) 343 return; 344 345 if (completeLastLoop) 346 numCompletedLoops += 1; 347 348 var newEnd = clip.start + firstLoopDuration + loopDuration * numCompletedLoops; 349 350 UndoExtensions.RegisterClip(clip, L10n.Tr("Trim Clip Last Loop")); 351 352 TrimClipWithEditMode(clip, TrimEdge.End, newEnd); 353 } 354 355 public static bool DoubleSpeed(IEnumerable<TimelineClip> clips) 356 { 357 foreach (var clip in clips) 358 { 359 if (clip.SupportsSpeedMultiplier()) 360 { 361 UndoExtensions.RegisterClip(clip, L10n.Tr("Double Clip Speed")); 362 clip.timeScale = clip.timeScale * 2.0f; 363 } 364 } 365 366 return true; 367 } 368 369 public static bool HalfSpeed(IEnumerable<TimelineClip> clips) 370 { 371 foreach (var clip in clips) 372 { 373 if (clip.SupportsSpeedMultiplier()) 374 { 375 UndoExtensions.RegisterClip(clip, L10n.Tr("Half Clip Speed")); 376 clip.timeScale = clip.timeScale * 0.5f; 377 } 378 } 379 380 return true; 381 } 382 383 public static bool ResetSpeed(IEnumerable<TimelineClip> clips) 384 { 385 foreach (var clip in clips) 386 { 387 if (clip.timeScale != 1.0) 388 { 389 UndoExtensions.RegisterClip(clip, L10n.Tr("Reset Clip Speed")); 390 clip.timeScale = 1.0; 391 } 392 } 393 394 return true; 395 } 396 } 397}