From 86285f2317b6f076335ecefa82b8073a6eabceab Mon Sep 17 00:00:00 2001 From: yenmoc Date: Sun, 10 Dec 2023 22:12:18 +0700 Subject: [PATCH] -update: tween 1.1.4 --- .../Editor/PrimeTweenManagerInspector.cs | 30 +++-- .../Editor/TweenSettingsPropDrawer.cs | 6 + .../Editor/TweenShakeSettingsPropDrawer.cs | 1 + .../Modules/PrimeTween/Runtime/Easing.cs | 21 +++- .../PrimeTween/Runtime/Internal/Constants.cs | 2 +- .../Internal/DOTweenAdapter/DOTweenAdapter.cs | 5 +- .../PrimeTween/Runtime/Internal/ITween.cs | 32 ++++++ .../Runtime/Internal/ITween.cs.meta | 3 + .../Runtime/Internal/PrimeTweenManager.cs | 103 ++++++++---------- .../Runtime/Internal/ReusableTween.cs | 59 ++++++---- .../Modules/PrimeTween/Runtime/Sequence.cs | 29 +++-- .../Heart/Modules/PrimeTween/Runtime/Shake.cs | 20 ++-- .../PrimeTween/Runtime/ShakeSettings.cs | 18 +-- .../Heart/Modules/PrimeTween/Runtime/Tween.cs | 18 +-- .../PrimeTween/Runtime/TweenMethods.cs | 14 ++- .../PrimeTween/Runtime/TweenSettings.cs | 22 ++-- .../PrimeTween/Runtime/TweenSettingsT.cs | 16 +-- 17 files changed, 245 insertions(+), 154 deletions(-) create mode 100644 Assets/Heart/Modules/PrimeTween/Runtime/Internal/ITween.cs create mode 100644 Assets/Heart/Modules/PrimeTween/Runtime/Internal/ITween.cs.meta diff --git a/Assets/Heart/Modules/PrimeTween/Editor/PrimeTweenManagerInspector.cs b/Assets/Heart/Modules/PrimeTween/Editor/PrimeTweenManagerInspector.cs index a11ae246f..ef0991c47 100644 --- a/Assets/Heart/Modules/PrimeTween/Editor/PrimeTweenManagerInspector.cs +++ b/Assets/Heart/Modules/PrimeTween/Editor/PrimeTweenManagerInspector.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using PrimeTween; using UnityEditor; using UnityEngine; @@ -5,13 +6,16 @@ [CustomEditor(typeof(PrimeTweenManager))] internal class PrimeTweenManagerInspector : Editor { SerializedProperty tweensProp; + SerializedProperty fixedUpdateTweensProp; GUIContent aliveTweenGuiContent; - const string aliveTweensLabel = "Alive tweens"; + GUIContent fixedUpdateTweenGuiContent; void OnEnable() { tweensProp = serializedObject.FindProperty(nameof(PrimeTweenManager.tweens)); + fixedUpdateTweensProp = serializedObject.FindProperty(nameof(PrimeTweenManager.fixedUpdateTweens)); Assert.IsNotNull(tweensProp); - aliveTweenGuiContent = new GUIContent(aliveTweensLabel); + aliveTweenGuiContent = new GUIContent("Tweens"); + fixedUpdateTweenGuiContent = new GUIContent("Fixed update tweens"); } public override void OnInspectorGUI() { @@ -23,8 +27,8 @@ public override void OnInspectorGUI() { Assert.IsNotNull(manager); GUILayout.BeginHorizontal(); - GUILayout.Label(aliveTweensLabel, EditorStyles.label); - GUILayout.Label(manager.tweens.Count.ToString(), EditorStyles.boldLabel); + GUILayout.Label("Alive tweens", EditorStyles.label); + GUILayout.Label(manager.tweensCount.ToString(), EditorStyles.boldLabel); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); @@ -42,15 +46,19 @@ public override void OnInspectorGUI() { EditorGUILayout.HelpBox("Use " + Constants.setTweensCapacityMethod + " to set tweens capacity.\n" + "To prevent memory allocations during runtime, choose the value that is greater than the maximum number of simultaneous tweens in your game.", MessageType.None); - if (tweensProp.isExpanded) { - foreach (var tween in manager.tweens) { - if (tween != null && string.IsNullOrEmpty(tween.debugDescription)) { - tween.debugDescription = tween.GetDescription(); + drawList(tweensProp, manager.tweens, aliveTweenGuiContent); + drawList(fixedUpdateTweensProp, manager.fixedUpdateTweens, fixedUpdateTweenGuiContent); + void drawList(SerializedProperty tweensProp, List list, GUIContent guiContent) { + if (tweensProp.isExpanded) { + foreach (var tween in list) { + if (tween != null && string.IsNullOrEmpty(tween.debugDescription)) { + tween.debugDescription = tween.GetDescription(); + } } } - } - using (new EditorGUI.DisabledScope(true)) { - EditorGUILayout.PropertyField(tweensProp, aliveTweenGuiContent); + using (new EditorGUI.DisabledScope(true)) { + EditorGUILayout.PropertyField(tweensProp, guiContent); + } } } } \ No newline at end of file diff --git a/Assets/Heart/Modules/PrimeTween/Editor/TweenSettingsPropDrawer.cs b/Assets/Heart/Modules/PrimeTween/Editor/TweenSettingsPropDrawer.cs index 01c33cf9e..6b0c2c683 100644 --- a/Assets/Heart/Modules/PrimeTween/Editor/TweenSettingsPropDrawer.cs +++ b/Assets/Heart/Modules/PrimeTween/Editor/TweenSettingsPropDrawer.cs @@ -30,6 +30,7 @@ internal static float getPropHeight([NotNull] SerializedProperty property) { count++; // startDelay count++; // endDelay count++; // useUnscaledTime + count++; // useFixedUpdate var result = singleLineHeight * count + standardVerticalSpacing * (count - 1); result += standardVerticalSpacing * 2; // extra spacing return result; @@ -108,6 +109,11 @@ internal static void drawStartDelayTillEnd(ref Rect rect, [NotNull] SerializedPr PropertyField(rect, property); moveToNextLine(ref rect); } + { // useFixedUpdate + property.NextVisible(true); + PropertyField(rect, property); + moveToNextLine(ref rect); + } } internal static int drawCycles(Rect rect, [NotNull] SerializedProperty property) { diff --git a/Assets/Heart/Modules/PrimeTween/Editor/TweenShakeSettingsPropDrawer.cs b/Assets/Heart/Modules/PrimeTween/Editor/TweenShakeSettingsPropDrawer.cs index 6d0cc83af..af01746f2 100644 --- a/Assets/Heart/Modules/PrimeTween/Editor/TweenShakeSettingsPropDrawer.cs +++ b/Assets/Heart/Modules/PrimeTween/Editor/TweenShakeSettingsPropDrawer.cs @@ -30,6 +30,7 @@ public override float GetPropertyHeight([NotNull] SerializedProperty property, G count++; // startDelay count++; // endDelay count++; // useUnscaledTime + count++; // useFixedUpdate var result = singleLineHeight * count + standardVerticalSpacing * (count - 1); result += standardVerticalSpacing * 2; // extra space return result; diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Easing.cs b/Assets/Heart/Modules/PrimeTween/Runtime/Easing.cs index 3b0c63aa8..b5c82f6e5 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/Easing.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Easing.cs @@ -2,6 +2,11 @@ using UnityEngine; namespace PrimeTween { + /// + /// A wrapper struct that encapsulates three available easing methods: standard Ease, AnimationCurve, or Parametric Easing.
+ /// Use static methods to create an Easing struct, for example: Easing.Standard(Ease.OutBounce), Easing.Curve(animationCurve), + /// Easing.Elastic(strength, period), etc. + ///
[PublicAPI] public readonly struct Easing { internal readonly Ease ease; @@ -27,7 +32,8 @@ public readonly struct Easing { } public static implicit operator Easing(Ease ease) => Standard(ease); - /// Standard Robert Penner's easing methods. Or simply use Ease enum instead of this method. + + /// Standard Robert Penner's easing method. Or simply use Ease enum instead. public static Easing Standard(Ease ease) { Assert.AreNotEqual(Ease.Custom, ease); if (ease == Ease.Default) { @@ -37,21 +43,26 @@ public static Easing Standard(Ease ease) { } public static implicit operator Easing([NotNull] AnimationCurve curve) => Curve(curve); - /// AnimationCurve to use an easing function. Or simply use AnimationCurve instead of this method. + + /// AnimationCurve to use as an easing function. Or simply use AnimationCurve instead. public static Easing Curve([NotNull] AnimationCurve curve) => new Easing(Ease.Custom, curve); - #if PRIME_TWEEN_EXPERIMENTAL + /// Customizes the bounce of Ease.OutBounce. public static Easing Bounce(float strength) => new Easing(ParametricEase.Bounce, strength); - /// The first bounce will have the exact measured in meters/angles. + + /// Customizes the exact of the first bounce in meters/angles. public static Easing BounceExact(float amplitude) => new Easing(ParametricEase.BounceExact, amplitude); + + /// Customizes the overshoot of Ease.OutBack. public static Easing Overshoot(float strength) => new Easing(ParametricEase.Overshoot, strength * StandardEasing.backEaseConst); + + /// Customizes the and oscillation of Ease.OutElastic. public static Easing Elastic(float strength, float period = 0.3f) { if (strength < 1) { strength = Mathf.Lerp(0.2f, 1f, strength); // remap strength to limit decayFactor } return new Easing(ParametricEase.Elastic, strength, Mathf.Max(0.1f, period)); } - #endif internal static float Evaluate(float t, [NotNull] ReusableTween tween) { var settings = tween.settings; diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/Constants.cs b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/Constants.cs index 705ece0fa..f7fc755e8 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/Constants.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/Constants.cs @@ -25,7 +25,7 @@ internal static string buildWarningCanBeDisabledMessage(string settingName) { internal const string endDelayTooltip = "Delays the completion of a tween.\n\n" + "For example, can be used to add the delay between cycles.\n\n" + "Or can be used to postpone the execution of the onComplete callback."; - internal const string infiniteTweenInSequenceError = "It's not allowed to have infinite tweens (cycles == -1) in a sequence. If you want the sequence to repeat forever, " + nameof(Sequence.SetRemainingCycles) + "(-1) on the parent sequence instead."; // todoo error instead of exception? + internal const string infiniteTweenInSequenceError = "It's not allowed to have infinite tweens (cycles == -1) in a sequence. If you want the sequence to repeat forever, " + nameof(Sequence.SetRemainingCycles) + "(-1) on the parent sequence instead."; internal const string customTweensDontSupportStartFromCurrentWarning = "Custom tweens don't support the '" + nameof(T.startFromCurrent) + "' because they don't know the current value of animated property.\n" + "This means that the animated value will be changed abruptly if a new tween is started mid-way.\n" + diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/DOTweenAdapter/DOTweenAdapter.cs b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/DOTweenAdapter/DOTweenAdapter.cs index 6ba72a775..e4f9e6fc9 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/DOTweenAdapter/DOTweenAdapter.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/DOTweenAdapter/DOTweenAdapter.cs @@ -1,3 +1,4 @@ +// todo add Sequence.SetEase() // ReSharper disable UnusedMember.Global // ReSharper disable UnusedType.Global // ReSharper disable UnusedParameter.Local @@ -133,6 +134,7 @@ public Sequence AppendCallback([NotNull] Action callback) { } public Sequence SetLoops(int loops) { + // todo add LoopType parameter Assert.IsTrue(isAlive); SetRemainingCycles(loops); return this; @@ -242,7 +244,6 @@ public Tween SetEase(Ease ease, float? amplitude = null, float? period = null) { } static Easing getParametricEasing(Ease ease, float? maybeStrength, float? maybePeriod) { - #if PRIME_TWEEN_EXPERIMENTAL var strength = maybeStrength ?? 1; switch (ease) { case Ease.OutBack: @@ -253,8 +254,6 @@ static Easing getParametricEasing(Ease ease, float? maybeStrength, float? maybeP case Ease.OutElastic: return Easing.Elastic(strength, maybePeriod ?? 0.3f); } - #endif - Debug.LogWarning($"Custom amplitude/period is not supported for {ease} ease. Consider using custom ease curve instead."); return Easing.Standard(ease); } diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ITween.cs b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ITween.cs new file mode 100644 index 000000000..a22872edc --- /dev/null +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ITween.cs @@ -0,0 +1,32 @@ +/*// ReSharper disable UnusedMemberInSuper.Global +using System; +using JetBrains.Annotations; + +namespace PrimeTween { + // ReSharper disable once TypeParameterCanBeVariant + internal interface ITween { + bool isAlive { get; } + void Stop(); + void Complete(); + TResult OnComplete([NotNull] Action onComplete, bool warnIfTargetDestroyed = true); + TResult OnComplete([NotNull] T target, [NotNull] Action onComplete, bool warnIfTargetDestroyed = true) where T : class; + Sequence Group(Tween tween); + Sequence Chain(Tween tween); + Sequence Group(Sequence sequence); + Sequence Chain(Sequence sequence); + void SetRemainingCycles(int cycles); + void SetRemainingCycles(bool stopAtEndValue); + + int cyclesDone { get; } + int cyclesTotal { get; } + bool isPaused { get; set; } + float timeScale { get; set; } + float duration { get; } + float durationTotal { get; } + float elapsedTime { get; set; } + float elapsedTimeTotal { get; set; } + float progress { get; set; } + float progressTotal { get; set; } + // K OnUpdate(T target, Action onUpdate) where T : class; // Sequence doesn't support OnUpdate because its root updates before all children tweens, but it's reasonable that OnUpdate() should be called AFTER all sequence children are updated + } +}*/ \ No newline at end of file diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ITween.cs.meta b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ITween.cs.meta new file mode 100644 index 000000000..3b1f60a05 --- /dev/null +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ITween.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3cd334e55fa448c3a55e6c14ebaf6638 +timeCreated: 1701681398 \ No newline at end of file diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/PrimeTweenManager.cs b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/PrimeTweenManager.cs index 29bf55b30..a16fb515f 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/PrimeTweenManager.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/PrimeTweenManager.cs @@ -29,6 +29,7 @@ internal class PrimeTweenManager : MonoBehaviour { [ItemCanBeNull] #endif [SerializeField] internal List tweens; + [SerializeField] internal List fixedUpdateTweens; [NonSerialized] internal List pool; internal int currentPoolCapacity { get; private set; } internal int maxSimultaneousTweensCount { get; private set; } @@ -36,7 +37,7 @@ internal class PrimeTweenManager : MonoBehaviour { [HideInInspector] internal int lastId; internal Ease defaultEase = Ease.OutQuad; - internal const Ease defaultShakeEase = Ease.OutSine; // todo to OutQuad for performance + internal const Ease defaultShakeEase = Ease.OutQuad; internal bool warnTweenOnDisabledTarget = true; internal bool warnZeroDuration = true; internal bool warnStructBoxingAllocationInCoroutine = true; @@ -62,6 +63,7 @@ static void beforeSceneLoad() { void init() { tweens = new List(0); + fixedUpdateTweens = new List(0); pool = new List(0); const int defaultInitialCapacity = 200; var capacity = customInitialCapacity != -1 ? customInitialCapacity : defaultInitialCapacity; @@ -104,6 +106,7 @@ static void iniOnLoad() { Debug.Log($"All tweens ({count}) were stopped because of 'Recompile And Continue Playing'."); } foundInScene.tweens = new List(); + foundInScene.fixedUpdateTweens = new List(); foundInScene.pool = new List(); foundInScene.SetTweensCapacity(foundInScene.currentPoolCapacity); Instance = foundInScene; @@ -114,24 +117,6 @@ void Reset() { Debug.LogError(manualInstanceCreationIsNotAllowedMessage); DestroyImmediate(this); } - - void OnEnable() => EditorApplication.pauseStateChanged += OnPauseStateChanged; - void OnDisable() => EditorApplication.pauseStateChanged -= OnPauseStateChanged; - void OnPauseStateChanged(PauseState state) => ignoreUnscaledDeltaTime = state == PauseState.Unpaused; - bool ignoreUnscaledDeltaTime; - float curUnscaledDeltaTime; - float getEditorUnscaledDeltaTime() { - if (ignoreUnscaledDeltaTime) { - ignoreUnscaledDeltaTime = false; - var timeScale = Time.timeScale; - if (timeScale != 0) { - curUnscaledDeltaTime = Time.deltaTime / timeScale; - } - } else { - curUnscaledDeltaTime = Time.unscaledDeltaTime; - } - return curUnscaledDeltaTime; - } #endif void Start() { @@ -140,6 +125,8 @@ void Start() { #endif Assert.AreEqual(Instance, this, manualInstanceCreationIsNotAllowedMessage); } + + internal void FixedUpdate() => update(fixedUpdateTweens, Time.fixedDeltaTime, Time.fixedUnscaledDeltaTime, out _); /// /// The most common tween lifecycle: @@ -149,16 +136,8 @@ void Start() { /// all tweens created in previous frames will already be updated before user's script Update() (if user's script execution order is greater than -2000). /// 4. PrimeTweenManager.Update() completes the tween on frame N+(duration*targetFrameRate) given that targetFrameRate is stable. /// - internal void Update() { - float unscaledDeltaTime = - #if UNITY_EDITOR - getEditorUnscaledDeltaTime(); - #else - Time.unscaledDeltaTime; - #endif - update(tweens, Time.deltaTime, unscaledDeltaTime, out processedCount); - } - + internal void Update() => update(tweens, Time.deltaTime, Time.unscaledDeltaTime, out processedCount); + void update(List tweens, float deltaTime, float unscaledDeltaTime, out int processedCount) { if (updateDepth != 0) { throw new Exception("updateDepth != 0"); @@ -235,6 +214,7 @@ void LateUpdate() { void releaseTweenToPool([NotNull] ReusableTween tween) { #if SAFETY_CHECKS checkNotInSequence(tweens); + checkNotInSequence(fixedUpdateTweens); void checkNotInSequence(List list) { foreach (var t in list) { if (t != null) { @@ -352,7 +332,11 @@ Tween addTween_internal([NotNull] ReusableTween tween) { } } // Debug.Log($"[{Time.frameCount}] add tween: {tween.GetDescription()}", tween.unityTarget); - tweens.Add(tween); + if (tween.settings.useFixedUpdate) { + fixedUpdateTweens.Add(tween); + } else { + tweens.Add(tween); + } #if UNITY_ASSERTIONS && !PRIME_TWEEN_DISABLE_ASSERTIONS maxSimultaneousTweensCount = Math.Max(maxSimultaneousTweensCount, tweensCount); if (warnBenchmarkWithAsserts && maxSimultaneousTweensCount > 50000) { @@ -381,39 +365,42 @@ internal static int processAll([CanBeNull] object onTarget, [NotNull] Predicate< internal static bool logCantManipulateError = true; int processAll_internal([CanBeNull] object onTarget, [NotNull] Predicate predicate) { - int numProcessed = 0; - int totalCount = 0; - var count = tweens.Count; // this is not an optimization, OnComplete() may create new tweens - for (var i = 0; i < count; i++) { - var tween = tweens[i]; - if (tween == null) { - continue; - } - totalCount++; - if (onTarget != null) { - if (tween.target != onTarget) { + return processInList(tweens) + processInList(fixedUpdateTweens); + int processInList(List tweens) { + int numProcessed = 0; + int totalCount = 0; + var count = tweens.Count; // this is not an optimization, OnComplete() may create new tweens + for (var i = 0; i < count; i++) { + var tween = tweens[i]; + if (tween == null) { continue; } - if (tween.IsInSequence()) { - // To support stopping sequences by target, I can add new API 'Sequence.Create(object sequenceTarget)'. - // But 'sequenceTarget' is a different concept to tween's target, so I should not mix these two concepts together: - // 'sequenceTarget' serves the purpose of unique 'id', while tween's target is the animated object. - // In my opinion, the benefits of this new API don't outweigh the added complexity. A much more simpler approach is to store the Sequence reference and call sequence.Stop() directly. - Assert.IsFalse(tween.isMainSequenceRoot()); - if (logCantManipulateError) { - Debug.LogError(Constants.cantManipulateNested); + totalCount++; + if (onTarget != null) { + if (tween.target != onTarget) { + continue; + } + if (tween.IsInSequence()) { + // To support stopping sequences by target, I can add new API 'Sequence.Create(object sequenceTarget)'. + // But 'sequenceTarget' is a different concept to tween's target, so I should not mix these two concepts together: + // 'sequenceTarget' serves the purpose of unique 'id', while tween's target is the animated object. + // In my opinion, the benefits of this new API don't outweigh the added complexity. A much more simpler approach is to store the Sequence reference and call sequence.Stop() directly. + Assert.IsFalse(tween.isMainSequenceRoot()); + if (logCantManipulateError) { + Debug.LogError(Constants.cantManipulateNested); + } + continue; } - continue; + } + if (tween._isAlive && predicate(tween)) { + numProcessed++; } } - if (tween._isAlive && predicate(tween)) { - numProcessed++; + if (onTarget == null) { + return totalCount; } + return numProcessed; } - if (onTarget == null) { - numProcessed = totalCount; - } - return numProcessed; } internal void SetTweensCapacity(int capacity) { @@ -424,13 +411,15 @@ internal void SetTweensCapacity(int capacity) { return; } tweens.Capacity = capacity; + fixedUpdateTweens.Capacity = capacity; resizeAndSetCapacity(pool, capacity - runningTweens, capacity); currentPoolCapacity = capacity; Assert.AreEqual(capacity, tweens.Capacity); + Assert.AreEqual(capacity, fixedUpdateTweens.Capacity); Assert.AreEqual(capacity, pool.Capacity); } - internal int tweensCount => tweens.Count; + internal int tweensCount => tweens.Count + fixedUpdateTweens.Count; internal static void resizeAndSetCapacity([NotNull] List list, int newCount, int newCapacity) { Assert.IsTrue(newCapacity >= newCount); diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ReusableTween.cs b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ReusableTween.cs index 9479f7a96..0b9561351 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ReusableTween.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Internal/ReusableTween.cs @@ -32,13 +32,14 @@ internal class ReusableTween { internal bool isAdditive; internal ValueContainer prevVal; [SerializeField] internal TweenSettings settings; - [SerializeField] internal int cyclesDone; + [SerializeField] int cyclesDone; + const int iniCyclesDone = -1; internal object customOnValueChange; internal int intParam; Action onValueChange; - [CanBeNull] internal Action onComplete; + [CanBeNull] Action onComplete; [CanBeNull] object onCompleteCallback; [CanBeNull] object onCompleteTarget; @@ -52,7 +53,7 @@ internal class ReusableTween { internal Func getter; internal bool startFromCurrent; - internal bool stoppedEmergently; + bool stoppedEmergently; internal readonly TweenCoroutineEnumerator coroutineEnumerator = new TweenCoroutineEnumerator(); internal float timeScale = 1f; bool warnIgnoredOnCompleteIfTargetDestroyed = true; @@ -85,7 +86,7 @@ internal void updateSequence(float _elapsedTimeTotal, bool isRestart) { setElapsedTimeTotal(_elapsedTimeTotal, out int cyclesDiff); // update sequence root bool isRestartToBeginning = isRestart && cyclesDiff < 0; - Assert.IsTrue(!isRestartToBeginning || cyclesDone == 0); + Assert.IsTrue(!isRestartToBeginning || cyclesDone == 0 || cyclesDone == iniCyclesDone); if (cyclesDiff != 0 && !isRestartToBeginning) { // print($" sequence cyclesDiff: {cyclesDiff}"); if (isRestart) { @@ -99,8 +100,8 @@ internal void updateSequence(float _elapsedTimeTotal, bool isRestart) { var interpolationFactor = cyclesDelta > 0 ? 1f : 0f; for (int i = 0; i < cyclesDiffAbs; i++) { Assert.IsTrue(!isRestart || i == 0); - if (cyclesDone == settings.cycles) { - // do nothing when moving backward from the last cycle + if (cyclesDone == settings.cycles || cyclesDone == iniCyclesDone) { + // do nothing when moving backward from the last cycle or forward from the -1 cycle cyclesDone += cyclesDelta; continue; } @@ -127,7 +128,7 @@ bool forceChildrenToPos() { cyclesDone += cyclesDelta; var sequenceCycleMode = settings.cycleMode; - if (sequenceCycleMode == CycleMode.Restart && cyclesDone != settings.cycles) { // '&& cyclesDone != 0' check is wrong because we should do the restart when moving from 1 to 0 cyclesDone + if (sequenceCycleMode == CycleMode.Restart && cyclesDone != settings.cycles && cyclesDone != iniCyclesDone) { // '&& cyclesDone != 0' check is wrong because we should do the restart when moving from 1 to 0 cyclesDone if (!restartChildren()) { return; } @@ -142,7 +143,7 @@ bool restartChildren() { return false; } Assert.IsTrue(isForwardCycle || tween.cyclesDone == tween.settings.cycles); - Assert.IsTrue(!isForwardCycle || tween.cyclesDone == 0); + Assert.IsTrue(!isForwardCycle || tween.cyclesDone <= 0); Assert.IsTrue(isForwardCycle || tween.state == State.After); Assert.IsTrue(!isForwardCycle || tween.state == State.Before); } @@ -172,7 +173,10 @@ bool restartChildren() { bool isDone(int cyclesDiff) { Assert.IsTrue(settings.cycles == -1 || cyclesDone <= settings.cycles); - return cyclesDiff > 0 && cyclesDone == settings.cycles; + if (timeScale >= 0f) { + return cyclesDiff > 0 && cyclesDone == settings.cycles; + } + return cyclesDiff < 0 && cyclesDone == iniCyclesDone; } void updateSequenceChild(float encompassingElapsedTime, bool isRestart) { @@ -224,7 +228,7 @@ float calcTFromElapsedTimeTotal(float _elapsedTimeTotal, out int cyclesDiff, out } _elapsedTimeTotal -= waitDelay; // waitDelay is applied before calculating cycles if (_elapsedTimeTotal < 0f) { - cyclesDiff = -cyclesDone; + cyclesDiff = iniCyclesDone - cyclesDone; newState = State.Before; return 0f; } @@ -233,20 +237,20 @@ float calcTFromElapsedTimeTotal(float _elapsedTimeTotal, out int cyclesDiff, out var duration = settings.duration; if (duration == 0f) { if (cyclesTotal == -1) { - cyclesDiff = 1; + cyclesDiff = cyclesDone == iniCyclesDone ? 2 : 1; newState = State.Running; return 1f; } Assert.AreNotEqual(-1, cyclesTotal); if (_elapsedTimeTotal == 0f) { - cyclesDiff = -cyclesDone; + cyclesDiff = iniCyclesDone - cyclesDone; newState = State.Before; return 0f; } var cyclesLeft = cyclesTotal - cyclesDone; Assert.IsTrue(cyclesLeft >= 0); cyclesDiff = cyclesLeft; - newState = State.After; + newState = State.After; return 1f; } Assert.AreNotEqual(0f, cycleDuration); @@ -355,7 +359,10 @@ internal void OnComplete([NotNull] T _target, [NotNull] Action _onComplete }; } - void handleOnCompleteException(Exception e) => Debug.LogError($"Tween's onComplete callback raised exception, tween: {GetDescription()}, exception:\n{e}", unityTarget); + void handleOnCompleteException(Exception e) { + // Design decision: if a tween is inside a Sequence and user's tween.OnComplete() throws an exception, the Sequence should continue + Debug.LogError($"Tween's onComplete callback raised exception, tween: {GetDescription()}, exception:\n{e}", unityTarget); + } internal static bool isDestroyedUnityObject(T obj) where T: class => obj is UnityEngine.Object unityObject && unityObject == null; @@ -394,7 +401,7 @@ internal void Setup([CanBeNull] object _target, ref TweenSettings _settings, [No _isPaused = false; revive(); - cyclesDone = 0; + cyclesDone = iniCyclesDone; _settings.SetValidValues(); settings.CopyFrom(ref _settings); recalculateTotalDuration(); @@ -426,7 +433,7 @@ void ReportOnValueChange(float _easedInterpolationFactor) { if (startFromCurrent) { startFromCurrent = false; startValue = Tween.tryGetStartValueFromOtherShake(this) ?? getter(this); - if (startValue.Vector4Val == endValue.Vector4Val && PrimeTweenManager.Instance.warnEndValueEqualsCurrent) { + if (startValue.Vector4Val == endValue.Vector4Val && PrimeTweenManager.Instance.warnEndValueEqualsCurrent && !shakeData.isAlive) { Debug.LogWarning($"Tween's 'endValue' equals to the current animated value: {startValue.Vector4Val}, tween: {GetDescription()}.\n" + $"{Constants.buildWarningCanBeDisabledMessage(nameof(PrimeTweenConfig.warnEndValueEqualsCurrent))}\n"); } @@ -437,15 +444,14 @@ void ReportOnValueChange(float _easedInterpolationFactor) { if (stoppedEmergently || !_isAlive) { return; } - if (onUpdate != null) { - onUpdate(this); - } + onUpdate?.Invoke(this); } void ReportOnComplete() { // Debug.Log($"[{Time.frameCount}] id {id} ReportOnComplete() {easedInterpolationFactor}"); Assert.IsFalse(startFromCurrent); - Assert.AreEqual(settings.cycles, cyclesDone); + Assert.IsTrue(timeScale < 0 || cyclesDone == settings.cycles); + Assert.IsTrue(timeScale >= 0 || cyclesDone == iniCyclesDone); onComplete?.Invoke(this); } @@ -703,14 +709,14 @@ internal bool trySetPause(bool isPaused) { object onUpdateCallback; Action onUpdate; - internal void SetOnUpdate(T _target, [NotNull] Action _onUpdate) where T : class { + internal void SetOnUpdate(T _target, [NotNull] Action _onUpdate) where T : class { Assert.IsNull(onUpdate, "Only one OnUpdate() is allowed for one tween."); Assert.IsNotNull(_onUpdate, nameof(_onUpdate) + " is null!"); onUpdateTarget = _target; onUpdateCallback = _onUpdate; onUpdate = reusableTween => reusableTween.invokeOnUpdate(); } - + void invokeOnUpdate() where T : class { var callback = onUpdateCallback as Action; Assert.IsNotNull(callback); @@ -759,5 +765,14 @@ internal float getDurationTotal() { Assert.AreNotEqual(0, cyclesTotal); return cycleDuration * cyclesTotal; } + + internal int getCyclesDone() { + int result = cyclesDone; + if (result == iniCyclesDone) { + return 0; + } + Assert.IsTrue(result >= 0); + return result; + } } } \ No newline at end of file diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Sequence.cs b/Assets/Heart/Modules/PrimeTween/Runtime/Sequence.cs index d8dd95502..760f0a4a4 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/Sequence.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Sequence.cs @@ -27,7 +27,8 @@ namespace PrimeTween { #if !ENABLE_SERIALIZATION && UNITY_2020_3_OR_NEWER readonly // duration setter produces error in Unity <= 2019.4.40: error CS1604: Cannot assign to 'this' because it is read-only #endif - partial struct Sequence { + partial struct Sequence/*: ITween*/ { + const int emptySequenceTag = -43; internal #if !ENABLE_SERIALIZATION && UNITY_2020_3_OR_NEWER readonly @@ -95,7 +96,7 @@ bool validateCanAddChildren() { return true; } - public static Sequence Create(int cycles = 1, CycleMode cycleMode = CycleMode.Restart, Ease sequenceEase = Ease.Linear, bool useUnscaledTime = false) { + public static Sequence Create(int cycles = 1, CycleMode cycleMode = CycleMode.Restart, Ease sequenceEase = Ease.Linear, bool useUnscaledTime = false, bool useFixedUpdate = false) { #if UNITY_EDITOR if (Constants.warnNoInstance) { return default; @@ -115,8 +116,9 @@ public static Sequence Create(int cycles = 1, CycleMode cycleMode = CycleMode.Re if (sequenceEase == Ease.Default) { sequenceEase = Ease.Linear; } - var settings = new TweenSettings(0f, sequenceEase, cycles, cycleMode, 0f, 0f, useUnscaledTime); + var settings = new TweenSettings(0f, sequenceEase, cycles, cycleMode, 0f, 0f, useUnscaledTime, useFixedUpdate); tween.Setup(PrimeTweenManager.dummyTarget, ref settings, _ => {}, null, false); + tween.intParam = emptySequenceTag; var root = PrimeTweenManager.addTween(tween); Assert.IsTrue(root.isAlive); return new Sequence(root); @@ -164,7 +166,7 @@ void addLinkedReference(Tween tween) { if (root.tween.next.IsCreated) { last = getLast(); var lastInSelf = getLastInSelfOrRoot(); - Assert.AreNotEqual(root, lastInSelf); + Assert.AreNotEqual(root.id, lastInSelf.id); Assert.IsFalse(lastInSelf.tween.nextSibling.IsCreated); lastInSelf.tween.nextSibling = tween; Assert.IsFalse(tween.tween.prevSibling.IsCreated); @@ -177,6 +179,7 @@ void addLinkedReference(Tween tween) { Assert.IsFalse(tween.tween.prev.IsCreated); last.tween.next = tween; tween.tween.prev = last; + root.tween.intParam = 0; } Tween getLast() { @@ -278,6 +281,9 @@ static void validateChildSettings(Tween child) { if (child.tween.settings.useUnscaledTime) { warnIgnoredChildrenSetting(nameof(TweenSettings.useUnscaledTime)); } + if (child.tween.settings.useFixedUpdate) { + warnIgnoredChildrenSetting(nameof(TweenSettings.useFixedUpdate)); + } void warnIgnoredChildrenSetting(string settingName) { Debug.LogError($"'{settingName}' was ignored after adding tween/sequence to the Sequence. Parent Sequence controls isPaused/timeScale/useUnscaledTime/useFixedUpdate of all its children tweens and sequences.\n"); } @@ -424,8 +430,7 @@ internal SequenceDirectEnumerator(Sequence s, bool isForward) { static bool isSequenceEmpty(Sequence s) { // tests: SequenceNestingDifferentSettings(), TestSequenceEnumeratorWithEmptySequences() - var firstInSequence = s.root.tween.next; - return !firstInSequence.IsCreated || firstInSequence.id == s.root.tween.nextSibling.id; + return s.root.tween.intParam == emptySequenceTag; } public @@ -556,7 +561,7 @@ void validateSequenceEnumerator() { root.tween }; foreach (var t in getAllTweens()) { - // Debug.Log($"----- root {t}"); + // Debug.Log($"----- {t}"); if (t.tween.isSequenceRoot()) { foreach (var ch in t.tween.sequence.getSelfChildren()) { // Debug.Log(ch); @@ -568,5 +573,15 @@ void validateSequenceEnumerator() { Debug.LogError($"{root.id}, duplicates in validateSequenceEnumerator():\n{string.Join("\n", buffer)}"); } } + + public Sequence OnComplete(Action onComplete, bool warnIfTargetDestroyed = true) { + root.OnComplete(onComplete, warnIfTargetDestroyed); + return this; + } + + public Sequence OnComplete(T target, Action onComplete, bool warnIfTargetDestroyed = true) where T : class { + root.OnComplete(target, onComplete, warnIfTargetDestroyed); + return this; + } } } \ No newline at end of file diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Shake.cs b/Assets/Heart/Modules/PrimeTween/Runtime/Shake.cs index 71d1e8dad..bdcbead2b 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/Shake.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Shake.cs @@ -2,6 +2,7 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMethodReturnValue.Global using System; +using System.Collections.Generic; using JetBrains.Annotations; using UnityEngine; using Random = UnityEngine.Random; @@ -92,13 +93,16 @@ static Tween shake(TweenType tweenType, PropType propType, [NotNull] Transform t return null; } var manager = PrimeTweenManager.Instance; - foreach (var tween in manager.tweens) { - if (tween != null && tween != newTween && tween._isAlive && ReferenceEquals(tween.unityTarget, target) && tween.tweenType == newTween.tweenType && !tween.startFromCurrent) { - // Debug.Log($"tryGetStartValueFromOtherShake {tween.GetDescription()}, {tween.startValue}"); - return tween.startValue; + ValueContainer? findIn(List list) { + foreach (var tween in list) { + if (tween != null && tween != newTween && tween._isAlive && ReferenceEquals(tween.unityTarget, target) && tween.tweenType == newTween.tweenType && !tween.startFromCurrent) { + // Debug.Log($"tryGetStartValueFromOtherShake {tween.GetDescription()}, {tween.startValue}"); + return tween.startValue; + } } + return null; } - return null; + return findIn(manager.tweens) ?? findIn(manager.fixedUpdateTweens); } public static Tween ShakeCustom([NotNull] T target, Vector3 startValue, ShakeSettings settings, [NotNull] Action onValueChange) where T : class { @@ -243,9 +247,9 @@ internal Vector3 getNextVal([NotNull] ReusableTween tween) { var interpolationFactor = tween.easedInterpolationFactor; Assert.IsTrue(interpolationFactor <= 1); - int cyclesDiff = tween.cyclesDone - prevCyclesDone; - prevCyclesDone = tween.cyclesDone; - if (interpolationFactor == 0f || (cyclesDiff > 0 && tween.cyclesDone != tween.settings.cycles)) { + int cyclesDiff = tween.getCyclesDone() - prevCyclesDone; + prevCyclesDone = tween.getCyclesDone(); + if (interpolationFactor == 0f || (cyclesDiff > 0 && tween.getCyclesDone() != tween.settings.cycles)) { onCycleComplete(); prevInterpolationFactor = interpolationFactor; } diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/ShakeSettings.cs b/Assets/Heart/Modules/PrimeTween/Runtime/ShakeSettings.cs index 4185e41fa..194021391 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/ShakeSettings.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/ShakeSettings.cs @@ -36,7 +36,7 @@ public struct ShakeSettings { [Range(0f, 1f)] public float asymmetry; /// [Tooltip("Ease between adjacent shake points.\n\n" + - "Default is Ease.OutSine.")] + "Default is Ease.OutQuad.")] public Ease easeBetweenShakes; [Tooltip(Constants.cyclesTooltip)] public int cycles; @@ -46,9 +46,10 @@ public struct ShakeSettings { public float endDelay; [Tooltip(Constants.unscaledTimeTooltip)] public bool useUnscaledTime; + public bool useFixedUpdate; internal bool isPunch { get; private set; } - ShakeSettings(Vector3 strength, float duration, float frequency, Ease? falloffEase, [CanBeNull] AnimationCurve strengthOverTime, Ease easeBetweenShakes, float asymmetryFactor, int cycles, float startDelay, float endDelay, bool useUnscaledTime) { + ShakeSettings(Vector3 strength, float duration, float frequency, Ease? falloffEase, [CanBeNull] AnimationCurve strengthOverTime, Ease easeBetweenShakes, float asymmetryFactor, int cycles, float startDelay, float endDelay, bool useUnscaledTime, bool useFixedUpdate) { this.frequency = frequency; this.strength = strength; this.duration = duration; @@ -68,18 +69,17 @@ public struct ShakeSettings { this.useUnscaledTime = useUnscaledTime; asymmetry = asymmetryFactor; isPunch = false; + this.useFixedUpdate = useFixedUpdate; } - public ShakeSettings(Vector3 strength, float duration = 0.5f, float frequency = defaultFrequency, bool enableFalloff = true, Ease easeBetweenShakes = Ease.Default, float asymmetryFactor = 0f, int cycles = 1, float startDelay = 0, float endDelay = 0, - bool useUnscaledTime = PrimeTweenConfig.defaultUseUnscaledTimeForShakes) + public ShakeSettings(Vector3 strength, float duration = 0.5f, float frequency = defaultFrequency, bool enableFalloff = true, Ease easeBetweenShakes = Ease.Default, float asymmetryFactor = 0f, int cycles = 1, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = PrimeTweenConfig.defaultUseUnscaledTimeForShakes, bool useFixedUpdate = false) // ReSharper disable once RedundantCast - : this(strength, duration, frequency, enableFalloff ? Ease.Default : (Ease?)null, null, easeBetweenShakes, asymmetryFactor, cycles, startDelay, endDelay, useUnscaledTime) {} + : this(strength, duration, frequency, enableFalloff ? Ease.Default : (Ease?)null, null, easeBetweenShakes, asymmetryFactor, cycles, startDelay, endDelay, useUnscaledTime, useFixedUpdate) {} - public ShakeSettings(Vector3 strength, float duration, float frequency, AnimationCurve strengthOverTime, Ease easeBetweenShakes = Ease.Default, float asymmetryFactor = 0f, int cycles = 1, float startDelay = 0, float endDelay = 0, - bool useUnscaledTime = PrimeTweenConfig.defaultUseUnscaledTimeForShakes) - : this(strength, duration, frequency, Ease.Custom, strengthOverTime, easeBetweenShakes, asymmetryFactor, cycles, startDelay, endDelay, useUnscaledTime) { } + public ShakeSettings(Vector3 strength, float duration, float frequency, AnimationCurve strengthOverTime, Ease easeBetweenShakes = Ease.Default, float asymmetryFactor = 0f, int cycles = 1, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = PrimeTweenConfig.defaultUseUnscaledTimeForShakes, bool useFixedUpdate = false) + : this(strength, duration, frequency, Ease.Custom, strengthOverTime, easeBetweenShakes, asymmetryFactor, cycles, startDelay, endDelay, useUnscaledTime, useFixedUpdate) { } - internal TweenSettings tweenSettings => new TweenSettings(duration, Ease.Linear, cycles, CycleMode.Restart, startDelay, endDelay, useUnscaledTime); + internal TweenSettings tweenSettings => new TweenSettings(duration, Ease.Linear, cycles, CycleMode.Restart, startDelay, endDelay, useUnscaledTime, useFixedUpdate); internal #if UNITY_2020_2_OR_NEWER diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/Tween.cs b/Assets/Heart/Modules/PrimeTween/Runtime/Tween.cs index e8fd0396f..657124a57 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/Tween.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/Tween.cs @@ -29,7 +29,7 @@ namespace PrimeTween { #if !ENABLE_SERIALIZATION readonly #endif - partial struct Tween { + partial struct Tween/*: ITween*/ { /// Uniquely identifies the tween. /// Can be observed from the Debug Inspector if PRIME_TWEEN_INSPECTOR_DEBUGGING is defined. Use only for debugging purposes. internal @@ -93,7 +93,7 @@ void setElapsedTime(float value) { /// The total number of cycles. Returns -1 to indicate infinite number cycles. public int cyclesTotal => validateIsAlive() ? tween.settings.cycles : 0; - public int cyclesDone => validateIsAlive() ? tween.cyclesDone : 0; + public int cyclesDone => validateIsAlive() ? tween.getCyclesDone() : 0; /// The duration of one cycle. public float duration { get { @@ -237,7 +237,7 @@ public void SetRemainingCycles(bool stopAtEndValue) { if (tween.settings.cycleMode == CycleMode.Restart || tween.settings.cycleMode == CycleMode.Incremental) { Debug.LogWarning(nameof(SetRemainingCycles) + "(bool " + nameof(stopAtEndValue) + ") is meant to be used with CycleMode.Yoyo or Rewind. Please consider using the overload that accepts int instead."); } - SetRemainingCycles(tween.cyclesDone % 2 == 0 == stopAtEndValue ? 1 : 2); + SetRemainingCycles(tween.getCyclesDone() % 2 == 0 == stopAtEndValue ? 1 : 2); } /// Sets the number of remaining cycles.
@@ -249,6 +249,9 @@ public void SetRemainingCycles(int cycles) { if (!tryManipulate()) { return; } + if (tween.timeScale < 0f) { + Debug.LogError(nameof(SetRemainingCycles) + "() doesn't work with negative " + nameof(tween.timeScale)); + } if (tween.tweenType == TweenType.Delay && tween.HasOnComplete) { Debug.LogError("Applying cycles to Delay will not repeat the OnComplete() callback, but instead will increase the Delay duration.\n" + "OnComplete() is called only once when ALL tween cycles complete. To repeat the OnComplete() callback, please use the Sequence.Create(cycles: numCycles) and put the tween inside a Sequence.\n" + @@ -258,13 +261,13 @@ public void SetRemainingCycles(int cycles) { tween.settings.cycles = -1; } else { TweenSettings.setCyclesTo1If0(ref cycles); - tween.settings.cycles = tween.cyclesDone + cycles; + tween.settings.cycles = tween.getCyclesDone() + cycles; } } /// Adds completion callback. Please consider using to prevent a possible capture of variable into a closure. /// Set to 'false' to disable the error about target's destruction. Please note that the the callback will be silently ignored in the case of target's destruction. More info: https://github.com/KyryloKuzyk/PrimeTween/discussions/4 - public Tween OnComplete([NotNull] Action onComplete, bool warnIfTargetDestroyed = true) { + public Tween OnComplete(Action onComplete, bool warnIfTargetDestroyed = true) { if (validateIsAlive()) { tween.OnComplete(onComplete, warnIfTargetDestroyed); } @@ -279,7 +282,7 @@ public Tween OnComplete([NotNull] Action onComplete, bool warnIfTargetDestroyed /// Tween.PositionX(transform, endValue: 1.5f, duration: 1f) /// .OnComplete(transform, _transform => Destroy(_transform.gameObject)); /// - public Tween OnComplete([NotNull] T target, [NotNull] Action onComplete, bool warnIfTargetDestroyed = true) where T : class { + public Tween OnComplete(T target, Action onComplete, bool warnIfTargetDestroyed = true) where T : class { if (validateIsAlive()) { tween.OnComplete(target, onComplete, warnIfTargetDestroyed); } @@ -305,7 +308,8 @@ public float timeScale { get => tryManipulate() ? tween.timeScale : 1; set { if (tryManipulate()) { - TweenSettings.clampTimescale(ref value); + Assert.IsFalse(float.IsNaN(value)); + Assert.IsFalse(float.IsInfinity(value)); tween.timeScale = value; } } diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/TweenMethods.cs b/Assets/Heart/Modules/PrimeTween/Runtime/TweenMethods.cs index cf629edda..c584d1250 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/TweenMethods.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/TweenMethods.cs @@ -79,6 +79,7 @@ static void forceUpdateManagerIfTargetIsNull([CanBeNull] object onTarget) { var manager = PrimeTweenManager.Instance; if (manager != null) { if (manager.updateDepth == 0) { + manager.FixedUpdate(); manager.Update(); } // Assert.AreEqual(0, manager.tweens.Count); // fails if user's OnComplete() creates new tweens @@ -296,16 +297,23 @@ public static Tween GlobalTimeScale(Single startValue, Single endValue, float du public static Tween GlobalTimeScale(Single endValue, TweenSettings settings) => GlobalTimeScale(new TweenSettings(endValue, settings)); public static Tween GlobalTimeScale(Single startValue, Single endValue, TweenSettings settings) => GlobalTimeScale(new TweenSettings(startValue, endValue, settings)); public static Tween GlobalTimeScale(TweenSettings settings) { + clampTimescale(ref settings.startValue); + clampTimescale(ref settings.endValue); if (!settings.settings.useUnscaledTime) { Debug.LogWarning("Setting " + nameof(TweenSettings.useUnscaledTime) + " to true to animate Time.timeScale correctly."); settings.settings.useUnscaledTime = true; } - return animate(PrimeTweenManager.dummyTarget, ref settings, val => Time.timeScale = val.FloatVal, _ => Time.timeScale.ToContainer()); + return animate(PrimeTweenManager.dummyTarget, ref settings, t => Time.timeScale = t.FloatVal, _ => Time.timeScale.ToContainer()); + + void clampTimescale(ref float value) { + if (value < 0) { + Debug.LogError($"timeScale should be >= 0, but was {value}"); + value = 0; + } + } } public static Tween TweenTimeScale(Tween tween, TweenSettings settings) { - TweenSettings.clampTimescale(ref settings.startValue); - TweenSettings.clampTimescale(ref settings.endValue); if (!tween.tryManipulate()) { return default; } diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/TweenSettings.cs b/Assets/Heart/Modules/PrimeTween/Runtime/TweenSettings.cs index afa9ab45f..2251c3fc9 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/TweenSettings.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/TweenSettings.cs @@ -34,11 +34,12 @@ public struct TweenSettings { public float endDelay; [Tooltip(Constants.unscaledTimeTooltip)] public bool useUnscaledTime; + public bool useFixedUpdate; [NonSerialized] internal ParametricEase parametricEase; [NonSerialized] internal float parametricEaseStrength; [NonSerialized] internal float parametricEasePeriod; - TweenSettings(float duration, Ease ease, Easing? customEasing, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false) { + TweenSettings(float duration, Ease ease, Easing? customEasing, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, bool useFixedUpdate = false) { this.duration = duration; var curve = customEasing?.curve; if (ease == Ease.Custom && customEasing?.parametricEase == ParametricEase.None) { @@ -57,6 +58,7 @@ public struct TweenSettings { parametricEase = customEasing?.parametricEase ?? ParametricEase.None; parametricEaseStrength = customEasing?.parametricEaseStrength ?? float.NaN; parametricEasePeriod = customEasing?.parametricEasePeriod ?? float.NaN; + this.useFixedUpdate = useFixedUpdate; } internal void SetEasing(Easing easing) { @@ -66,12 +68,12 @@ internal void SetEasing(Easing easing) { parametricEasePeriod = easing.parametricEasePeriod; } - public TweenSettings(float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false) - : this(duration, ease, null, cycles, cycleMode, startDelay, endDelay, useUnscaledTime) { + public TweenSettings(float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, bool useFixedUpdate = false) + : this(duration, ease, null, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, useFixedUpdate) { } - public TweenSettings(float duration, Easing easing, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false) - : this(duration, easing.ease, easing, cycles, cycleMode, startDelay, endDelay, useUnscaledTime) { + public TweenSettings(float duration, Easing easing, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, bool useFixedUpdate = false) + : this(duration, easing.ease, easing, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, useFixedUpdate) { } internal static void setCyclesTo1If0(ref int cycles) { @@ -80,13 +82,6 @@ internal static void setCyclesTo1If0(ref int cycles) { } } - internal static void clampTimescale(ref float value) { - if (value < 0) { - Debug.LogError($"timeScale should be >= 0, but was {value}"); - value = 0; - } - } - internal void CopyFrom(ref TweenSettings other) { duration = other.duration; ease = other.ease; @@ -99,6 +94,7 @@ internal void CopyFrom(ref TweenSettings other) { parametricEase = other.parametricEase; parametricEaseStrength = other.parametricEaseStrength; parametricEasePeriod = other.parametricEasePeriod; + useFixedUpdate = other.useFixedUpdate; } internal void SetValidValues() { @@ -170,7 +166,7 @@ string getError() { } } - /// The easing curve of an animation. Different easing curves produce a different animation 'feeling'.
+ /// The standard animation easing types. Different easing curves produce a different animation 'feeling'.
/// Play around with different ease types to choose one that suites you the best. /// You can also provide a custom AnimationCurve as an ease function or parametrize eases with the Easing.Overshoot/Elastic/BounceExact(...) methods.
public enum Ease { Custom = -1, Default = 0, Linear = 1, diff --git a/Assets/Heart/Modules/PrimeTween/Runtime/TweenSettingsT.cs b/Assets/Heart/Modules/PrimeTween/Runtime/TweenSettingsT.cs index 9f6d6ecbd..f29188978 100644 --- a/Assets/Heart/Modules/PrimeTween/Runtime/TweenSettingsT.cs +++ b/Assets/Heart/Modules/PrimeTween/Runtime/TweenSettingsT.cs @@ -39,20 +39,20 @@ public TweenSettings(T startValue, T endValue, TweenSettings settings) { this.settings = settings; } - public TweenSettings(T endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false) - : this(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)) { + public TweenSettings(T endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, bool useFixedUpdate = false) + : this(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, useFixedUpdate)) { } - public TweenSettings(T startValue, T endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false) - : this(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)) { + public TweenSettings(T startValue, T endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, bool useFixedUpdate = false) + : this(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, useFixedUpdate)) { } - public TweenSettings(T endValue, float duration, Easing customEase, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false) - : this(endValue, new TweenSettings(duration, customEase, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)) { + public TweenSettings(T endValue, float duration, Easing customEase, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, bool useFixedUpdate = false) + : this(endValue, new TweenSettings(duration, customEase, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, useFixedUpdate)) { } - public TweenSettings(T startValue, T endValue, float duration, Easing customEase, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false) - : this(startValue, endValue, new TweenSettings(duration, customEase, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)) { + public TweenSettings(T startValue, T endValue, float duration, Easing customEase, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, bool useFixedUpdate = false) + : this(startValue, endValue, new TweenSettings(duration, customEase, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, useFixedUpdate)) { } /// Use this method to choose the direction of an animation based on the '' parameter.