|
|
using System;using System.Linq;using UnityEngine;using UnityEngine.Timeline;using UnityEngine.Playables;
namespace UnityEditor.Timeline{ static class ClipModifier { public static bool Delete(TimelineAsset timeline, TimelineClip clip) { return timeline.DeleteClip(clip); }
public static bool Tile(TimelineClip[] clips) { if (clips.Length < 2) return false;
var clipsByTracks = clips.GroupBy(x => x.parentTrack) .Select(track => new {track.Key, Items = track.OrderBy(c => c.start)});
foreach (var track in clipsByTracks) { TimelineUndo.PushUndo(track.Key, "Tile"); }
foreach (var track in clipsByTracks) { double newStart = track.Items.First().start; foreach (var c in track.Items) { c.start = newStart; newStart += c.duration; } }
return true; }
public static bool TrimStart(TimelineClip[] clips, double trimTime) { var result = false;
foreach (var clip in clips) result |= TrimStart(clip, trimTime);
return result; }
public static bool TrimStart(TimelineClip clip, double trimTime) { if (clip.asset == null) return false;
if (clip.start > trimTime) return false;
if (clip.end < trimTime) return false;
TimelineUndo.PushUndo(clip.parentTrack, "Trim Clip Start");
// Note: We are NOT using edit modes in this case because we want the same result
// regardless of the selected EditMode: split at cursor and delete left part
SetStart(clip, trimTime);
return true; }
public static bool TrimEnd(TimelineClip[] clips, double trimTime) { var result = false;
foreach (var clip in clips) result |= TrimEnd(clip, trimTime);
return result; }
public static bool TrimEnd(TimelineClip clip, double trimTime) { if (clip.asset == null) return false;
if (clip.start > trimTime) return false;
if (clip.end < trimTime) return false;
TimelineUndo.PushUndo(clip.parentTrack, "Trim Clip End"); TrimClipWithEditMode(clip, TrimEdge.End, trimTime);
return true; }
public static bool MatchDuration(TimelineClip[] clips) { double referenceDuration = clips[0].duration; foreach (var clip in clips) { TimelineUndo.PushUndo(clip.parentTrack, "Match Clip Duration");
var newEnd = clip.start + referenceDuration; TrimClipWithEditMode(clip, TrimEdge.End, newEnd); }
return true; }
public static bool Split(TimelineClip[] clips, double splitTime, PlayableDirector director) { var result = false;
foreach (var clip in clips) { if (clip.start >= splitTime) continue;
if (clip.end <= splitTime) continue;
TimelineUndo.PushUndo(clip.parentTrack, "Split Clip");
TimelineClip newClip = TimelineHelpers.Clone(clip, director, director, clip.start);
SetStart(clip, splitTime); SetEnd(newClip, splitTime, false);
// Sort produced by cloning clips on top of each other is unpredictable (it varies between mono runtimes)
clip.parentTrack.SortClips();
result = true; }
return result; }
public static void SetStart(TimelineClip clip, double time) { var supportsClipIn = clip.SupportsClipIn(); var supportsPadding = TimelineUtility.IsRecordableAnimationClip(clip);
// treat empty recordable clips as not supporting clip in (there are no keys to modify)
if (supportsPadding && (clip.animationClip == null || clip.animationClip.empty)) { supportsClipIn = false; }
if (supportsClipIn && !supportsPadding) { var minStart = clip.FromLocalTimeUnbound(0.0); if (time < minStart) time = minStart; }
var maxStart = clip.end - TimelineClip.kMinDuration; if (time > maxStart) time = maxStart;
var timeOffset = time - clip.start; var duration = clip.duration - timeOffset;
if (supportsClipIn) { if (supportsPadding) { double clipInGlobal = clip.clipIn / clip.timeScale; double keyShift = -timeOffset; if (timeOffset < 0) // left drag, eliminate clipIn before shifting
{ double clipInDelta = Math.Max(-clipInGlobal, timeOffset); keyShift = -Math.Min(0, timeOffset - clipInDelta); clip.clipIn += clipInDelta * clip.timeScale; } else if (timeOffset > 0) // right drag, elimate padding in animation clip before adding clip in
{ var clipInfo = AnimationClipCurveCache.Instance.GetCurveInfo(clip.animationClip); double keyDelta = clip.FromLocalTimeUnbound(clipInfo.keyTimes.Min()) - clip.start; keyShift = -Math.Max(0, Math.Min(timeOffset, keyDelta)); clip.clipIn += Math.Max(timeOffset + keyShift, 0) * clip.timeScale; } if (keyShift != 0) { AnimationTrackRecorder.ShiftAnimationClip(clip.animationClip, (float)(keyShift * clip.timeScale)); } } else { clip.clipIn += timeOffset * clip.timeScale; } }
clip.start = time; clip.duration = duration; }
public static void SetEnd(TimelineClip clip, double time, bool affectTimeScale) { var duration = Math.Max(time - clip.start, TimelineClip.kMinDuration);
if (affectTimeScale && clip.SupportsSpeedMultiplier()) { var f = clip.duration / duration; clip.timeScale *= f; }
clip.duration = duration; }
public static bool ResetEditing(TimelineClip[] clips) { var result = false;
foreach (var clip in clips) result = result || ResetEditing(clip);
return result; }
public static bool ResetEditing(TimelineClip clip) { if (clip.asset == null) return false;
TimelineUndo.PushUndo(clip.parentTrack, "Reset Clip Editing");
clip.clipIn = 0.0;
if (clip.clipAssetDuration < double.MaxValue) { var duration = clip.clipAssetDuration / clip.timeScale; TrimClipWithEditMode(clip, TrimEdge.End, clip.start + duration); }
return true; }
public static bool MatchContent(TimelineClip[] clips) { var result = false;
foreach (var clip in clips) result = result || MatchContent(clip);
return result; }
public static bool MatchContent(TimelineClip clip) { if (clip.asset == null) return false;
TimelineUndo.PushUndo(clip.parentTrack, "Match Clip Content");
var newStartCandidate = clip.start - clip.clipIn / clip.timeScale; var newStart = newStartCandidate < 0.0 ? 0.0 : newStartCandidate;
TrimClipWithEditMode(clip, TrimEdge.Start, newStart);
// In case resetting the start was blocked by edit mode or timeline start, we do the best we can
clip.clipIn = (clip.start - newStartCandidate) * clip.timeScale; if (TimelineHelpers.HasUsableAssetDuration(clip)) { var duration = TimelineHelpers.GetLoopDuration(clip); var offset = (clip.clipIn / clip.timeScale) % duration; TrimClipWithEditMode(clip, TrimEdge.End, clip.start - offset + duration); }
return true; }
public static void TrimClipWithEditMode(TimelineClip clip, TrimEdge edge, double time) { var clipItem = ItemsUtils.ToItem(clip); EditMode.BeginTrim(clipItem, edge); if (edge == TrimEdge.Start) EditMode.TrimStart(clipItem, time); else EditMode.TrimEnd(clipItem, time, false); EditMode.FinishTrim(); }
public static bool CompleteLastLoop(TimelineClip[] clips) { foreach (var clip in clips) { CompleteLastLoop(clip); }
return true; }
public static void CompleteLastLoop(TimelineClip clip) { FixLoops(clip, true); }
public static bool TrimLastLoop(TimelineClip[] clips) { foreach (var clip in clips) { TrimLastLoop(clip); }
return true; }
public static void TrimLastLoop(TimelineClip clip) { FixLoops(clip, false); }
static void FixLoops(TimelineClip clip, bool completeLastLoop) { if (!TimelineHelpers.HasUsableAssetDuration(clip)) return;
var loopDuration = TimelineHelpers.GetLoopDuration(clip); var firstLoopDuration = loopDuration - clip.clipIn * (1.0 / clip.timeScale);
// Making sure we don't trim to zero
if (!completeLastLoop && firstLoopDuration > clip.duration) return;
var numLoops = (clip.duration - firstLoopDuration) / loopDuration; var numCompletedLoops = Math.Floor(numLoops);
if (!(numCompletedLoops < numLoops)) return;
if (completeLastLoop) numCompletedLoops += 1;
var newEnd = clip.start + firstLoopDuration + loopDuration * numCompletedLoops;
TimelineUndo.PushUndo(clip.parentTrack, "Trim Clip Last Loop");
TrimClipWithEditMode(clip, TrimEdge.End, newEnd); }
public static bool DoubleSpeed(TimelineClip[] clips) { foreach (var clip in clips) { if (clip.SupportsSpeedMultiplier()) { TimelineUndo.PushUndo(clip.parentTrack, "Double Clip Speed"); clip.timeScale = clip.timeScale * 2.0f; } }
return true; }
public static bool HalfSpeed(TimelineClip[] clips) { foreach (var clip in clips) { if (clip.SupportsSpeedMultiplier()) { TimelineUndo.PushUndo(clip.parentTrack, "Half Clip Speed"); clip.timeScale = clip.timeScale * 0.5f; } }
return true; }
public static bool ResetSpeed(TimelineClip[] clips) { foreach (var clip in clips) { if (clip.timeScale != 1.0) { TimelineUndo.PushUndo(clip.parentTrack, "Reset Clip Speed"); clip.timeScale = 1.0; } }
return true; } }}
|