using System; using UnityEditorInternal; using UnityEngine; using UnityEngine.Timeline; using Object = UnityEngine.Object; namespace UnityEditor.Timeline { class TimelineWindowTimeControl : IAnimationWindowControl { [Serializable] public struct ClipData { public double start; public double duration; public TrackAsset track; } #if UNITY_2021_2_OR_NEWER const AnimationWindowState.SnapMode k_SnapMode = AnimationWindowState.SnapMode.SnapToFrame; #else const AnimationWindowState.SnapMode k_SnapMode = AnimationWindowState.SnapMode.SnapToClipFrame; #endif [SerializeField] ClipData m_ClipData; [SerializeField] TimelineClip m_Clip; [SerializeField] AnimationWindowState m_AnimWindowState; TrackAsset track { get { if (m_Clip != null) { return m_Clip.GetParentTrack(); } return m_ClipData.track; } } static TimelineWindow window { get { return TimelineWindow.instance; } } static WindowState state { get { if (window != null) return window.state; return null; } } void OnStateChange() { if (m_AnimWindowState != null) m_AnimWindowState.Repaint(); } public void Init(AnimationWindowState animState, TimelineClip clip) { m_Clip = clip; m_AnimWindowState = animState; } public void Init(AnimationWindowState animState, ClipData clip) { m_ClipData = clip; m_AnimWindowState = animState; } public override void OnEnable() { if (state != null) state.OnTimeChange += OnStateChange; base.OnEnable(); } public void OnDisable() { if (state != null) state.OnTimeChange -= OnStateChange; } public override AnimationKeyTime time { get { if (state == null) return AnimationKeyTime.Time(0.0f, 0.0f); return AnimationKeyTime.Time(ToAnimationClipTime(state.editSequence.time), (float)state.referenceSequence.frameRate); } } void ChangeTime(double newTime) { if (state != null && state.editSequence.director != null) { // avoid rounding errors var finalTime = ToGlobalTime(newTime); if (TimeUtility.OnFrameBoundary(finalTime, state.referenceSequence.frameRate, TimeUtility.kFrameRateEpsilon)) finalTime = TimeUtility.RoundToFrame(finalTime, state.referenceSequence.frameRate); state.editSequence.time = finalTime; window.Repaint(); } } void ChangeFrame(int frame) { frame = Math.Max(0, frame); if (state != null && state.referenceSequence != null) { double frameTime = TimeUtility.FromFrames(frame, state.referenceSequence.frameRate); ChangeTime(frameTime); } } public override void GoToTime(float newTime) { ChangeTime(newTime); } public override void GoToFrame(int frame) { ChangeFrame(frame); } public override void StartScrubTime() { } public override void EndScrubTime() { } public override void ScrubTime(float newTime) { ChangeTime(newTime); } public override void GoToPreviousFrame() { if (state != null) ChangeFrame(state.editSequence.frame - 1); } public override void GoToNextFrame() { if (state != null) ChangeFrame(state.editSequence.frame + 1); } AnimationWindowCurve[] GetCurves() { var curves = (m_AnimWindowState.showCurveEditor && m_AnimWindowState.activeCurves.Count > 0) ? m_AnimWindowState.activeCurves : m_AnimWindowState.allCurves; return curves.ToArray(); } public override void GoToPreviousKeyframe() { var newTime = AnimationWindowUtility.GetPreviousKeyframeTime(GetCurves(), time.time, m_AnimWindowState.clipFrameRate); GoToTime(m_AnimWindowState.SnapToFrame(newTime, k_SnapMode)); } public override void GoToNextKeyframe() { var newTime = AnimationWindowUtility.GetNextKeyframeTime(GetCurves(), time.time, m_AnimWindowState.clipFrameRate); GoToTime(m_AnimWindowState.SnapToFrame(newTime, k_SnapMode)); } public override void GoToFirstKeyframe() { GoToTime(0); } public override void GoToLastKeyframe() { double animClipTime = 0; if (m_Clip != null) { var curves = m_Clip.curves; var animAsset = m_Clip.asset as AnimationPlayableAsset; if (animAsset != null) { animClipTime = animAsset.clip != null ? animAsset.clip.length : 0; } else if (curves != null) { animClipTime = curves.length; } else { animClipTime = m_Clip.clipAssetDuration; } } else { animClipTime = m_ClipData.duration; } GoToTime((float)animClipTime); } public override bool canPlay { get { return state != null && state.previewMode; } } public override bool playing { get { return state != null && state.playing; } } static void SetPlaybackState(bool playbackState) { if (state == null || playbackState == state.playing) return; state.SetPlaying(playbackState); } public override bool StartPlayback() { SetPlaybackState(true); return state != null && state.playing; } public override void StopPlayback() { SetPlaybackState(false); } public override bool PlaybackUpdate() { return state != null && state.playing; } public override bool canRecord { get { return state != null && state.canRecord; } } public override bool recording { get { return state != null && state.recording; } } public override bool canPreview { get { return false; } } public override bool previewing { get { return true; } } public override bool StartRecording(Object targetObject) { if (!canRecord) return false; if (track != null && state != null && !state.ignorePreview) { state.ArmForRecord(track); return state.recording; } return false; } public override void StopRecording() { if (track != null && state != null && !state.ignorePreview) state.UnarmForRecord(track); } public override void OnSelectionChanged() { } public override void ResampleAnimation() { } public override bool StartPreview() { if (state != null) state.previewMode = true; return state != null && state.previewMode; } public override void StopPreview() { if (state != null) state.previewMode = false; } public override void ProcessCandidates() { } public override void ClearCandidates() { } double ToGlobalTime(double localTime) { if (m_Clip != null) return Math.Max(0, m_Clip.FromLocalTimeUnbound(localTime)); return Math.Max(0, m_ClipData.start + localTime); } float ToAnimationClipTime(double globalTime) { if (m_Clip != null) return (float)m_Clip.ToLocalTimeUnbound(globalTime); return (float)(globalTime - m_ClipData.start); } } }