// Author: Daniele Giardini - http://www.demigiant.com // Created: 2014/07/05 18:31 // // License Copyright (c) Daniele Giardini. // This work is subject to the terms at http://dotween.demigiant.com/license.php using DG.Tweening.Core; using DG.Tweening.Plugins.Core.PathCore; using DG.Tweening.Plugins.Options; using UnityEngine; #pragma warning disable 1573 namespace DG.Tweening { /// /// Methods that extend Tween objects and allow to control or get data from them /// public static class TweenExtensions { // =================================================================================== // TWEENERS + SEQUENCES -------------------------------------------------------------- #region Runtime Operations /// Completes the tween public static void Complete(this Tween t) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } TweenManager.Complete(t); } /// Flips the direction of this tween (backwards if it was going forward or viceversa) public static void Flip(this Tween t) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } TweenManager.Flip(t); } /// Forces the tween to initialize its settings immediately public static void ForceInit(this Tween t) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } TweenManager.ForceInit(t); } /// Send the tween to the given position in time /// Time position to reach /// (if higher than the whole tween duration the tween will simply reach its end) /// If TRUE will play the tween after reaching the given position, otherwise it will pause it public static void Goto(this Tween t, float to, bool andPlay = false) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } if (to < 0) to = 0; TweenManager.Goto(t, to, andPlay); } /// Kills the tween /// If TRUE completes the tween before killing it public static void Kill(this Tween t, bool complete = false) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } if (complete) { TweenManager.Complete(t); if (t.autoKill) return; // Already killed by Complete, so no need to go on } if (TweenManager.isUpdateLoop) { // Just mark it for killing, so the update loop will take care of it t.active = false; } else TweenManager.Despawn(t); } /// Pauses the tween public static T Pause(this T t) where T : Tween { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return t; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return t; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return t; } TweenManager.Pause(t); return t; } /// Plays the tween public static T Play(this T t) where T : Tween { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return t; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return t; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return t; } TweenManager.Play(t); return t; } /// Sets the tween in a backwards direction and plays it public static void PlayBackwards(this Tween t) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } TweenManager.PlayBackwards(t); } /// Sets the tween in a forward direction and plays it public static void PlayForward(this Tween t) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } TweenManager.PlayForward(t); } /// Restarts the tween from the beginning /// If TRUE includes the eventual tween delay, otherwise skips it public static void Restart(this Tween t, bool includeDelay = true) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } TweenManager.Restart(t, includeDelay); } /// Rewinds the tween /// If TRUE includes the eventual tween delay, otherwise skips it public static void Rewind(this Tween t, bool includeDelay = true) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } TweenManager.Rewind(t, includeDelay); } /// Plays the tween if it was paused, pauses it if it was playing public static void TogglePause(this Tween t) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } TweenManager.TogglePause(t); } #region Path Tweens /// Send a path tween to the given waypoint. /// Has no effect if this is not a path tween. /// BEWARE, this is a special utility method: /// the lookAt direction might be wrong after calling this and might need to be set manually /// (because it relies on a smooth path movement and doesn't work well with jumps that encompass dramatic direction changes) /// Waypoint index to reach /// (if higher than the max waypoint index the tween will simply go to the last one) /// If TRUE will play the tween after reaching the given waypoint, otherwise it will pause it public static void GotoWaypoint(this Tween t, int waypointIndex, bool andPlay = false) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return; } TweenerCore pathTween = t as TweenerCore; if (pathTween == null) { if (Debugger.logPriority > 1) Debugger.LogNonPathTween(t); return; } if (waypointIndex < 0) waypointIndex = 0; else if (waypointIndex > pathTween.changeValue.wps.Length - 1) waypointIndex = pathTween.changeValue.wps.Length - 1; // Find path percentage relative to given waypoint float wpLength = 0; // Total length from start to the given waypoint for (int i = 0; i < waypointIndex + 1; i++) wpLength += pathTween.changeValue.wpLengths[i]; float wpPerc = wpLength / pathTween.changeValue.length; // Convert to time taking eventual inverse direction into account bool useInversePosition = t.loopType == LoopType.Yoyo && (t.position < t.duration ? t.completedLoops % 2 != 0 : t.completedLoops % 2 == 0); if (useInversePosition) wpPerc = 1 - wpPerc; float to = (t.isComplete ? t.completedLoops - 1 : t.completedLoops) * t.duration + wpPerc * t.duration; TweenManager.Goto(t, to, andPlay); } #endregion #endregion #region Yield Coroutines /// /// Creates a yield instruction that waits until the tween is killed or complete. /// It can be used inside a coroutine as a yield. /// Example usage:yield return myTween.WaitForCompletion(); /// public static YieldInstruction WaitForCompletion(this Tween t) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return null; } return DOTween.instance.StartCoroutine(DOTween.instance.WaitForCompletion(t)); } /// /// Creates a yield instruction that waits until the tween is killed or rewinded. /// It can be used inside a coroutine as a yield. /// Example usage:yield return myTween.WaitForRewind(); /// public static YieldInstruction WaitForRewind(this Tween t) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return null; } return DOTween.instance.StartCoroutine(DOTween.instance.WaitForRewind(t)); } /// /// Creates a yield instruction that waits until the tween is killed. /// It can be used inside a coroutine as a yield. /// Example usage:yield return myTween.WaitForKill(); /// public static YieldInstruction WaitForKill(this Tween t) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return null; } return DOTween.instance.StartCoroutine(DOTween.instance.WaitForKill(t)); } /// /// Creates a yield instruction that waits until the tween is killed or has gone through the given amount of loops. /// It can be used inside a coroutine as a yield. /// Example usage:yield return myTween.WaitForElapsedLoops(2); /// /// Elapsed loops to wait for public static YieldInstruction WaitForElapsedLoops(this Tween t, int elapsedLoops) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return null; } return DOTween.instance.StartCoroutine(DOTween.instance.WaitForElapsedLoops(t, elapsedLoops)); } /// /// Creates a yield instruction that waits until the tween is killed or has reached the given position (loops included, delays excluded). /// It can be used inside a coroutine as a yield. /// Example usage:yield return myTween.WaitForPosition(2.5f); /// /// Position (loops included, delays excluded) to wait for public static YieldInstruction WaitForPosition(this Tween t, float position) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return null; } return DOTween.instance.StartCoroutine(DOTween.instance.WaitForPosition(t, position)); } /// /// Creates a yield instruction that waits until the tween is killed or started /// (meaning when the tween is set in a playing state the first time, after any eventual delay). /// It can be used inside a coroutine as a yield. /// Example usage:yield return myTween.WaitForStart(); /// public static Coroutine WaitForStart(this Tween t) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return null; } return DOTween.instance.StartCoroutine(DOTween.instance.WaitForStart(t)); } #endregion #region Info Getters /// Returns the total number of loops completed by this tween public static int CompletedLoops(this Tween t) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return 0; } return t.completedLoops; } /// Returns the eventual delay set for this tween public static float Delay(this Tween t) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return 0; } return t.delay; } /// Returns the duration of this tween (delays excluded). /// NOTE: when using settings like SpeedBased, the duration will be recalculated when the tween starts /// If TRUE returns the full duration loops included, /// otherwise the duration of a single loop cycle public static float Duration(this Tween t, bool includeLoops = true) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return 0; } // Calculate duration here instead than getting fullDuration because fullDuration might // not be set yet, since it's set inside DoStartup if (includeLoops) return t.loops == -1 ? Mathf.Infinity : t.duration * t.loops; return t.duration; } /// Returns the elapsed time for this tween (delays exluded) /// If TRUE returns the elapsed time since startup loops included, /// otherwise the elapsed time within the current loop cycle public static float Elapsed(this Tween t, bool includeLoops = true) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return 0; } if (includeLoops) { int loopsToCount = t.position >= t.duration ? t.completedLoops - 1 : t.completedLoops; return (loopsToCount * t.duration) + t.position; } return t.position; } /// Returns the elapsed percentage (0 to 1) of this tween (delays exluded) /// If TRUE returns the elapsed percentage since startup loops included, /// otherwise the elapsed percentage within the current loop cycle public static float ElapsedPercentage(this Tween t, bool includeLoops = true) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return 0; } if (includeLoops) { int loopsToCount = t.position >= t.duration ? t.completedLoops - 1 : t.completedLoops; return ((loopsToCount * t.duration) + t.position) / t.fullDuration; } return t.position / t.duration; } /// Returns FALSE if this tween has been killed. /// BEWARE: if this tween is recyclable it might have been spawned again for another use and thus return TRUE anyway. /// When working with recyclable tweens you should take care to know when a tween has been killed and manually set your references to NULL. /// If you want to be sure your references are set to NULL when a tween is killed you can use the OnKill callback like this: /// .OnKill(()=> myTweenReference = null) public static bool IsActive(this Tween t) { return t.active; } /// Returns TRUE if this tween was reversed and is set to go backwards public static bool IsBackwards(this Tween t) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return false; } return t.isBackwards; } /// Returns TRUE if the tween is complete /// (silently fails and returns FALSE if the tween has been killed) public static bool IsComplete(this Tween t) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return false; } return t.isComplete; } /// Returns TRUE if this tween is playing public static bool IsPlaying(this Tween t) { if (!t.active) { if (Debugger.logPriority > 0) Debugger.LogInvalidTween(t); return false; } return t.isPlaying; } #region Path Tweens /// /// Returns the length of a path (returns -1 if this is not a path tween, if the tween is invalid, or if the path is not yet initialized). /// A path is initialized after its tween starts, or immediately if the tween was created with the Path Editor (DOTween Pro feature). /// You can force a path to be initialized by calling myTween.ForceInit(). /// public static float PathLength(this Tween t) { if (t == null) { if (Debugger.logPriority > 1) Debugger.LogNullTween(t); return -1; } else if (!t.active) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return -1; } else if (t.isSequenced) { if (Debugger.logPriority > 1) Debugger.LogInvalidTween(t); return -1; } TweenerCore pathTween = t as TweenerCore; if (pathTween == null) { if (Debugger.logPriority > 1) Debugger.LogNonPathTween(t); return -1; } else if (!pathTween.endValue.isFinalized) { if (Debugger.logPriority > 1) Debugger.LogWarning("The path is not finalized yet"); return -1; } return pathTween.endValue.length; } #endregion #endregion } }