首先抽象出单独一个界面元素运动的属性。
[Serializable] public class TweenPosition { public Transform target; public Transform to; public Ease ease; public bool isQueue; public bool isLocalUsePosition; public float delay; public float duration; } target,界面动画目标元素。to,动画结束位置。ease,用来控制target的tween动画缓动类型。isQueue,元素动画是否进入队列,不进入队列的就会同时执行,队列的会依次等待。这样可以控制界面不同的元素顺序执行。isLocalUsePosition,针对to的位置是局部坐标,还是世界坐标。delay,动画延迟执行时间,可以精细的控制动画效果。duration,动画总时间。然后,一个界面会有多个元素需要动画,所以会有多个TweenPosition可以配置。对于界面整体,需要可以播放和逆序播放动画,用作出场和退场的控制。
看完整的代码封装。
public class UITween : MonoBehaviour { public TweenPosition[] TweenPositions; private Sequence seq = null; private bool isRewind = false; void Start() { this.Play(); } /// /// isAutoKill 完成Tween后是否释放对象,如果true则无法调用PlayBackwards /// public void Play(Action OnTweenComplete = null, bool isAutoKill = false) { if (this.seq == null) { float insertTime = 0f; float appendTime = 0f; this.seq = DOTween.Sequence(); foreach (TweenPosition tp in TweenPositions) { if (tp.target.gameObject.activeSelf == false) { continue; } Tween tween = null; if (tp.isQueue) { appendTime += tp.duration + tp.delay; if (tp.isLocalUsePosition) { tween = DOTween.To(() => tp.target.localPosition, (v) => tp.target.localPosition = v, tp.to.localPosition, tp.duration) .SetEase(tp.ease); } else { tween = DOTween.To(() => tp.target.position, (v) => tp.target.position = v, tp.to.position, tp.duration) .SetEase(tp.ease); } if (tp.delay > 0) { this.seq.AppendInterval(tp.delay); } this.seq.Append(tween); } else { if (tp.duration > insertTime) { insertTime = tp.duration + tp.delay; } if (tp.isLocalUsePosition) { tween = DOTween.To(() => tp.target.localPosition, (v) => tp.target.localPosition = v, tp.to.localPosition, tp.duration) .SetEase(tp.ease); } else { tween = DOTween.To(() => tp.target.position, (v) => tp.target.position = v, tp.to.position, tp.duration) .SetEase(tp.ease); } this.seq.Insert(tp.delay, tween); } } float maxTime; if (insertTime > appendTime) { maxTime = insertTime; } else { maxTime = appendTime; } //--------------------------------------------------------------------------------------------------- this.seq.InsertCallback(maxTime, () => { if (isAutoKill) { this.seq = null; } if (OnTweenComplete != null && this.isRewind == false) { OnTweenComplete(); } }); this.seq.SetAutoKill(isAutoKill); this.seq.Play(); } else { this.seq.PlayForward(); } } public void PlayBackwards(Action OnTweenComplete = null, bool isAutoKill = false) { if (this.seq != null) { if (this.seq.IsPlaying()) { DebugUtils.Log("UITween current playing can not PlayBackwards ?"); return; } this.seq.SetAutoKill(isAutoKill); this.seq.PlayBackwards(); this.isRewind = true; this.seq.OnRewind(() => { this.isRewind = false; if (isAutoKill) { this.seq = null; } if (OnTweenComplete != null) { OnTweenComplete(); } }); } else { DebugUtils.LogError("UITween Play with isAutoKill = true, so can not PlayBackwards !"); } } }