using Cinemachine; using System; using System.Runtime.CompilerServices; using UnityEngine; namespace TweenAnimation { public enum ETweenType { Once, Looped, PingPong } [RequireComponent(typeof(CinemachineSmoothPath)), ExecuteAlways] public class TweenPath : MonoBehaviour { [Serializable] public class TweenTarget { public Transform transform; public float startPos; } [SerializeReference] public TweenTarget[] targets; [SerializeField] public CinemachineSmoothPath path; [SerializeField, HideInInspector] private Transform minStarPosTransform; [SerializeField] public ETweenType TweenType; [Min(0.1f)] public float Duration = 1; [Min(0)] public float Delay = 0; [SerializeField] public AnimationCurve speedCurve = AnimationCurve.Linear(0, 0, 1, 1); private float playTime = 0; private bool isPlaying = false; #if UNITY_EDITOR public bool isEditing = false; #endif private void Awake() { #if UNITY_EDITOR path ??= GetComponent(); #endif } private void Start() { #if UNITY_EDITOR if (!Application.isPlaying) { return; } #endif Play(); } #if UNITY_EDITOR [ContextMenu("Play")] #endif public void Play() { isPlaying = true; playTime = 0; } #if UNITY_EDITOR [ContextMenu("Stop")] #endif public void Stop() { isPlaying = false; playTime = 0; } private void Update() { #if UNITY_EDITOR if (targets == null) { return; } if (!isEditing && !isPlaying && !Application.isPlaying) { float minStartPos = 10; for (int i = 0; i < targets.Length; i++) { var target = targets[i]; EvaluatePosition(target.transform, target.startPos); if (target.startPos < minStartPos) { minStartPos = target.startPos; minStarPosTransform = target.transform; } } } #endif if (!isPlaying) { return; } playTime += Time.smoothDeltaTime; if (playTime < Delay) { return; } for (int i = 0; i < targets.Length; i++) { var target = targets[i]; var t = (playTime - Delay) / Duration + target.startPos; EvaluatePosition(target.transform, t); if (target.transform == minStarPosTransform && TweenType == ETweenType.Once && t >= 1) { Stop(); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void EvaluatePosition(Transform transform, float t) { if (TweenType == ETweenType.Looped) { t = t % 1.0f; } else if (TweenType == ETweenType.PingPong) { if (((int)t & 1) == 0) { t = t % 1.0f; } else { t = 1f - (t % 1.0f); } } var p = speedCurve.Evaluate(t); p = p * (path.MaxPos - path.MinPos); transform.localPosition = path.EvaluateLocalPosition(p); } } }