//---------------------------------------------- // NGUI: Next-Gen UI kit // Copyright © 2011-2014 Tasharen Entertainment //---------------------------------------------- using UnityEngine; using System.Collections; using System.Collections.Generic; /// /// Base class for all tweening operations. /// public abstract class UITweener : MonoBehaviour { /// /// Current tween that triggered the callback function. /// public int exeOrder = 0; static public UITweener current; public enum Method { Linear, EaseIn, EaseOut, EaseInOut, BounceIn, BounceOut, } public enum Style { Once, Loop, PingPong, } /// /// Tweening method used. /// [HideInInspector] public Method method = Method.Linear; /// /// Does it play once? Does it loop? /// [HideInInspector] public Style style = Style.Once; /// /// Optional curve to apply to the tween's time factor value. /// [HideInInspector] public AnimationCurve animationCurve = new AnimationCurve (new Keyframe (0f, 0f, 0f, 1f), new Keyframe (1f, 1f, 1f, 0f)); /// /// Whether the tween will ignore the timescale, making it work while the game is paused. /// [HideInInspector] public bool ignoreTimeScale = true; /// /// How long will the tweener wait before starting the tween? /// [HideInInspector] public float delay = 0f; /// /// How long is the duration of the tween? /// [HideInInspector] public float duration = 1f; /// /// Whether the tweener will use steeper curves for ease in / out style interpolation. /// [HideInInspector] public bool steeperCurves = false; /// /// Used by buttons and tween sequences. Group of '0' means not in a sequence. /// [HideInInspector] public int tweenGroup = 0; /// /// Event delegates called when the animation finishes. /// [HideInInspector] public List onFinished = new List (); // Deprecated functionality, kept for backwards compatibility [HideInInspector] public GameObject eventReceiver; [HideInInspector] public string callWhenFinished; public object onFinishCallback; // add by chenbin 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 AnimationOrTween.Direction direction { get { return mAmountPerDelta < 0f ? AnimationOrTween.Direction.Reverse : AnimationOrTween.Direction.Forward; } } /// /// This function is called by Unity when you add a component. Automatically set the starting values for convenience. /// public void Reset () { if (!mStarted) { SetStartToCurrentValue (); SetEndToCurrentValue (); } } /// /// Update as soon as it's started so that there is no delay. /// protected virtual void Start () { Update (); } /// /// Update the tweening factor and call the virtual update function. /// void Update () { float delta = ignoreTimeScale ? RealTime.deltaTime : Time.deltaTime; float time = ignoreTimeScale ? RealTime.time : Time.time; if (!mStarted) { mStarted = true; 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) { mTemp = onFinished; onFinished = new List (); // Notify the listener delegates EventDelegate.Execute (mTemp, gameObject); // Re-add the previous persistent delegates for (int i = 0; i < mTemp.Count; ++i) EventDelegate.Add (onFinished, mTemp [i]); mTemp = null; } // Deprecated legacy functionality support if (eventReceiver != null && !string.IsNullOrEmpty (callWhenFinished)) eventReceiver.SendMessage (callWhenFinished, this, SendMessageOptions.DontRequireReceiver); Coolape.Utl.doCallback (onFinishCallback, this); // add by chenbin current = null; } else Sample (mFactor, false); } List mTemp = null; /// /// Convenience function -- set a new OnFinished event delegate (here for to be consistent with RemoveOnFinished). /// public void SetOnFinished (EventDelegate.Callback del) { EventDelegate.Set (onFinished, del); } /// /// Convenience function -- set a new OnFinished event delegate (here for to be consistent with RemoveOnFinished). /// public void SetOnFinished (EventDelegate del) { EventDelegate.Set (onFinished, del); } /// /// Convenience function -- add a new OnFinished event delegate (here for to be consistent with RemoveOnFinished). /// public void AddOnFinished (EventDelegate.Callback del) { EventDelegate.Add (onFinished, del); } /// /// Convenience function -- add a new OnFinished event delegate (here for to be consistent with RemoveOnFinished). /// public void AddOnFinished (EventDelegate del) { EventDelegate.Add (onFinished, del); } /// /// Remove an OnFinished delegate. Will work even while iterating through the list when the tweener has finished its operation. /// public void RemoveOnFinished (EventDelegate del) { if (onFinished != null) onFinished.Remove (del); if (mTemp != null) mTemp.Remove (del); } /// /// 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. /// [System.Obsolete("Use PlayForward() instead")] public void Play () { Play (true); } /// /// Play the tween forward. /// public void PlayForward () { Play (true); } /// /// Play the tween in reverse. /// public void PlayReverse () { Play (false); } /// /// Manually activate the tweening process, reversing it if necessary. /// public void Play (bool forward) { mAmountPerDelta = Mathf.Abs (amountPerDelta); if (!forward) mAmountPerDelta = -mAmountPerDelta; enabled = true; Update (); } /// /// Manually reset the tweener's state to the beginning. /// If the tween is playing forward, this means the tween's start. /// If the tween is playing in reverse, this means the tween's end. /// 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 : UITweener { T comp = go.GetComponent (); #if UNITY_FLASH if ((object)comp == null) comp = (T)go.AddComponent(); #else if (comp == null) comp = go.AddComponent (); #endif 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.eventReceiver = null; comp.callWhenFinished = null; comp.enabled = true; 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 () { } }