using DG.Tweening; using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; /******************************************************************************* *Create By CG *Function Timeline正播倒播辅助类 *******************************************************************************/ namespace CG.UTility { [RequireComponent(typeof(PlayableDirector))] public class TimelineDirectorCtrl : MonoBehaviour { #region ENUM public enum Status { NULL, PLAYING, PAUSED, STOPPED, } public enum Direction { NULL, FORWARD, BACKWARD } #endregion [SerializeField] private PlayableDirector m_playableDirector; [Range(0f, 1f)] public float PlaySpeed = 1f; /// /// 播放模式 /// public WrapMode WrapMode = WrapMode.Once; /// /// 开始播放事件, 返回时 时间点,和触发时方向 /// public Action OnPlay; /// /// 暂停播放事件, 返回时 时间点,和触发时方向 /// public Action OnPause; /// /// 停止播放事件, 返回时 时间点,和触发时方向 /// public Action OnStop; /// /// 继续播放事件, 返回时 时间点,和触发时方向 /// public Action OnContinue; public Dictionary> OnTriDic = new Dictionary>(); /// /// Timeline长度 /// public double Duration { get; private set; } = -1f; /// /// 当前播放状态(如果用不到可是删除,现在这个字段只是一个状态的记录) /// public Status CurrentPlayStatus { get; private set; } = Status.NULL; /// /// 当前播放方向(如果用不到可是删除,现在这个字段只是一个状态的记录) /// public Direction CurrentPlayDirection { get; private set; } = Direction.NULL; /// /// 当前播放进度 /// public double CurrentTime { get; private set; } = 0d; private Tweener m_timeTween; private void Awake() { m_playableDirector = GetComponent(); m_playableDirector.playOnAwake = false; Duration = m_playableDirector.duration; CurrentPlayStatus = Status.STOPPED; } public void SetDuration() { Duration = m_playableDirector.duration; } /// /// 继续播放 /// public void Continue() { OnContinue?.Invoke(CurrentTime, CurrentPlayDirection); CurrentPlayStatus = Status.PLAYING; CurrentPlayDirection = Direction.FORWARD; m_timeTween.Kill(); RatioExecute(Duration); } /// /// 从暂停时间点正向播放到指定时间点, 应用在倒播中途暂停后切换为正播 /// public void ContinuePlayForwardByPausePoint(double time,Action finishCall) { OnContinue?.Invoke(CurrentTime, CurrentPlayDirection); CurrentPlayStatus = Status.PLAYING; CurrentPlayDirection = Direction.FORWARD; m_timeTween.Kill(); RatioExecute(time); m_timeTween.OnComplete(()=> { finishCall?.Invoke(); }); } /// /// 从暂停时间点反向播放到指定时间点, 应用在正播中途暂停后切换为倒播 /// public void ContinuePlayBackwardByPausePoint(double time, Action finishCall) { OnContinue?.Invoke(CurrentTime, CurrentPlayDirection); CurrentPlayStatus = Status.PLAYING; CurrentPlayDirection = Direction.BACKWARD; m_timeTween.Kill(); RatioExecute(time); m_timeTween.OnComplete(() => { finishCall?.Invoke(); }); } /// /// 从开始播放 /// public void PlayForward() { OnPlay?.Invoke(CurrentTime, CurrentPlayDirection); m_timeTween.Kill(); CurrentTime = 0d; RatioExecute(Duration); } /// /// 从结尾倒放 /// public void PlayBackward() { OnPlay?.Invoke(CurrentTime, CurrentPlayDirection); m_timeTween.Kill(); CurrentTime = Duration; RatioExecute(0); } /// /// 暂停播放 /// public void Pause() { OnPause?.Invoke(CurrentTime, CurrentPlayDirection); m_timeTween.Pause(); } /// /// 暂停在某个时间点 /// public void StopInTime(double time) { m_timeTween.Kill(); CurrentTime = time; m_playableDirector.time = CurrentTime; m_playableDirector.Evaluate(); } /// /// 停止播放 /// public void Stop() { OnStop?.Invoke(CurrentTime, CurrentPlayDirection); m_timeTween.Kill(); CurrentTime = 0d; if (m_playableDirector) { m_playableDirector.time = CurrentTime; m_playableDirector.Evaluate(); } DOTween.KillAll(); } private void RatioExecute(double target) { // 使用DoTween最当前时间进行线性过渡 m_timeTween = DOTween.To(() => CurrentTime, x => CurrentTime = x, target, PlaySpeed).SetSpeedBased().SetEase(Ease.Linear); // 做出限制避免bug CurrentTime = Clamp(CurrentTime, 0d, Duration); m_timeTween.OnUpdate(() => { // 直接取样 if (m_playableDirector != null) { m_playableDirector.time = CurrentTime; foreach (float item in OnTriDic.Keys) { if(Math.Abs(item-CurrentTime)<0.01f) { OnTriDic[item]?.Invoke(item); break; } } m_playableDirector.Evaluate(); } }); m_timeTween.Play(); } /// /// 针对Double的Clamp /// public static double Clamp(double value, double min, double max) { if (value < min) value = min; else if (value > max) value = max; return value; } } }