using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.EventSystems; using System; using UnityEngine.Events; public enum Direction { Reverse = -1, Toggle = 0, Forward = 1, } public enum EnableCondition { DoNothing = 0, EnableThenPlay, } public enum DisableCondition { DisableAfterReverse = -1, DoNotDisable = 0, DisableAfterForward = 1, } /// /// Base class for all tweening operations. /// public abstract class Tweener : MonoBehaviour { /// /// Current tween that triggered the callback function. /// static public Tweener current { get; private set; } public enum Method { Linear, EaseIn, EaseOut, EaseInOut, BounceIn, BounceOut, } public enum Style { Once, Loop, PingPong, } /// /// Tweening method used. /// public Method method = Method.Linear; /// /// Does it play once? Does it loop? /// public Style style = Style.Once; /// /// Optional curve to apply to the tween's time factor value. /// public AnimationCurve animationCurve = new AnimationCurve(new Keyframe(0f, 0f, 0f, 1f), new Keyframe(1f, 1f, 1f, 0f)); /// /// How long will the tweener wait before starting the tween? /// public float delay = 0f; public bool noDelayWhenReverse = false; /// /// How long is the duration of the tween? /// public float duration = 1f; /// /// Whether the tweener will use steeper curves for ease in / out style interpolation. /// public bool steeperCurves = false; /// /// Used by buttons and tween sequences. Group of '0' means not in a sequence. /// public int tweenGroup = 0; /// /// Event delegates called when the anim finishes. /// [SerializeField, HideInInspector] UnityEvent m_OnFinished = new UnityEvent(); public Action onFinished; bool mStarted = false; float mStartTime = 0f; float mDuration = 0f; float mAmountPerDelta = 1000f; float mFactor = 0f; /// /// Amount advanced per delta time. /// public float amountPerDelta { get { if (mDuration != duration) { mDuration = duration; mAmountPerDelta = Mathf.Abs((duration > 0f) ? 1f / duration : 1000f); } return mAmountPerDelta; } } /// /// Tween factor, 0-1 range. /// public float tweenFactor { get { return mFactor; } set { mFactor = Mathf.Clamp01(value); } } /// /// Direction that the tween is currently playing in. /// public Direction direction { get { return mAmountPerDelta < 0f ? Direction.Reverse : Direction.Forward; } } void Reset() { if (!mStarted) { SetStartToCurrentValue(); SetEndToCurrentValue(); } } protected virtual void Awake() { onFinished += m_OnFinished.Invoke; } protected virtual void Start() { Update(); } void Update() { float delta = Time.deltaTime; float time = Time.time; if (!mStarted) { mStarted = true; if (noDelayWhenReverse) { if (direction == Direction.Reverse) mStartTime = time; else mStartTime = time + delay; } else { mStartTime = time + delay; } } if (time < mStartTime) return; // Advance the sampling factor mFactor += amountPerDelta * delta; // Loop style simply resets the play factor after it exceeds 1. if (style == Style.Loop) { if (mFactor > 1f) { mFactor -= Mathf.Floor(mFactor); } } else if (style == Style.PingPong) { // Ping-pong style reverses the direction if (mFactor > 1f) { mFactor = 1f - (mFactor - Mathf.Floor(mFactor)); mAmountPerDelta = -mAmountPerDelta; } else if (mFactor < 0f) { mFactor = -mFactor; mFactor -= Mathf.Floor(mFactor); mAmountPerDelta = -mAmountPerDelta; } } // If the factor goes out of range and this is a one-time tweening operation, disable the script if ((style == Style.Once) && (duration == 0f || mFactor > 1f || mFactor < 0f)) { mFactor = Mathf.Clamp01(mFactor); Sample(mFactor, true); // Disable this script unless the function calls above changed something if (duration == 0f || (mFactor == 1f && mAmountPerDelta > 0f || mFactor == 0f && mAmountPerDelta < 0f)) enabled = false; current = this; if (onFinished != null) { onFinished(); } current = null; } else Sample(mFactor, false); } /// /// Mark as not started when finished to enable delay on next play. /// void OnDisable() { mStarted = false; } /// /// Sample the tween at the specified factor. /// public void Sample(float factor, bool isFinished) { // Calculate the sampling value float val = Mathf.Clamp01(factor); if (method == Method.EaseIn) { val = 1f - Mathf.Sin(0.5f * Mathf.PI * (1f - val)); if (steeperCurves) val *= val; } else if (method == Method.EaseOut) { val = Mathf.Sin(0.5f * Mathf.PI * val); if (steeperCurves) { val = 1f - val; val = 1f - val * val; } } else if (method == Method.EaseInOut) { const float pi2 = Mathf.PI * 2f; val = val - Mathf.Sin(val * pi2) / pi2; if (steeperCurves) { val = val * 2f - 1f; float sign = Mathf.Sign(val); val = 1f - Mathf.Abs(val); val = 1f - val * val; val = sign * val * 0.5f + 0.5f; } } else if (method == Method.BounceIn) { val = BounceLogic(val); } else if (method == Method.BounceOut) { val = 1f - BounceLogic(1f - val); } // Call the virtual update OnUpdate((animationCurve != null) ? animationCurve.Evaluate(val) : val, isFinished); } /// /// main Bounce logic to simplify the Sample function /// float BounceLogic(float val) { if (val < 0.363636f) // 0.363636 = (1/ 2.75) { val = 7.5685f * val * val; } else if (val < 0.727272f) // 0.727272 = (2 / 2.75) { val = 7.5625f * (val -= 0.545454f) * val + 0.75f; // 0.545454f = (1.5 / 2.75) } else if (val < 0.909090f) // 0.909090 = (2.5 / 2.75) { val = 7.5625f * (val -= 0.818181f) * val + 0.9375f; // 0.818181 = (2.25 / 2.75) } else { val = 7.5625f * (val -= 0.9545454f) * val + 0.984375f; // 0.9545454 = (2.625 / 2.75) } return val; } /// /// Play the tween forward. /// public void PlayForward() { Play(true); } /// /// Play the tween in reverse. /// public void PlayReverse() { Play(false); } /// /// Stop the tween. /// public void Stop() { enabled = false; } /// /// Manually activate the tweening process, reversing it if necessary. /// public virtual void Play(bool forward) { enabled = true; mAmountPerDelta = Mathf.Abs(amountPerDelta); if (!forward) mAmountPerDelta = -mAmountPerDelta; ResetToBeginning(); Update(); } /// /// Manually reset the tweener's state to the beginning. /// public void ResetToBeginning() { mStarted = false; mFactor = (mAmountPerDelta < 0f) ? 1f : 0f; Sample(mFactor, false); } /// /// Manually start the tweening process, reversing its direction. /// public void Toggle() { if (mFactor > 0f) { mAmountPerDelta = -amountPerDelta; } else { mAmountPerDelta = Mathf.Abs(amountPerDelta); } enabled = true; } /// /// Actual tweening logic should go here. /// abstract protected void OnUpdate(float factor, bool isFinished); /// /// Starts the tweening operation. /// static public T Begin(GameObject go, float duration) where T : Tweener { T comp = go.GetComponent(); if (comp == null) comp = go.AddComponent(); //comp.onFinished = null; comp.mStarted = false; comp.duration = duration; comp.mFactor = 0f; comp.mAmountPerDelta = Mathf.Abs(comp.mAmountPerDelta); comp.style = Style.Once; comp.animationCurve = new AnimationCurve(new Keyframe(0f, 0f, 0f, 1f), new Keyframe(1f, 1f, 1f, 0f)); comp.enabled = true; comp.onFinished = null; comp.m_OnFinished = new UnityEvent(); if (duration <= 0f) { comp.Sample(1f, true); comp.enabled = false; } return comp; } /// /// Set the 'from' value to the current one. /// public virtual void SetStartToCurrentValue() { } /// /// Set the 'to' value to the current one. /// public virtual void SetEndToCurrentValue() { } }