This commit is contained in:
陈思海 2025-05-15 10:40:15 +08:00
parent de686b7cf8
commit a2d2062689
34 changed files with 1038 additions and 353 deletions

View File

@ -946,6 +946,17 @@ MonoBehaviour:
dependency: 9
placeInGlobalScope: 0
generateOnlyOverloads: 0
- description: Color_VisualElement
methodName: Color
targetType: UnityEngine.UIElements.VisualElement
propertyType: 2
propertyName:
propertyGetter: style.color.value
propertySetter: style.color = val
dotweenMethodName:
dependency: 9
placeInGlobalScope: 0
generateOnlyOverloads: 0
- description: VisualElementBackgroundColor_VisualElement
methodName: VisualElementBackgroundColor
targetType: UnityEngine.UIElements.VisualElement
@ -957,6 +968,28 @@ MonoBehaviour:
dependency: 9
placeInGlobalScope: 0
generateOnlyOverloads: 0
- description: VisualElementOpacity_VisualElement
methodName: VisualElementOpacity
targetType: UnityEngine.UIElements.VisualElement
propertyType: 1
propertyName:
propertyGetter: style.opacity.value
propertySetter: style.opacity = val
dotweenMethodName:
dependency: 9
placeInGlobalScope: 0
generateOnlyOverloads: 0
- description: Alpha_VisualElement
methodName: Alpha
targetType: UnityEngine.UIElements.VisualElement
propertyType: 1
propertyName:
propertyGetter: style.opacity.value
propertySetter: style.opacity = val
dotweenMethodName:
dependency: 9
placeInGlobalScope: 0
generateOnlyOverloads: 0
- description: OffsetMin_RectTransform
methodName: OffsetMin
targetType: UnityEngine.RectTransform
@ -1034,6 +1067,17 @@ MonoBehaviour:
dependency: 10
placeInGlobalScope: 0
generateOnlyOverloads: 1
- description: FontSize_TMP_Text
methodName: TextFontSize
targetType: TMPro.TMP_Text
propertyType: 1
propertyName: fontSize
propertyGetter:
propertySetter:
dotweenMethodName:
dependency: 10
placeInGlobalScope: 0
generateOnlyOverloads: 0
additiveMethodsGenerator:
additiveMethods:
- methodName: PositionAdditive

View File

@ -248,9 +248,14 @@ namespace PrimeTween {
{ // generate enums
foreach (var group in methodsData.GroupBy(_ => _.dependency)) {
foreach (var data in group) {
text += " ";
var enumName = GetTweenTypeEnumName(data);
string enumName = GetTweenTypeEnumName(data);
if (methodDataToEnumName.Values.Contains(enumName)) {
// skip duplicates like VisualElementColor_VisualElement / Color_VisualElement and VisualElementOpacity_VisualElement / Alpha_VisualElement
Debug.Log($"skip duplicate {enumName}");
continue;
}
methodDataToEnumName.Add(data, enumName);
text += " ";
text += enumName;
text += ",\n";
}
@ -281,7 +286,10 @@ internal static class Utils {
}
}
foreach (var data in group) {
utilsText += $" case TweenType.{methodDataToEnumName[data]}:\n";
if (!methodDataToEnumName.TryGetValue(data, out string enumName)) {
continue;
}
utilsText += $" case TweenType.{enumName}:\n";
utilsText += $" return (PropType.{data.propertyType}, typeof({getTypeByName(data.targetType).FullName}));\n";
}
if (shouldWrapInDefine(dependency)) {
@ -348,6 +356,12 @@ internal static class Utils {
static string GetTweenTypeEnumName(MethodGenerationData data) {
string result = "";
var dependency = data.dependency;
if (dependency == Dependency.UI_ELEMENTS_MODULE_INSTALLED) {
if (data.methodName == "Alpha") {
return "VisualElementOpacity";
}
}
if (dependency != Dependency.None) {
result += getMethodPrefix(dependency);
}
@ -355,7 +369,7 @@ internal static class Utils {
result += "VisualElement";
}
result += data.methodName;
if ((data.methodName == "Alpha" || data.methodName == "Color") && data.dependency == Dependency.UNITY_UGUI_INSTALLED) {
if ((data.methodName == "Alpha" || data.methodName == "Color") && dependency == Dependency.UNITY_UGUI_INSTALLED) {
result += getTypeByName(data.targetType).Name;
} else if (data.methodName == "Scale" && data.propertyType == PropType.Float) {
result += "Uniform";
@ -542,7 +556,6 @@ internal static class Utils {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Float);
tween.customOnValueChange = onValueChange;
tween.Setup(PrimeTweenManager.dummyTarget, ref settings.settings, _tween => {
var _onValueChange = _tween.customOnValueChange as Action<Single>;
@ -550,7 +563,8 @@ internal static class Utils {
try {
_onValueChange(val);
} catch (Exception e) {
Assert.LogError($""Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n"", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e);
Assert.LogWarning($""Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n"", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomFloat);
@ -576,7 +590,6 @@ internal static class Utils {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Float);
tween.customOnValueChange = onValueChange;
tween.isAdditive = isAdditive;
tween.Setup(target, ref settings.settings, _tween => {
@ -593,7 +606,8 @@ internal static class Utils {
try {
_onValueChange(_target, val);
} catch (Exception e) {
Assert.LogError($""Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n"", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e, _target as UnityEngine.Object);
Assert.LogWarning($""Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n"", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomFloat);
@ -603,7 +617,6 @@ internal static class Utils {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Float);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -612,7 +625,6 @@ internal static class Utils {
tween.intParam = intParam;
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Float);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}";

View File

@ -70,7 +70,8 @@ namespace PrimeTween {
try {
_onValueChange(val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomFloat);
@ -113,7 +114,8 @@ namespace PrimeTween {
try {
_onValueChange(_target, val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e, _target as UnityEngine.Object);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomFloat);

View File

@ -7,8 +7,10 @@ using UnityEngine;
[CustomEditor(typeof(PrimeTweenManager))]
internal class PrimeTweenManagerInspector : Editor {
SerializedProperty tweensProp;
SerializedProperty lateUpdateTweensProp;
SerializedProperty fixedUpdateTweensProp;
GUIContent aliveTweenGuiContent;
GUIContent lateUpdateTweenGuiContent;
GUIContent fixedUpdateTweenGuiContent;
StringCache tweensCountCache;
StringCache maxSimultaneousTweensCountCache;
@ -16,10 +18,13 @@ internal class PrimeTweenManagerInspector : Editor {
void OnEnable() {
tweensProp = serializedObject.FindProperty(nameof(PrimeTweenManager.tweens));
lateUpdateTweensProp = serializedObject.FindProperty(nameof(PrimeTweenManager.lateUpdateTweens));
fixedUpdateTweensProp = serializedObject.FindProperty(nameof(PrimeTweenManager.fixedUpdateTweens));
Assert.IsNotNull(tweensProp);
Assert.IsNotNull(lateUpdateTweensProp);
Assert.IsNotNull(fixedUpdateTweensProp);
aliveTweenGuiContent = new GUIContent("Tweens");
lateUpdateTweenGuiContent = new GUIContent("Late update tweens");
fixedUpdateTweenGuiContent = new GUIContent("Fixed update tweens");
}
@ -52,6 +57,7 @@ internal class PrimeTweenManagerInspector : Editor {
"To prevent memory allocations during runtime, choose the value that is greater than the maximum number of simultaneous tweens in your game.", MessageType.None);
drawList(tweensProp, manager.tweens, aliveTweenGuiContent);
drawList(lateUpdateTweensProp, manager.lateUpdateTweens, lateUpdateTweenGuiContent);
drawList(fixedUpdateTweensProp, manager.fixedUpdateTweens, fixedUpdateTweenGuiContent);
void drawList(SerializedProperty tweensProp, List<ReusableTween> list, GUIContent guiContent) {
if (tweensProp.isExpanded) {

View File

@ -9,6 +9,10 @@ using AssertionException = UnityEngine.Assertions.AssertionException;
public class EditModeTests {
[Test]
public void TestEditMode() {
Tween.StopAll();
Assert.AreEqual(0, PrimeTweenManager.Instance.tweensCount);
PrimeTweenConfig.warnEndValueEqualsCurrent = false;
PrimeTweenConfig.warnZeroDuration = false;
expectError();
Tween.Custom(0, 1, 1, delegate {});
var go = new GameObject();
@ -21,6 +25,7 @@ public class EditModeTests {
Tween.Delay(0);
expectError();
Tween.CompleteAll();
PrimeTweenConfig.warnEndValueEqualsCurrent = true;
expectError();
Tween.StopAll();
expectError();
@ -38,17 +43,18 @@ public class EditModeTests {
expectError();
Tween.GetTweensCount();
expectError();
Sequence.Create(new Tween());
Sequence.Create(Tween.Delay(0.01f));
TweenSettings.ValidateCustomCurveKeyframes(AnimationCurve.Linear(0, 0, 1, 1));
PrimeTweenConfig.SetTweensCapacity(10);
Assert.Throws<AssertionException>(() => PrimeTweenConfig.defaultEase = Ease.InCirc);
Assert.DoesNotThrow(() => PrimeTweenConfig.defaultEase = Ease.InCirc);
}
Object.DestroyImmediate(go);
void expectError() {
LogAssert.Expect(LogType.Warning, Constants.editModeWarning);
}
LogAssert.NoUnexpectedReceived();
PrimeTweenConfig.warnEndValueEqualsCurrent = true;
PrimeTweenConfig.warnZeroDuration = true;
}
#if PRIME_TWEEN_SAFETY_CHECKS

View File

@ -1,4 +1,3 @@
using System;
using JetBrains.Annotations;
using PrimeTween;
using UnityEditor;
@ -71,7 +70,7 @@ internal class TweenSettingsPropDrawer : PropertyDrawer {
drawStartDelayTillEnd(ref rect, property);
}
internal static void DrawEaseAndCycles(SerializedProperty property, ref Rect rect, bool addSpace = true, bool draw = true) {
internal static void DrawEaseAndCycles(SerializedProperty property, ref Rect rect, bool addSpace = true, bool draw = true, bool allowInfiniteCycles = true) {
{ // ease
property.NextVisible(true);
if (draw) PropertyField(rect, property);
@ -88,7 +87,7 @@ internal class TweenSettingsPropDrawer : PropertyDrawer {
rect.y += standardVerticalSpacing * 2;
}
{ // cycles
var cycles = drawCycles(rect, property, draw);
var cycles = drawCycles(rect, property, draw, allowInfiniteCycles);
moveToNextLine(ref rect);
{
// cycleMode
@ -118,24 +117,54 @@ internal class TweenSettingsPropDrawer : PropertyDrawer {
moveToNextLine(ref rect);
}
{ // useFixedUpdate
property.NextVisible(true);
PropertyField(rect, property);
moveToNextLine(ref rect);
property.Next(false);
bool useFixedUpdateObsolete = property.boolValue;
var useFixedUpdateProp = property.Copy();
property.NextVisible(false);
var current = (_UpdateType)property.enumValueIndex;
if (useFixedUpdateObsolete && current != _UpdateType.FixedUpdate) {
property.serializedObject.Update();
property.enumValueIndex = (int)_UpdateType.FixedUpdate;
property.serializedObject.ApplyModifiedProperties();
} else {
using (var propScope = new PropertyScope(rect, new GUIContent(property.displayName, property.tooltip), property)) {
using (var changeCheck = new ChangeCheckScope()) {
var newUpdateType = (_UpdateType)EnumPopup(rect, propScope.content, current);
if (changeCheck.changed) {
property.enumValueIndex = (int)newUpdateType;
useFixedUpdateProp.boolValue = newUpdateType == _UpdateType.FixedUpdate;
}
moveToNextLine(ref rect);
}
}
}
}
}
internal static int drawCycles(Rect rect, [NotNull] SerializedProperty property, bool draw = true) {
internal static int drawCycles(Rect rect, [NotNull] SerializedProperty property, bool draw = true, bool allowInfiniteCycles = true) {
property.NextVisible(false);
if (property.intValue == 0) {
property.intValue = 1;
} else if (property.intValue < -1) {
property.intValue = -1;
int val = property.intValue;
if (val == 0) {
val = 1;
} else if (val < 0) {
val = allowInfiniteCycles ? -1 : 1;
}
property.intValue = val;
if (draw) PropertyField(rect, property);
return property.intValue;
return val;
}
static void moveToNextLine(ref Rect rect) {
rect.y += singleLineHeight + standardVerticalSpacing;
}
}
[CustomPropertyDrawer(typeof(UpdateType))]
class UpdateTypePropDrawer : PropertyDrawer {
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => singleLineHeight;
public override void OnGUI(Rect pos, SerializedProperty prop, GUIContent label) {
prop.Next(true);
PropertyField(pos, prop, label);
}
}

View File

@ -42,7 +42,7 @@ public class ValueContainerStartEndPropDrawer : PropertyDrawer {
return SerializedPropertyType.Rect;
case PropType.Int:
return SerializedPropertyType.Integer;
case PropType.Double:
case PropType.Double: // todo support double
case PropType.None:
default:
throw new Exception();
@ -122,7 +122,7 @@ public class ValueContainerStartEndPropDrawer : PropertyDrawer {
case PropType.Int:
var newIntVal = EditorGUI.IntField(position, guiContent, Mathf.RoundToInt(valueContainer.FloatVal));
return ((float)newIntVal).ToContainer();
case PropType.Double:
case PropType.Double: // todo support double with EditorGUI.DoubleField()?
case PropType.None:
default:
throw new Exception();

View File

@ -105,12 +105,12 @@ namespace PrimeTween {
var parametricEase = settings.parametricEase;
var strength = settings.parametricEaseStrength;
if (parametricEase == ParametricEase.BounceExact) {
var fullAmplitude = tween.getPropType() == PropType.Quaternion ?
var fullAmplitude = tween.propType == PropType.Quaternion ?
Quaternion.Angle(tween.startValue.QuaternionVal, tween.endValue.QuaternionVal) :
tween.diff.Vector4Val.magnitude;
// todo account for double
/*double calcFullAmplitude() {
switch (tween.getPropType()) {
switch (tween.propType) {
case PropType.Quaternion:
return Quaternion.Angle(tween.startValue.QuaternionVal, tween.endValue.QuaternionVal);
case PropType.Double:
@ -166,11 +166,6 @@ namespace PrimeTween {
Debug.LogError("Ease.Custom is an invalid type for Easing.Evaluate(). Please choose another Ease type instead.");
return interpolationFactor;
case Ease.Default:
#if UNITY_EDITOR
if (Constants.warnNoInstance) {
return interpolationFactor;
}
#endif
return StandardEasing.Evaluate(interpolationFactor, PrimeTweenManager.Instance.defaultEase);
default:
return StandardEasing.Evaluate(interpolationFactor, ease);

View File

@ -1,3 +1,4 @@
using System;
using JetBrains.Annotations;
using UnityEngine;
using T = PrimeTween.TweenSettings<float>;
@ -29,7 +30,9 @@ namespace PrimeTween {
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.";
internal const string updateTypeTooltip = "Controls Unity's event function, which updates the animation.\n\n" +
"Default is MonoBehaviour.Update().";
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."; // todo allow this as the last animation in the Sequence? it would still not be possible to Chain anything after the infinite animation, but will unlock use cases like adding startDelay to the Sequence. I can't do by adding startDelay support to Sequence because startDelay works differently for tweens: it's applied every tween loop
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" +
@ -47,21 +50,5 @@ namespace PrimeTween {
internal const string nestSequenceTwiceError = "Sequence can be nested in other sequence only once.";
internal const string nestTweenTwiceError = "A tween can be added to a sequence only once and can only belong to one sequence.";
internal const string addDeadTweenToSequenceError = "It's not allowed to add 'dead' tweens to a sequence.";
#if UNITY_EDITOR
internal const string editModeWarning = "Please don't call PrimeTween's API in Edit mode (while the scene is not playing).";
internal static bool warnNoInstance {
get {
if (noInstance) {
Debug.LogWarning(editModeWarning);
return true;
}
return false;
}
}
internal static bool noInstance => ReferenceEquals(null, PrimeTweenManager.Instance);
#endif
}
}

View File

@ -305,7 +305,7 @@ namespace PrimeTween {
tween.endValue = CalculateRelative(tween, getter(tween), tween.endValue);
/*var getter = tween.getter;
// todo this doesn't account for double val
if (tween.getPropType() == PropType.Quaternion) {
if (tween.propType == PropType.Quaternion) {
if (getter != null) {
tween.endValue.QuaternionVal *= getter(tween).QuaternionVal;
} else {
@ -411,7 +411,7 @@ namespace PrimeTween {
public Tween From(Rect fromValue, bool setImmediately = true, bool isRelative = false) => setFrom(setImmediately, isRelative, fromValue.ToContainer(), PropType.Rect);
static ValueContainer CalculateRelative(ReusableTween tween, ValueContainer current, ValueContainer diff) {
switch (tween.getPropType()) {
switch (tween.propType) {
case PropType.Quaternion:
return (current.QuaternionVal * diff.QuaternionVal).ToContainer();
case PropType.Double:
@ -443,8 +443,8 @@ namespace PrimeTween {
tween.endValue = CalculateRelative(tween, current, tween.endValue);
}
if (fromValue.HasValue) {
if (tween.getPropType() != propType) {
Debug.LogError($"Animated value is {tween.getPropType()}, but '{nameof(From)}()' was called with {propType}. Please provide a correct type.");
if (tween.propType != propType) {
Debug.LogError($"Animated value is {tween.propType}, but '{nameof(From)}()' was called with {propType}. Please provide a correct type.");
return this;
}
tween.startFromCurrent = false;

View File

@ -1,7 +1,6 @@
// ReSharper disable Unity.RedundantHideInInspectorAttribute
#if PRIME_TWEEN_SAFETY_CHECKS && UNITY_ASSERTIONS
#define SAFETY_CHECKS
using System.Linq;
#endif
using System;
using System.Collections.Generic;
@ -16,9 +15,41 @@ using UnityEditor;
namespace PrimeTween {
[AddComponentMenu("")]
internal class PrimeTweenManager : MonoBehaviour {
internal static PrimeTweenManager Instance;
#if UNITY_EDITOR || SAFETY_CHECKS
internal static PrimeTweenManager _instance;
internal static PrimeTweenManager Instance {
get {
if (!HasInstance) {
#if UNITY_EDITOR
CreateInstance();
_instance.gameObject.hideFlags = HideFlags.HideAndDontSave;
#else
const string error = nameof(PrimeTweenManager) + " is not created yet. Please add the 'PRIME_TWEEN_EXPERIMENTAL' define to your project, then use '" + nameof(PrimeTweenConfig) + "." + nameof(PrimeTweenConfig.ManualInitialize) + "()' to initialize PrimeTween before '" + nameof(RuntimeInitializeLoadType) + "." + nameof(RuntimeInitializeLoadType.BeforeSceneLoad) + "'.";
throw new Exception(error);
#endif
} /*else if (_instance == null) { // todo this throws if PrimeTween API is called from OnDestroy(). See the DestructionOrderTest scene. How to detect manual PrimeTweenManager destruction?
throw new Exception(nameof(PrimeTweenManager) + " was manually destroyed after creation, which is not allowed. Please check you're not destroying all objects manually.");
}*/
return _instance;
}
private set => _instance = value;
}
#else
internal static PrimeTweenManager Instance;
#endif
internal static bool HasInstance {
get {
#if UNITY_EDITOR || SAFETY_CHECKS
return !ReferenceEquals(null, _instance);
#else
return Instance != null;
#endif
}
}
#if UNITY_EDITOR
static bool isHotReload = true;
static bool isHotReload = true;
#endif
internal static int customInitialCapacity = -1;
@ -29,6 +60,7 @@ namespace PrimeTween {
[ItemCanBeNull]
#endif
[SerializeField] internal List<ReusableTween> tweens;
[SerializeField] internal List<ReusableTween> lateUpdateTweens;
[SerializeField] internal List<ReusableTween> fixedUpdateTweens;
[NonSerialized] internal List<ReusableTween> pool;
/// startValue can't be replaced with 'Tween lastTween'
@ -41,6 +73,7 @@ namespace PrimeTween {
[HideInInspector]
internal long lastId = 1;
internal Ease defaultEase = Ease.OutQuad;
internal _UpdateType defaultUpdateType = _UpdateType.Update;
internal const Ease defaultShakeEase = Ease.OutQuad;
internal bool warnTweenOnDisabledTarget = true;
internal bool warnZeroDuration = true;
@ -49,17 +82,38 @@ namespace PrimeTween {
internal bool validateCustomCurves = true;
internal bool warnEndValueEqualsCurrent = true;
int processedCount;
int lateUpdateTweensProcessedCount;
int fixedUpdateTweensProcessedCount;
int maxLateUpdateCount;
internal int updateDepth;
internal static readonly object dummyTarget = new object();
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void beforeSceneLoad() {
if (!HasInstance) {
CreateInstanceAndDontDestroy();
}
}
internal static void CreateInstanceAndDontDestroy() {
CreateInstance();
DontDestroyOnLoad(Instance.gameObject);
}
static void CreateInstance() {
#if UNITY_EDITOR
isHotReload = false;
#endif
Assert.IsNull(Instance);
var go = new GameObject(nameof(PrimeTweenManager));
DontDestroyOnLoad(go);
GameObject go;
try {
go = new GameObject(nameof(PrimeTweenManager));
} catch (UnityException e) {
if (e.Message.Contains("is not allowed to be called from a MonoBehaviour constructor")) {
const string message = "this PrimeTween's API is not allowed to be called from a MonoBehaviour constructor (or instance field initializer).";
throw new Exception(message);
}
throw;
}
var instance = go.AddComponent<PrimeTweenManager>();
const int defaultInitialCapacity = 200;
instance.init(customInitialCapacity != -1 ? customInitialCapacity : defaultInitialCapacity);
@ -68,6 +122,7 @@ namespace PrimeTween {
void init(int capacity) {
tweens = new List<ReusableTween>(capacity);
lateUpdateTweens = new List<ReusableTween>(capacity);
fixedUpdateTweens = new List<ReusableTween>(capacity);
pool = new List<ReusableTween>(capacity);
for (int i = 0; i < capacity; i++) {
@ -78,32 +133,63 @@ namespace PrimeTween {
}
const string manualInstanceCreationIsNotAllowedMessage = "Please don't create the " + nameof(PrimeTweenManager) + " instance manually.";
void Awake() => Assert.IsNull(Instance, manualInstanceCreationIsNotAllowedMessage);
void Awake() => Assert.IsFalse(HasInstance, manualInstanceCreationIsNotAllowedMessage);
#if UNITY_EDITOR
[InitializeOnLoadMethod]
static void iniOnLoad() {
float curTime = (float)EditorApplication.timeSinceStartup;
EditorApplication.update += () => {
if (Application.isPlaying) {
return;
}
float newTime = (float)EditorApplication.timeSinceStartup;
float unscaledDeltaTime = newTime - curTime;
if (unscaledDeltaTime < 1f / 120f) {
return;
}
if (unscaledDeltaTime > 1f / 10f) {
// Unity Editor doesn't trigger EditorApplication.update when a context menu is open, which results in a big time jump. Clamp dt in this case
unscaledDeltaTime = 1f / 120f;
}
curTime = newTime;
if (HasInstance && Instance.tweensCount > 0) {
float deltaTime = unscaledDeltaTime * Time.timeScale;
Instance.UpdateTweens(_UpdateType.Update, deltaTime, unscaledDeltaTime);
Instance.UpdateTweens(_UpdateType.LateUpdate, deltaTime, unscaledDeltaTime);
Instance.UpdateTweens(_UpdateType.FixedUpdate, deltaTime, unscaledDeltaTime);
}
};
EditorApplication.playModeStateChanged += state => {
if (state == PlayModeStateChange.EnteredEditMode) {
Instance = null;
customInitialCapacity = -1;
switch (state) {
case PlayModeStateChange.EnteredEditMode:
Instance = null;
customInitialCapacity = -1;
break;
case PlayModeStateChange.ExitingEditMode:
if (HasInstance) {
DestroyImmediate(Instance.gameObject);
Instance = null;
}
break;
}
};
if (!isHotReload) {
return;
}
if (!Application.isPlaying) {
if (HasInstance) {
return;
}
var instances = Resources.FindObjectsOfTypeAll<PrimeTweenManager>();
Assert.IsTrue(instances.Length <= 1, instances.Length);
if (instances.Length == 0) {
return;
}
var foundInScene = instances[0];
if (foundInScene == null) {
return;
}
Assert.IsNull(Instance);
var foundInScene =
#if UNITY_2023_1_OR_NEWER
FindAnyObjectByType
#else
FindObjectOfType
#endif
<PrimeTweenManager>();
Assert.IsNotNull(foundInScene);
#if PRIME_TWEEN_INSPECTOR_DEBUGGING
Debug.LogError("PRIME_TWEEN_INSPECTOR_DEBUGGING doesn't work with 'Recompile And Continue Playing' because Tween.id is serializable but Tween.tween is not.");
return;
@ -113,24 +199,20 @@ namespace PrimeTween {
Debug.Log($"All tweens ({count}) were stopped because of 'Recompile And Continue Playing'.");
}
foundInScene.init(foundInScene.currentPoolCapacity);
foundInScene.updateDepth = 0;
Instance = foundInScene;
}
void Reset() {
Assert.IsFalse(Application.isPlaying);
Debug.LogError(manualInstanceCreationIsNotAllowedMessage);
DestroyImmediate(this);
}
#endif
void Start() {
#if SAFETY_CHECKS
// Selection.activeGameObject = gameObject;
#endif
Assert.AreEqual(Instance, this, manualInstanceCreationIsNotAllowedMessage);
}
internal void FixedUpdate() => update(fixedUpdateTweens, Time.fixedDeltaTime, Time.fixedUnscaledDeltaTime, out _);
internal void FixedUpdate() => UpdateTweens(_UpdateType.FixedUpdate);
/// <summary>
/// The most common tween lifecycle:
@ -140,15 +222,15 @@ namespace PrimeTween {
/// 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.
/// </summary>
internal void Update() => update(tweens, Time.deltaTime, Time.unscaledDeltaTime, out processedCount);
internal void Update() => UpdateTweens(_UpdateType.Update);
void update(List<ReusableTween> tweens, float deltaTime, float unscaledDeltaTime, out int processedCount) {
void update(List<ReusableTween> tweens, float deltaTime, float unscaledDeltaTime, out int processedCount, int? maxCount = null) {
if (updateDepth != 0) {
throw new Exception("updateDepth != 0");
}
updateDepth++;
// onComplete and onValueChange can create new tweens. Cache count to process only those tweens that were present when the update started
var oldCount = tweens.Count;
int oldCount = maxCount < tweens.Count ? maxCount.Value : tweens.Count;
var numRemoved = 0;
// Process tweens in the order of creation.
// This allows to create tween duplicates because the latest tween on the same value will overwrite the previous ones.
@ -176,49 +258,104 @@ namespace PrimeTween {
}
processedCount = oldCount - numRemoved;
#if SAFETY_CHECKS
Assert.IsTrue(tweens.Skip(oldCount - numRemoved).Take(numRemoved).All(_ => _ == null));
Assert.IsTrue(tweens.Skip(oldCount).All(_ => _ != null));
for (int i = oldCount - numRemoved; i < oldCount; i++) { // Check removed tweens are shifted to the left and are null
Assert.IsNull(tweens[i]);
}
for (int i = oldCount; i < tweens.Count; i++) { // Check all newly created tweens are not null
Assert.IsNotNull(tweens[i]);
}
#endif
updateDepth--;
if (numRemoved == 0) {
return;
}
var newCount = tweens.Count;
for (int i = oldCount; i < newCount; i++) {
var tween = tweens[i];
var newIndex = i - numRemoved;
if (numRemoved != 0) {
var newCount = tweens.Count;
for (int i = oldCount; i < newCount; i++) {
var tween = tweens[i];
var newIndex = i - numRemoved;
#if SAFETY_CHECKS
Assert.IsNotNull(tween);
#endif
tweens[newIndex] = tween;
}
tweens.RemoveRange(newCount - numRemoved, numRemoved);
Assert.AreEqual(tweens.Count, newCount - numRemoved);
#if SAFETY_CHECKS
Assert.IsNotNull(tween);
foreach (var t in tweens) {
Assert.IsNotNull(t);
}
// Check no duplicates
hashSet.Clear();
hashSet.UnionWith(tweens);
Assert.AreEqual(hashSet.Count, tweens.Count);
#endif
tweens[newIndex] = tween;
}
tweens.RemoveRange(newCount - numRemoved, numRemoved);
Assert.AreEqual(tweens.Count, newCount - numRemoved);
#if SAFETY_CHECKS
Assert.IsFalse(tweens.Any(_ => _ == null));
Assert.AreEqual(tweens.Count, tweens.Distinct().Count());
#endif
}
void LateUpdate() {
updateDepth++;
var cachedCount = tweens.Count;
for (int i = processedCount; i < cachedCount; i++) {
var tween = tweens[i];
// ReSharper disable once PossibleNullReferenceException
if (tween._isAlive && !tween.startFromCurrent && tween.settings.startDelay == 0 && !tween.isUnityTargetDestroyed() && !tween.isAdditive
&& tween.canManipulate()
&& tween.elapsedTimeTotal == 0f) {
Assert.AreEqual(0f, tween.elapsedTimeTotal);
tween.SetElapsedTimeTotal(0f);
}
#if SAFETY_CHECKS
readonly HashSet<ReusableTween> hashSet = new HashSet<ReusableTween>();
#endif
internal void LateUpdate() {
UpdateTweens(_UpdateType.LateUpdate);
ApplyStartValues(_UpdateType.Update);
ApplyStartValues(_UpdateType.LateUpdate);
}
internal void ApplyStartValues(_UpdateType updateType) {
switch (updateType) {
case _UpdateType.Default:
Debug.LogError("Please provide non-default update type.");
break;
case _UpdateType.Update:
ApplyStartValuesInternal(tweens, processedCount);
break;
case _UpdateType.LateUpdate:
ApplyStartValuesInternal(lateUpdateTweens, lateUpdateTweensProcessedCount);
break;
case _UpdateType.FixedUpdate:
ApplyStartValuesInternal(fixedUpdateTweens, fixedUpdateTweensProcessedCount);
break;
default: throw new Exception($"Invalid update type: {updateType}");
}
void ApplyStartValuesInternal(List<ReusableTween> list, int processedCount) {
updateDepth++;
int cachedCount = list.Count;
for (int i = processedCount; i < cachedCount; i++) {
var tween = list[i];
// ReSharper disable once PossibleNullReferenceException
if (tween._isAlive && !tween.startFromCurrent && tween.settings.startDelay == 0 && !tween.isUnityTargetDestroyed() && !tween.isAdditive
&& tween.canManipulate()
&& tween.elapsedTimeTotal == 0f) {
tween.SetElapsedTimeTotal(0f);
}
}
updateDepth--;
}
}
internal void UpdateTweens(_UpdateType updateType, float? deltaTime = null, float? unscaledDeltaTime = null) {
switch (updateType) {
case _UpdateType.Default:
Debug.LogError("Please provide non-default update type.");
break;
case _UpdateType.Update:
update(tweens, deltaTime ?? Time.deltaTime, unscaledDeltaTime ?? Time.unscaledDeltaTime, out processedCount);
break;
case _UpdateType.LateUpdate:
update(lateUpdateTweens, deltaTime ?? Time.deltaTime, unscaledDeltaTime ?? Time.unscaledDeltaTime, out lateUpdateTweensProcessedCount, maxLateUpdateCount);
maxLateUpdateCount = lateUpdateTweens.Count;
break;
case _UpdateType.FixedUpdate:
update(fixedUpdateTweens, deltaTime ?? Time.fixedDeltaTime, unscaledDeltaTime ?? Time.fixedUnscaledDeltaTime, out fixedUpdateTweensProcessedCount);
break;
default: throw new Exception($"Invalid update type: {updateType}");
}
updateDepth--;
}
void releaseTweenToPool([NotNull] ReusableTween tween) {
#if SAFETY_CHECKS
checkNotInSequence(tweens);
checkNotInSequence(lateUpdateTweens);
checkNotInSequence(fixedUpdateTweens);
void checkNotInSequence(List<ReusableTween> list) {
foreach (var t in list) {
@ -236,13 +373,7 @@ namespace PrimeTween {
/// Returns null if target is a destroyed UnityEngine.Object
internal static Tween? delayWithoutDurationCheck([CanBeNull] object target, float duration, bool useUnscaledTime) {
#if UNITY_EDITOR
if (Constants.warnNoInstance) {
return null;
}
#endif
var tween = fetchTween();
tween.setPropType(PropType.Float);
var settings = new TweenSettings {
duration = duration,
ease = Ease.Linear,
@ -256,11 +387,6 @@ namespace PrimeTween {
[NotNull]
internal static ReusableTween fetchTween() {
#if UNITY_EDITOR
if (Constants.warnNoInstance) {
return new ReusableTween();
}
#endif
return Instance.fetchTween_internal();
}
@ -291,22 +417,12 @@ namespace PrimeTween {
}
internal static void checkDuration<T>([CanBeNull] T target, float duration) where T : class {
#if UNITY_EDITOR
if (Constants.noInstance) {
return;
}
#endif
if (Instance.warnZeroDuration && duration <= 0) {
Debug.LogWarning($"Tween duration ({duration}) <= 0. {Constants.buildWarningCanBeDisabledMessage(nameof(warnZeroDuration))}", target as UnityEngine.Object);
}
}
internal static Tween addTween([NotNull] ReusableTween tween) {
#if UNITY_EDITOR
if (Constants.noInstance) {
return default;
}
#endif
return Instance.addTween_internal(tween);
}
@ -328,15 +444,27 @@ namespace PrimeTween {
Debug.LogWarning($"Tween is started on GameObject that is not active in hierarchy: {comp.name}. {Constants.buildWarningCanBeDisabledMessage(nameof(warnTweenOnDisabledTarget))}", comp);
}
}
if (tween.settings._updateType == _UpdateType.Default) {
tween.settings._updateType = defaultUpdateType;
}
switch (tween.settings._updateType) {
case _UpdateType.Update:
tweens.Add(tween);
break;
case _UpdateType.LateUpdate:
lateUpdateTweens.Add(tween);
break;
case _UpdateType.FixedUpdate:
fixedUpdateTweens.Add(tween);
break;
default:
Debug.LogError($"Invalid update type: {tween.settings._updateType}");
return default;
}
#if SAFETY_CHECKS
// Debug.Log($"[{Time.frameCount}] created: {tween.GetDescription()}", tween.unityTarget);
StackTraces.Record(tween.id);
#endif
if (tween.settings.useFixedUpdate) {
fixedUpdateTweens.Add(tween);
} else {
tweens.Add(tween);
}
lastId++; // increment only when tween added successfully
#if UNITY_ASSERTIONS && !PRIME_TWEEN_DISABLE_ASSERTIONS
maxSimultaneousTweensCount = Math.Max(maxSimultaneousTweensCount, tweensCount);
@ -355,18 +483,13 @@ namespace PrimeTween {
}
internal static int processAll([CanBeNull] object onTarget, [NotNull] Predicate<ReusableTween> predicate, bool allowToProcessTweensInsideSequence) {
#if UNITY_EDITOR
if (Constants.warnNoInstance) {
return default;
}
#endif
return Instance.processAll_internal(onTarget, predicate, allowToProcessTweensInsideSequence);
}
internal static bool logCantManipulateError = true;
int processAll_internal([CanBeNull] object onTarget, [NotNull] Predicate<ReusableTween> predicate, bool allowToProcessTweensInsideSequence) {
return processInList(tweens) + processInList(fixedUpdateTweens);
return processInList(tweens) + processInList(lateUpdateTweens) + processInList(fixedUpdateTweens);
int processInList(List<ReusableTween> tweens) {
int numProcessed = 0;
int totalCount = 0;
@ -412,6 +535,7 @@ namespace PrimeTween {
return;
}
tweens.Capacity = capacity;
lateUpdateTweens.Capacity = capacity;
fixedUpdateTweens.Capacity = capacity;
#if UNITY_2021_2_OR_NEWER
shakes.EnsureCapacity(capacity);
@ -419,11 +543,12 @@ namespace PrimeTween {
resizeAndSetCapacity(pool, capacity - runningTweens, capacity);
currentPoolCapacity = capacity;
Assert.AreEqual(capacity, tweens.Capacity);
Assert.AreEqual(capacity, lateUpdateTweens.Capacity);
Assert.AreEqual(capacity, fixedUpdateTweens.Capacity);
Assert.AreEqual(capacity, pool.Capacity);
}
internal int tweensCount => tweens.Count + fixedUpdateTweens.Count;
internal int tweensCount => tweens.Count + lateUpdateTweens.Count + fixedUpdateTweens.Count;
internal static void resizeAndSetCapacity([NotNull] List<ReusableTween> list, int newCount, int newCapacity) {
Assert.IsTrue(newCapacity >= newCount);

View File

@ -2,6 +2,7 @@
#define SAFETY_CHECKS
#endif
using System;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
using UnityEngine;
using Debug = UnityEngine.Debug;
@ -24,22 +25,21 @@ namespace PrimeTween {
[SerializeField] internal float easedInterpolationFactor;
internal float cycleDuration;
#if UNITY_ASSERTIONS && !PRIME_TWEEN_DISABLE_ASSERTIONS
/// todo remove here and from generated code. Only used for assertion
[NonSerialized] PropType propType;
internal void setPropType(PropType value) => propType = value;
#else
[System.Diagnostics.Conditional("_")]
internal void setPropType([System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "UnusedParameter.Global")] PropType value) {
}
#endif
[SerializeField] internal ValueContainerStartEnd startEndValue;
internal PropType getPropType() => Utils.TweenTypeToTweenData(startEndValue.tweenType).Item1; // todo rename to propType
internal PropType propType => Utils.TweenTypeToTweenData(startEndValue.tweenType).Item1;
internal ref TweenType tweenType => ref startEndValue.tweenType;
internal ref ValueContainer startValue => ref startEndValue.startValue;
internal ref ValueContainer endValue => ref startEndValue.endValue;
internal ValueContainer diff;
internal bool isAdditive;
internal bool isAdditive {
[MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetFlag(Flags.Additive);
[MethodImpl(MethodImplOptions.AggressiveInlining)] set => SetFlag(Flags.Additive, value);
}
internal bool resetBeforeComplete {
[MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetFlag(Flags.ResetBeforeComplete);
[MethodImpl(MethodImplOptions.AggressiveInlining)] set => SetFlag(Flags.ResetBeforeComplete, value);
}
internal ValueContainer prevVal;
[SerializeField] internal TweenSettings settings;
[SerializeField] int cyclesDone;
@ -70,10 +70,24 @@ namespace PrimeTween {
bool stoppedEmergently;
internal readonly TweenCoroutineEnumerator coroutineEnumerator = new TweenCoroutineEnumerator();
internal float timeScale = 1f;
bool warnIgnoredOnCompleteIfTargetDestroyed = true;
bool warnIgnoredOnCompleteIfTargetDestroyed {
[MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetFlag(Flags.WarnIgnoredOnCompleteIfTargetDestroyed);
[MethodImpl(MethodImplOptions.AggressiveInlining)] set => SetFlag(Flags.WarnIgnoredOnCompleteIfTargetDestroyed, value);
}
internal ShakeData shakeData;
internal bool shakeSign {
[MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetFlag(Flags.ShakeSign);
[MethodImpl(MethodImplOptions.AggressiveInlining)] set => SetFlag(Flags.ShakeSign, value);
}
internal bool isPunch {
[MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetFlag(Flags.ShakePunch);
[MethodImpl(MethodImplOptions.AggressiveInlining)] set => SetFlag(Flags.ShakePunch, value);
}
State state;
bool warnEndValueEqualsCurrent;
bool warnEndValueEqualsCurrent {
[MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetFlag(Flags.WarnEndValueEqualsCurrent);
[MethodImpl(MethodImplOptions.AggressiveInlining)] set => SetFlag(Flags.WarnEndValueEqualsCurrent, value);
}
internal bool updateAndCheckIfRunning(float dt) {
if (!_isAlive) {
@ -189,6 +203,16 @@ namespace PrimeTween {
}
Assert.IsTrue(newCyclesDone == cyclesDone, id);
if (isDone(cyclesDiff)) {
if (resetBeforeComplete && isMainSequenceRoot()) {
// reset Sequence
foreach (var t in getSequenceSelfChildren(false)) {
t.tween.updateSequenceChild(0f, true);
if (isEarlyExitAfterChildUpdate()) {
goto EarlyExit;
}
}
EarlyExit:;
}
if (isMainSequenceRoot() && !_isPaused) {
sequence.releaseTweens();
}
@ -357,7 +381,6 @@ namespace PrimeTween {
#endif
id = -1;
target = null;
setPropType(PropType.None);
settings.customEase = null;
customOnValueChange = null;
onValueChange = null;
@ -372,11 +395,14 @@ namespace PrimeTween {
timeScale = 1f;
warnIgnoredOnCompleteIfTargetDestroyed = true;
clearOnUpdate();
resetBeforeComplete = false;
}
/// <param name="warnIfTargetDestroyed">https://github.com/KyryloKuzyk/PrimeTween/discussions/4</param>
internal void OnComplete([NotNull] Action _onComplete, bool warnIfTargetDestroyed) {
Assert.IsNotNull(_onComplete);
internal void OnComplete([CanBeNull] Action _onComplete, bool warnIfTargetDestroyed) {
if (_onComplete == null) {
return;
}
validateOnCompleteAssignment();
warnIgnoredOnCompleteIfTargetDestroyed = warnIfTargetDestroyed;
onCompleteCallback = _onComplete;
@ -391,12 +417,14 @@ namespace PrimeTween {
};
}
internal void OnComplete<T>([CanBeNull] T _target, [NotNull] Action<T> _onComplete, bool warnIfTargetDestroyed) where T : class {
internal void OnComplete<T>([CanBeNull] T _target, [CanBeNull] Action<T> _onComplete, bool warnIfTargetDestroyed) where T : class {
if (_target == null || isDestroyedUnityObject(_target)) {
Debug.LogError($"{nameof(_target)} is null or has been destroyed. {Constants.onCompleteCallbackIgnored}");
return;
}
Assert.IsNotNull(_onComplete);
if (_onComplete == null) {
return;
}
validateOnCompleteAssignment();
warnIgnoredOnCompleteIfTargetDestroyed = warnIfTargetDestroyed;
onCompleteTarget = _target;
@ -439,16 +467,8 @@ namespace PrimeTween {
Assert.IsNotNull(_onValueChange);
Assert.IsNull(getter);
tweenType = _tweenType;
var propertyType = getPropType();
var propertyType = propType;
Assert.AreNotEqual(PropType.None, propertyType);
#if UNITY_ASSERTIONS && !PRIME_TWEEN_DISABLE_ASSERTIONS
Assert.AreEqual(propType, getPropType());
#endif
#if UNITY_EDITOR
if (Constants.noInstance) {
return;
}
#endif
if (_settings.ease == Ease.Default) {
_settings.ease = PrimeTweenManager.Instance.defaultEase;
} else if (_settings.ease == Ease.Custom && _settings.parametricEase == ParametricEase.None) {
@ -520,6 +540,10 @@ namespace PrimeTween {
Assert.IsFalse(startFromCurrent);
Assert.IsTrue(timeScale < 0 || cyclesDone == settings.cycles);
Assert.IsTrue(timeScale >= 0 || cyclesDone == iniCyclesDone);
if (resetBeforeComplete && !sequence.IsCreated) {
// reset Tween
setElapsedTimeTotal(0f, out _);
}
onComplete?.Invoke(this);
}
@ -675,7 +699,7 @@ namespace PrimeTween {
internal void cacheDiff() {
Assert.IsFalse(startFromCurrent);
var propertyType = getPropType();
var propertyType = propType;
Assert.AreNotEqual(PropType.None, propertyType);
switch (propertyType) {
case PropType.Quaternion:
@ -861,5 +885,24 @@ namespace PrimeTween {
Assert.IsTrue(result >= 0);
return result;
}
[Flags]
private enum Flags : byte {
Additive = 1 << 0,
ShakeSign = 1 << 1,
ShakePunch = 1 << 2,
WarnEndValueEqualsCurrent = 1 << 3,
WarnIgnoredOnCompleteIfTargetDestroyed = 1 << 4,
ResetBeforeComplete = 1 << 5
}
[SerializeField] Flags flags = Flags.WarnIgnoredOnCompleteIfTargetDestroyed; // todo how to show this in the Inspector?
[MethodImpl(MethodImplOptions.AggressiveInlining)] bool GetFlag(Flags flag) => (flags & flag) != 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)] void SetFlag(Flags flag, bool value) {
if (value) {
flags |= flag;
} else {
flags &= ~flag;
}
}
}
}

View File

@ -42,7 +42,10 @@ namespace PrimeTween {
}
}
int hash = ComputeHash(buf, len);
Assert.AreEqual(id, idToHash.Count);
if (id != idToHash.Count) {
Debug.LogError("PRIME_TWEEN_SAFETY_CHECKS doesn't support script hot reloading (Recompile And Continue Playing). Please remove the PRIME_TWEEN_SAFETY_CHECKS define."); // todo fix?
return;
}
idToHash.Add(hash);
if (hashToTraces.TryGetValue(hash, out var traces)) {
if (!Contains(traces, buf, len)) {

View File

@ -152,7 +152,7 @@ namespace PrimeTween {
case Ease.Custom:
case Ease.Default:
default:
throw new System.Exception();
throw new System.Exception($"Invalid ease type: {ease}.");
}
}
}

View File

@ -135,7 +135,9 @@ namespace PrimeTween {
VisualElementTopLeft,
VisualElementColor,
VisualElementBackgroundColor,
VisualElementOpacity,
TextMaxVisibleCharacters,
TextFontSize,
}
public partial struct Tween {
@ -1733,6 +1735,24 @@ namespace PrimeTween {
}, t => (t.target as UnityEngine.UIElements.VisualElement).style.color.value.ToContainer(), TweenType.VisualElementColor);
}
public static Tween Color([NotNull] UnityEngine.UIElements.VisualElement target, UnityEngine.Color endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> Color(target, new TweenSettings<UnityEngine.Color>(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween Color([NotNull] UnityEngine.UIElements.VisualElement target, UnityEngine.Color endValue, float duration, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> Color(target, new TweenSettings<UnityEngine.Color>(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween Color([NotNull] UnityEngine.UIElements.VisualElement target, UnityEngine.Color startValue, UnityEngine.Color endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> Color(target, new TweenSettings<UnityEngine.Color>(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween Color([NotNull] UnityEngine.UIElements.VisualElement target, UnityEngine.Color startValue, UnityEngine.Color endValue, float duration, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> Color(target, new TweenSettings<UnityEngine.Color>(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween Color([NotNull] UnityEngine.UIElements.VisualElement target, UnityEngine.Color endValue, TweenSettings settings) => Color(target, new TweenSettings<UnityEngine.Color>(endValue, settings));
public static Tween Color([NotNull] UnityEngine.UIElements.VisualElement target, UnityEngine.Color startValue, UnityEngine.Color endValue, TweenSettings settings) => Color(target, new TweenSettings<UnityEngine.Color>(startValue, endValue, settings));
public static Tween Color([NotNull] UnityEngine.UIElements.VisualElement target, TweenSettings<UnityEngine.Color> settings) {
return animate(target, ref settings, _tween => {
var _target = _tween.target as UnityEngine.UIElements.VisualElement;
var val = _tween.ColorVal;
_target.style.color = val;
}, t => (t.target as UnityEngine.UIElements.VisualElement).style.color.value.ToContainer(), TweenType.VisualElementColor);
}
public static Tween VisualElementBackgroundColor([NotNull] UnityEngine.UIElements.VisualElement target, UnityEngine.Color endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> VisualElementBackgroundColor(target, new TweenSettings<UnityEngine.Color>(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween VisualElementBackgroundColor([NotNull] UnityEngine.UIElements.VisualElement target, UnityEngine.Color endValue, float duration, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
@ -1751,6 +1771,42 @@ namespace PrimeTween {
}, t => (t.target as UnityEngine.UIElements.VisualElement).style.backgroundColor.value.ToContainer(), TweenType.VisualElementBackgroundColor);
}
public static Tween VisualElementOpacity([NotNull] UnityEngine.UIElements.VisualElement target, Single endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> VisualElementOpacity(target, new TweenSettings<float>(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween VisualElementOpacity([NotNull] UnityEngine.UIElements.VisualElement target, Single endValue, float duration, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> VisualElementOpacity(target, new TweenSettings<float>(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween VisualElementOpacity([NotNull] UnityEngine.UIElements.VisualElement target, Single startValue, Single endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> VisualElementOpacity(target, new TweenSettings<float>(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween VisualElementOpacity([NotNull] UnityEngine.UIElements.VisualElement target, Single startValue, Single endValue, float duration, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> VisualElementOpacity(target, new TweenSettings<float>(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween VisualElementOpacity([NotNull] UnityEngine.UIElements.VisualElement target, Single endValue, TweenSettings settings) => VisualElementOpacity(target, new TweenSettings<float>(endValue, settings));
public static Tween VisualElementOpacity([NotNull] UnityEngine.UIElements.VisualElement target, Single startValue, Single endValue, TweenSettings settings) => VisualElementOpacity(target, new TweenSettings<float>(startValue, endValue, settings));
public static Tween VisualElementOpacity([NotNull] UnityEngine.UIElements.VisualElement target, TweenSettings<float> settings) {
return animate(target, ref settings, _tween => {
var _target = _tween.target as UnityEngine.UIElements.VisualElement;
var val = _tween.FloatVal;
_target.style.opacity = val;
}, t => (t.target as UnityEngine.UIElements.VisualElement).style.opacity.value.ToContainer(), TweenType.VisualElementOpacity);
}
public static Tween Alpha([NotNull] UnityEngine.UIElements.VisualElement target, Single endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> Alpha(target, new TweenSettings<float>(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween Alpha([NotNull] UnityEngine.UIElements.VisualElement target, Single endValue, float duration, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> Alpha(target, new TweenSettings<float>(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween Alpha([NotNull] UnityEngine.UIElements.VisualElement target, Single startValue, Single endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> Alpha(target, new TweenSettings<float>(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween Alpha([NotNull] UnityEngine.UIElements.VisualElement target, Single startValue, Single endValue, float duration, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> Alpha(target, new TweenSettings<float>(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween Alpha([NotNull] UnityEngine.UIElements.VisualElement target, Single endValue, TweenSettings settings) => Alpha(target, new TweenSettings<float>(endValue, settings));
public static Tween Alpha([NotNull] UnityEngine.UIElements.VisualElement target, Single startValue, Single endValue, TweenSettings settings) => Alpha(target, new TweenSettings<float>(startValue, endValue, settings));
public static Tween Alpha([NotNull] UnityEngine.UIElements.VisualElement target, TweenSettings<float> settings) {
return animate(target, ref settings, _tween => {
var _target = _tween.target as UnityEngine.UIElements.VisualElement;
var val = _tween.FloatVal;
_target.style.opacity = val;
}, t => (t.target as UnityEngine.UIElements.VisualElement).style.opacity.value.ToContainer(), TweenType.VisualElementOpacity);
}
#endif
#if TEXT_MESH_PRO_INSTALLED
public static Tween TextMaxVisibleCharacters([NotNull] TMPro.TMP_Text target, int endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
@ -1764,6 +1820,24 @@ namespace PrimeTween {
public static Tween TextMaxVisibleCharacters([NotNull] TMPro.TMP_Text target, int endValue, TweenSettings settings) => TextMaxVisibleCharacters(target, new TweenSettings<int>(endValue, settings));
public static Tween TextMaxVisibleCharacters([NotNull] TMPro.TMP_Text target, int startValue, int endValue, TweenSettings settings) => TextMaxVisibleCharacters(target, new TweenSettings<int>(startValue, endValue, settings));
public static Tween TextFontSize([NotNull] TMPro.TMP_Text target, Single endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> TextFontSize(target, new TweenSettings<float>(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween TextFontSize([NotNull] TMPro.TMP_Text target, Single endValue, float duration, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> TextFontSize(target, new TweenSettings<float>(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween TextFontSize([NotNull] TMPro.TMP_Text target, Single startValue, Single endValue, float duration, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> TextFontSize(target, new TweenSettings<float>(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween TextFontSize([NotNull] TMPro.TMP_Text target, Single startValue, Single endValue, float duration, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> TextFontSize(target, new TweenSettings<float>(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween TextFontSize([NotNull] TMPro.TMP_Text target, Single endValue, TweenSettings settings) => TextFontSize(target, new TweenSettings<float>(endValue, settings));
public static Tween TextFontSize([NotNull] TMPro.TMP_Text target, Single startValue, Single endValue, TweenSettings settings) => TextFontSize(target, new TweenSettings<float>(startValue, endValue, settings));
public static Tween TextFontSize([NotNull] TMPro.TMP_Text target, TweenSettings<float> settings) {
return animate(target, ref settings, _tween => {
var _target = _tween.target as TMPro.TMP_Text;
var val = _tween.FloatVal;
_target.fontSize = val;
}, t => (t.target as TMPro.TMP_Text).fontSize.ToContainer(), TweenType.TextFontSize);
}
#endif
public static Tween Custom(float startValue, float endValue, float duration, [NotNull] Action<float> onValueChange, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
@ -1779,7 +1853,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Float);
tween.customOnValueChange = onValueChange;
tween.Setup(PrimeTweenManager.dummyTarget, ref settings.settings, _tween => {
var _onValueChange = _tween.customOnValueChange as Action<float>;
@ -1787,7 +1860,8 @@ namespace PrimeTween {
try {
_onValueChange(val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomFloat);
@ -1813,7 +1887,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Float);
tween.customOnValueChange = onValueChange;
tween.isAdditive = isAdditive;
tween.Setup(target, ref settings.settings, _tween => {
@ -1830,7 +1903,8 @@ namespace PrimeTween {
try {
_onValueChange(_target, val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e, _target as UnityEngine.Object);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomFloat);
@ -1840,7 +1914,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Float);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -1849,7 +1922,6 @@ namespace PrimeTween {
tween.intParam = intParam;
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Float);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -1867,7 +1939,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Color);
tween.customOnValueChange = onValueChange;
tween.Setup(PrimeTweenManager.dummyTarget, ref settings.settings, _tween => {
var _onValueChange = _tween.customOnValueChange as Action<UnityEngine.Color>;
@ -1875,7 +1946,8 @@ namespace PrimeTween {
try {
_onValueChange(val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomColor);
@ -1901,7 +1973,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Color);
tween.customOnValueChange = onValueChange;
tween.isAdditive = isAdditive;
tween.Setup(target, ref settings.settings, _tween => {
@ -1918,7 +1989,8 @@ namespace PrimeTween {
try {
_onValueChange(_target, val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e, _target as UnityEngine.Object);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomColor);
@ -1928,7 +2000,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Color);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -1937,7 +2008,6 @@ namespace PrimeTween {
tween.intParam = intParam;
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Color);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -1955,7 +2025,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector2);
tween.customOnValueChange = onValueChange;
tween.Setup(PrimeTweenManager.dummyTarget, ref settings.settings, _tween => {
var _onValueChange = _tween.customOnValueChange as Action<UnityEngine.Vector2>;
@ -1963,7 +2032,8 @@ namespace PrimeTween {
try {
_onValueChange(val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomVector2);
@ -1989,7 +2059,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector2);
tween.customOnValueChange = onValueChange;
tween.isAdditive = isAdditive;
tween.Setup(target, ref settings.settings, _tween => {
@ -2006,7 +2075,8 @@ namespace PrimeTween {
try {
_onValueChange(_target, val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e, _target as UnityEngine.Object);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomVector2);
@ -2016,7 +2086,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector2);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -2025,7 +2094,6 @@ namespace PrimeTween {
tween.intParam = intParam;
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector2);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -2043,7 +2111,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector3);
tween.customOnValueChange = onValueChange;
tween.Setup(PrimeTweenManager.dummyTarget, ref settings.settings, _tween => {
var _onValueChange = _tween.customOnValueChange as Action<UnityEngine.Vector3>;
@ -2051,7 +2118,8 @@ namespace PrimeTween {
try {
_onValueChange(val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomVector3);
@ -2077,7 +2145,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector3);
tween.customOnValueChange = onValueChange;
tween.isAdditive = isAdditive;
tween.Setup(target, ref settings.settings, _tween => {
@ -2094,7 +2161,8 @@ namespace PrimeTween {
try {
_onValueChange(_target, val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e, _target as UnityEngine.Object);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomVector3);
@ -2104,7 +2172,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector3);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -2113,7 +2180,6 @@ namespace PrimeTween {
tween.intParam = intParam;
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector3);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -2131,7 +2197,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector4);
tween.customOnValueChange = onValueChange;
tween.Setup(PrimeTweenManager.dummyTarget, ref settings.settings, _tween => {
var _onValueChange = _tween.customOnValueChange as Action<UnityEngine.Vector4>;
@ -2139,7 +2204,8 @@ namespace PrimeTween {
try {
_onValueChange(val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomVector4);
@ -2165,7 +2231,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector4);
tween.customOnValueChange = onValueChange;
tween.isAdditive = isAdditive;
tween.Setup(target, ref settings.settings, _tween => {
@ -2182,7 +2247,8 @@ namespace PrimeTween {
try {
_onValueChange(_target, val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e, _target as UnityEngine.Object);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomVector4);
@ -2192,7 +2258,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector4);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -2201,7 +2266,6 @@ namespace PrimeTween {
tween.intParam = intParam;
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Vector4);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -2219,7 +2283,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Quaternion);
tween.customOnValueChange = onValueChange;
tween.Setup(PrimeTweenManager.dummyTarget, ref settings.settings, _tween => {
var _onValueChange = _tween.customOnValueChange as Action<UnityEngine.Quaternion>;
@ -2227,7 +2290,8 @@ namespace PrimeTween {
try {
_onValueChange(val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomQuaternion);
@ -2253,7 +2317,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Quaternion);
tween.customOnValueChange = onValueChange;
tween.isAdditive = isAdditive;
tween.Setup(target, ref settings.settings, _tween => {
@ -2270,7 +2333,8 @@ namespace PrimeTween {
try {
_onValueChange(_target, val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e, _target as UnityEngine.Object);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomQuaternion);
@ -2280,7 +2344,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Quaternion);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -2289,7 +2352,6 @@ namespace PrimeTween {
tween.intParam = intParam;
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Quaternion);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -2307,7 +2369,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Rect);
tween.customOnValueChange = onValueChange;
tween.Setup(PrimeTweenManager.dummyTarget, ref settings.settings, _tween => {
var _onValueChange = _tween.customOnValueChange as Action<UnityEngine.Rect>;
@ -2315,7 +2376,8 @@ namespace PrimeTween {
try {
_onValueChange(val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomRect);
@ -2341,7 +2403,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Rect);
tween.customOnValueChange = onValueChange;
tween.isAdditive = isAdditive;
tween.Setup(target, ref settings.settings, _tween => {
@ -2358,7 +2419,8 @@ namespace PrimeTween {
try {
_onValueChange(_target, val);
} catch (Exception e) {
Assert.LogError($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}, exception:\n{e}\n", _tween.id, _tween.target as UnityEngine.Object);
UnityEngine.Debug.LogException(e, _target as UnityEngine.Object);
Assert.LogWarning($"Tween was stopped because of exception in {nameof(onValueChange)} callback, tween: {_tween.GetDescription()}\n", _tween.id, _tween.target as UnityEngine.Object);
_tween.EmergencyStop();
}
}, null, false, TweenType.CustomRect);
@ -2368,7 +2430,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Rect);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -2377,7 +2438,6 @@ namespace PrimeTween {
tween.intParam = intParam;
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Rect);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}

View File

@ -10,11 +10,6 @@ namespace PrimeTween {
/// <summary>Returns the number of alive tweens.</summary>
/// <param name="onTarget">If specified, returns the number of running tweens on the target. Please note: if target is specified, this method call has O(n) complexity where n is the total number of running tweens.</param>
public static int GetTweensCount([CanBeNull] object onTarget = null) {
#if UNITY_EDITOR
if (Constants.warnNoInstance) {
return default;
}
#endif
var manager = PrimeTweenManager.Instance;
if (onTarget == null && manager.updateDepth == 0) {
int result = manager.tweensCount;
@ -51,7 +46,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Double);
tween.customOnValueChange = onValueChange;
tween.Setup(PrimeTweenManager.dummyTarget, ref settings.settings, _tween => {
var _onValueChange = _tween.customOnValueChange as Action<Double>;
@ -89,7 +83,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Double);
tween.customOnValueChange = onValueChange;
tween.isAdditive = isAdditive;
tween.Setup(target, ref settings.settings, _tween => {
@ -157,8 +150,9 @@ namespace PrimeTween {
var manager = PrimeTweenManager.Instance;
if (manager != null) {
if (manager.updateDepth == 0) {
manager.FixedUpdate();
manager.Update();
manager.LateUpdate();
manager.FixedUpdate();
}
// Assert.AreEqual(0, manager.tweens.Count); // fails if user's OnComplete() creates new tweens
}
@ -380,7 +374,6 @@ namespace PrimeTween {
var tween = PrimeTweenManager.fetchTween();
tween.startValue.CopyFrom(ref settings.startValue);
tween.endValue.CopyFrom(ref settings.endValue);
tween.setPropType(PropType.Int);
tween.Setup(target, ref settings.settings, setter, getter, settings.startFromCurrent, _tweenType);
return PrimeTweenManager.Animate(tween);
}
@ -432,5 +425,51 @@ namespace PrimeTween {
return result;
}
public static Tween TweenTimeScale(Sequence sequence, TweenSettings<float> settings) => AnimateTimeScale(sequence.root, settings, TweenType.TweenTimeScaleSequence);
public static Tween RotationAtSpeed([NotNull] Transform target, Vector3 endValue, float averageAngularSpeed, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> RotationAtSpeed(target, new TweenSettings<Vector3>(endValue, new TweenSettings(averageAngularSpeed, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween RotationAtSpeed([NotNull] Transform target, Vector3 endValue, float averageAngularSpeed, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> RotationAtSpeed(target, new TweenSettings<Vector3>(endValue, new TweenSettings(averageAngularSpeed, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween RotationAtSpeed([NotNull] Transform target, Vector3 startValue, Vector3 endValue, float averageAngularSpeed, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> RotationAtSpeed(target, new TweenSettings<Vector3>(startValue, endValue, new TweenSettings(averageAngularSpeed, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween RotationAtSpeed([NotNull] Transform target, Vector3 startValue, Vector3 endValue, float averageAngularSpeed, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> RotationAtSpeed(target, new TweenSettings<Vector3>(startValue, endValue, new TweenSettings(averageAngularSpeed, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
static Tween RotationAtSpeed([NotNull] Transform target, TweenSettings<Vector3> settingsVector3) {
var settings = toQuaternion(settingsVector3);
var speed = settings.settings.duration;
if (speed <= 0) {
Debug.LogError($"Invalid speed provided to the Tween.{nameof(RotationAtSpeed)}() method: {speed}.");
return default;
}
if (settings.startFromCurrent) {
settings.startFromCurrent = false;
settings.startValue = target.rotation;
}
settings.settings.duration = Extensions.CalcDistance(settings.startValue, settings.endValue) / speed;
return Rotation(target, settings);
}
public static Tween LocalRotationAtSpeed([NotNull] Transform target, Vector3 endValue, float averageAngularSpeed, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> LocalRotationAtSpeed(target, new TweenSettings<Vector3>(endValue, new TweenSettings(averageAngularSpeed, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween LocalRotationAtSpeed([NotNull] Transform target, Vector3 endValue, float averageAngularSpeed, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> LocalRotationAtSpeed(target, new TweenSettings<Vector3>(endValue, new TweenSettings(averageAngularSpeed, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween LocalRotationAtSpeed([NotNull] Transform target, Vector3 startValue, Vector3 endValue, float averageAngularSpeed, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> LocalRotationAtSpeed(target, new TweenSettings<Vector3>(startValue, endValue, new TweenSettings(averageAngularSpeed, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
public static Tween LocalRotationAtSpeed([NotNull] Transform target, Vector3 startValue, Vector3 endValue, float averageAngularSpeed, Easing ease, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false)
=> LocalRotationAtSpeed(target, new TweenSettings<Vector3>(startValue, endValue, new TweenSettings(averageAngularSpeed, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime)));
static Tween LocalRotationAtSpeed([NotNull] Transform target, TweenSettings<Vector3> settingsVector3) {
var settings = toQuaternion(settingsVector3);
var speed = settings.settings.duration;
if (speed <= 0) {
Debug.LogError($"Invalid speed provided to the Tween.{nameof(LocalRotationAtSpeed)}() method: {speed}.");
return default;
}
if (settings.startFromCurrent) {
settings.startFromCurrent = false;
settings.startValue = target.localRotation;
}
settings.settings.duration = Extensions.CalcDistance(settings.startValue, settings.endValue) / speed;
return LocalRotation(target, settings);
}
}
}

View File

@ -195,10 +195,14 @@ internal static class Utils {
return (PropType.Color, typeof(UnityEngine.UIElements.VisualElement));
case TweenType.VisualElementBackgroundColor:
return (PropType.Color, typeof(UnityEngine.UIElements.VisualElement));
case TweenType.VisualElementOpacity:
return (PropType.Float, typeof(UnityEngine.UIElements.VisualElement));
#endif
#if TEXT_MESH_PRO_INSTALLED
case TweenType.TextMaxVisibleCharacters:
return (PropType.Int, typeof(TMPro.TMP_Text));
case TweenType.TextFontSize:
return (PropType.Float, typeof(TMPro.TMP_Text));
#endif
case TweenType.None:
return (PropType.None, null);

View File

@ -1,3 +1,4 @@
using System;
using JetBrains.Annotations;
using UnityEngine;
@ -5,14 +6,7 @@ namespace PrimeTween {
/// Global PrimeTween configuration.
[PublicAPI]
public static partial class PrimeTweenConfig {
internal static PrimeTweenManager Instance {
get {
#if UNITY_EDITOR
Assert.IsFalse(Constants.noInstance, Constants.editModeWarning);
#endif
return PrimeTweenManager.Instance;
}
}
internal static PrimeTweenManager Instance => PrimeTweenManager.Instance;
/// <summary>
/// If <see cref="PrimeTweenManager"/> instance is already created, <see cref="SetTweensCapacity"/> will allocate garbage,
@ -30,11 +24,10 @@ namespace PrimeTween {
/// </example>
public static void SetTweensCapacity(int capacity) {
Assert.IsTrue(capacity >= 0);
var instance = PrimeTweenManager.Instance; // should use PrimeTweenManager.Instance because Instance property has a built-in null check
if (instance == null) {
PrimeTweenManager.customInitialCapacity = capacity;
if (PrimeTweenManager.HasInstance) {
PrimeTweenManager.Instance.SetTweensCapacity(capacity);
} else {
instance.SetTweensCapacity(capacity);
PrimeTweenManager.customInitialCapacity = capacity;
}
}
@ -48,6 +41,17 @@ namespace PrimeTween {
Instance.defaultEase = value;
}
}
public static UpdateType defaultUpdateType {
get => new UpdateType(Instance.defaultUpdateType);
set {
if (value == UpdateType.Default) {
Debug.LogError(nameof(defaultUpdateType) + " can't be " + nameof(_UpdateType.Default) + ".");
return;
}
Instance.defaultUpdateType = value.enumValue;
}
}
public static bool warnTweenOnDisabledTarget {
set => Instance.warnTweenOnDisabledTarget = value;
@ -75,5 +79,33 @@ namespace PrimeTween {
public static bool warnEndValueEqualsCurrent {
set => Instance.warnEndValueEqualsCurrent = value;
}
#if PRIME_TWEEN_EXPERIMENTAL
public
#endif
static void ManualUpdate(UpdateType updateType, float? deltaTime = null, float? unscaledDeltaTime = null) {
Instance.enabled = false;
Instance.UpdateTweens(updateType.enumValue, deltaTime, unscaledDeltaTime);
}
#if PRIME_TWEEN_EXPERIMENTAL
public
#endif
static void ManualUpdateApplyStartValues(UpdateType updateType) {
Instance.enabled = false;
Instance.ApplyStartValues(updateType.enumValue);
}
#if PRIME_TWEEN_EXPERIMENTAL
public
#endif
static void ManualInitialize() {
if (!PrimeTweenManager.HasInstance) {
PrimeTweenManager.CreateInstanceAndDontDestroy();
} else {
const string error = "'" + nameof(PrimeTweenManager) + "' is already created. Use this method only to create '" + nameof(PrimeTweenManager) + "' before '" + nameof(RuntimeInitializeLoadType) + "." + nameof(RuntimeInitializeLoadType.BeforeSceneLoad) + "'.";
Debug.LogError(error);
}
}
}
}
}

View File

@ -100,14 +100,8 @@ namespace PrimeTween {
return true;
}
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;
}
#endif
public static Sequence Create(int cycles = 1, CycleMode cycleMode = CycleMode.Restart, Ease sequenceEase = Ease.Linear, bool useUnscaledTime = false, UpdateType updateType = default) {
var tween = PrimeTweenManager.fetchTween();
tween.setPropType(PropType.Float);
if (cycleMode == CycleMode.Incremental) {
Debug.LogError($"Sequence doesn't support CycleMode.Incremental. Parameter {nameof(sequenceEase)} is applied to the sequence's 'timeline', and incrementing the 'timeline' doesn't make sense. For the same reason, {nameof(sequenceEase)} is clamped to [0:1] range.");
cycleMode = CycleMode.Restart;
@ -116,10 +110,10 @@ namespace PrimeTween {
Debug.LogError("Sequence doesn't support Ease.Custom.");
sequenceEase = Ease.Linear;
}
if (sequenceEase == Ease.Default) {
if (sequenceEase == Ease.Default) { // todo this is questionable
sequenceEase = Ease.Linear;
}
var settings = new TweenSettings(0f, sequenceEase, cycles, cycleMode, 0f, 0f, useUnscaledTime, useFixedUpdate);
var settings = new TweenSettings(0f, sequenceEase, cycles, cycleMode, 0f, 0f, useUnscaledTime, updateType);
tween.Setup(PrimeTweenManager.dummyTarget, ref settings, _ => {}, null, false, TweenType.MainSequence);
tween.intParam = emptySequenceTag;
var root = PrimeTweenManager.addTween(tween);
@ -128,11 +122,6 @@ namespace PrimeTween {
}
public static Sequence Create(Tween firstTween) {
#if UNITY_EDITOR
if (Constants.warnNoInstance) {
return default;
}
#endif
return Create().Group(firstTween);
}
@ -303,8 +292,8 @@ namespace PrimeTween {
if (tween.settings.useUnscaledTime && tween.settings.useUnscaledTime != rootTween.settings.useUnscaledTime) {
warnIgnoredChildrenSetting(nameof(TweenSettings.useUnscaledTime));
}
if (tween.settings.useFixedUpdate && tween.settings.useFixedUpdate != rootTween.settings.useFixedUpdate) {
warnIgnoredChildrenSetting(nameof(TweenSettings.useFixedUpdate));
if (tween.settings._updateType != _UpdateType.Default && tween.settings._updateType != rootTween.settings._updateType) {
warnIgnoredChildrenSetting(nameof(TweenSettings.updateType));
}
void warnIgnoredChildrenSetting(string settingName) {
Debug.LogError($"'{settingName}' was ignored after adding child animation to the Sequence. Parent Sequence controls '{settingName}' of all its children animations.\n" +
@ -614,5 +603,13 @@ namespace PrimeTween {
public override int GetHashCode() => root.GetHashCode();
public bool Equals(Sequence other) => root.Equals(other.root);
#if PRIME_TWEEN_EXPERIMENTAL
public
#endif
Sequence ResetBeforeComplete() {
root.ResetBeforeComplete();
return this;
}
}
}

View File

@ -71,7 +71,6 @@ namespace PrimeTween {
Assert.IsNotNull(onValueChange);
Assert.IsNotNull(getter);
var tween = PrimeTweenManager.fetchTween();
tween.setPropType(propType);
prepareShakeData(settings, tween);
tween.customOnValueChange = onValueChange;
var tweenSettings = settings.tweenSettings;
@ -87,7 +86,6 @@ namespace PrimeTween {
public static Tween ShakeCustom<T>([NotNull] T target, Vector3 startValue, ShakeSettings settings, [NotNull] Action<T, Vector3> onValueChange) where T : class {
Assert.IsNotNull(onValueChange);
var tween = PrimeTweenManager.fetchTween();
tween.setPropType(PropType.Vector3);
tween.startValue.CopyFrom(ref startValue);
prepareShakeData(settings, tween);
tween.customOnValueChange = onValueChange;
@ -110,7 +108,7 @@ namespace PrimeTween {
static void prepareShakeData(ShakeSettings settings, [NotNull] ReusableTween tween) {
tween.endValue.Reset(); // not used
tween.shakeData.Setup(settings);
tween.shakeData.Setup(settings, tween);
}
static Vector3 getShakeVal([NotNull] ReusableTween tween) {
@ -147,13 +145,11 @@ namespace PrimeTween {
#endif
internal struct ShakeData {
float t;
bool sign;
Vector3 from, to;
float symmetryFactor;
int falloffEaseInt;
AnimationCurve customStrengthOverTime;
Ease easeBetweenShakes;
bool isPunch;
const int disabledFalloff = -42;
internal bool isAlive => frequency != 0f;
internal Vector3 strengthPerAxis { get; private set; }
@ -161,8 +157,8 @@ namespace PrimeTween {
float prevInterpolationFactor;
int prevCyclesDone;
internal void Setup(ShakeSettings settings) {
isPunch = settings.isPunch;
internal void Setup(ShakeSettings settings, ReusableTween tween) {
tween.isPunch = settings.isPunch;
symmetryFactor = Mathf.Clamp01(1 - settings.asymmetry);
{
var _strength = settings.strength;
@ -209,14 +205,14 @@ namespace PrimeTween {
}
easeBetweenShakes = _easeBetweenShakes;
}
onCycleComplete();
onCycleComplete(tween);
}
internal void onCycleComplete() {
internal void onCycleComplete(ReusableTween tween) {
Assert.IsTrue(isAlive);
resetAfterCycle();
sign = isPunch || Random.value < 0.5f;
to = generateShakePoint();
tween.shakeSign = tween.isPunch || Random.value < 0.5f;
to = generateShakePoint(tween);
}
static int getMainAxisIndex(Vector3 strengthByAxis) {
@ -240,7 +236,7 @@ namespace PrimeTween {
int cyclesDiff = tween.getCyclesDone() - prevCyclesDone;
prevCyclesDone = tween.getCyclesDone();
if (interpolationFactor == 0f || (cyclesDiff > 0 && tween.getCyclesDone() != tween.settings.cycles)) {
onCycleComplete();
onCycleComplete(tween);
prevInterpolationFactor = interpolationFactor;
}
@ -257,15 +253,15 @@ namespace PrimeTween {
}
t += frequency * dt * frequencyFactor * getIniVelFactor();
if (t < 0f || t >= 1f) {
sign = !sign;
tween.shakeSign = !tween.shakeSign;
if (t < 0f) {
t = 1f;
to = from;
from = generateShakePoint();
from = generateShakePoint(tween);
} else {
t = 0f;
from = to;
to = generateShakePoint();
to = generateShakePoint(tween);
}
}
@ -276,13 +272,13 @@ namespace PrimeTween {
return result;
}
Vector3 generateShakePoint() {
Vector3 generateShakePoint(ReusableTween tween) {
var mainAxisIndex = getMainAxisIndex(strengthPerAxis);
Vector3 result = default;
float signFloat = sign ? 1f : -1f;
float signFloat = tween.shakeSign ? 1f : -1f;
for (int i = 0; i < 3; i++) {
var strength = strengthPerAxis[i];
if (isPunch) {
if (tween.isPunch) {
result[i] = clampBySymmetryFactor(strength * signFloat, strength, symmetryFactor);
} else {
result[i] = i == mainAxisIndex ? calcMainAxisEndVal(signFloat, strength, symmetryFactor) : calcNonMainAxisEndVal(strength, symmetryFactor);

View File

@ -46,10 +46,33 @@ namespace PrimeTween {
public float endDelay;
[Tooltip(Constants.unscaledTimeTooltip)]
public bool useUnscaledTime;
public bool useFixedUpdate;
[Obsolete("use '" + nameof(updateType) + "' instead.")]
public bool useFixedUpdate {
get => updateType == UpdateType.FixedUpdate || _useFixedUpdate;
set {
_updateType = value ? _UpdateType.FixedUpdate : _UpdateType.Update;
_useFixedUpdate = value;
}
}
[SerializeField, UnityEngine.Serialization.FormerlySerializedAs("useFixedUpdate")]
[HideInInspector]
bool _useFixedUpdate;
public UpdateType updateType {
get => _useFixedUpdate ? UpdateType.FixedUpdate : new UpdateType(_updateType);
set {
_updateType = value.enumValue;
_useFixedUpdate = value == UpdateType.FixedUpdate;
}
}
[SerializeField, Tooltip(Constants.updateTypeTooltip)]
internal _UpdateType _updateType;
[field: NonSerialized]
internal bool isPunch { get; private set; }
internal 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) {
internal ShakeSettings(Vector3 strength, float duration, float frequency, Ease? falloffEase, [CanBeNull] AnimationCurve strengthOverTime, Ease easeBetweenShakes, float asymmetryFactor, int cycles, float startDelay, float endDelay, bool useUnscaledTime, UpdateType updateType) {
this.frequency = frequency;
this.strength = strength;
this.duration = duration;
@ -69,17 +92,18 @@ namespace PrimeTween {
this.useUnscaledTime = useUnscaledTime;
asymmetry = asymmetryFactor;
isPunch = false;
this.useFixedUpdate = useFixedUpdate;
_useFixedUpdate = updateType == UpdateType.FixedUpdate;
_updateType = updateType.enumValue;
}
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)
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, UpdateType updateType = default)
// ReSharper disable once RedundantCast
: this(strength, duration, frequency, enableFalloff ? Ease.Default : (Ease?)null, null, easeBetweenShakes, asymmetryFactor, cycles, startDelay, endDelay, useUnscaledTime, useFixedUpdate) {}
: this(strength, duration, frequency, enableFalloff ? Ease.Default : (Ease?)null, null, easeBetweenShakes, asymmetryFactor, cycles, startDelay, endDelay, useUnscaledTime, updateType) {}
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) { }
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, UpdateType updateType = default)
: this(strength, duration, frequency, Ease.Custom, strengthOverTime, easeBetweenShakes, asymmetryFactor, cycles, startDelay, endDelay, useUnscaledTime, updateType) { }
internal TweenSettings tweenSettings => new TweenSettings(duration, Ease.Linear, cycles, CycleMode.Restart, startDelay, endDelay, useUnscaledTime, useFixedUpdate);
internal TweenSettings tweenSettings => new TweenSettings(duration, Ease.Linear, cycles, CycleMode.Restart, startDelay, endDelay, useUnscaledTime, updateType);
internal
#if UNITY_2020_2_OR_NEWER

View File

@ -276,7 +276,7 @@ namespace PrimeTween {
/// <summary>Adds completion callback. Please consider using <see cref="OnComplete{T}"/> to prevent a possible capture of variable into a closure.</summary>
/// <param name="warnIfTargetDestroyed">Set to 'false' to disable the error about target's destruction. Please note that the the <see cref="onComplete"/> callback will be silently ignored in the case of target's destruction. More info: https://github.com/KyryloKuzyk/PrimeTween/discussions/4</param>
public Tween OnComplete(Action onComplete, bool warnIfTargetDestroyed = true) {
public Tween OnComplete([CanBeNull] Action onComplete, bool warnIfTargetDestroyed = true) {
if (validateIsAlive()) {
tween.OnComplete(onComplete, warnIfTargetDestroyed);
}
@ -291,7 +291,7 @@ namespace PrimeTween {
/// Tween.PositionX(transform, endValue: 1.5f, duration: 1f)
/// .OnComplete(transform, _transform =&gt; Destroy(_transform.gameObject));
/// </code></example>
public Tween OnComplete<T>([NotNull] T target, Action<T> onComplete, bool warnIfTargetDestroyed = true) where T : class {
public Tween OnComplete<T>([NotNull] T target, [CanBeNull] Action<T> onComplete, bool warnIfTargetDestroyed = true) where T : class {
if (validateIsAlive()) {
tween.OnComplete(target, onComplete, warnIfTargetDestroyed);
}
@ -336,5 +336,17 @@ namespace PrimeTween {
public override int GetHashCode() => id.GetHashCode();
/// https://www.jacksondunstan.com/articles/5148
public bool Equals(Tween other) => isAlive && other.isAlive && id == other.id;
#if PRIME_TWEEN_EXPERIMENTAL
public
#else
internal
#endif
Tween ResetBeforeComplete() {
if (validateIsAlive()) {
tween.resetBeforeComplete = true;
}
return this;
}
}
}

View File

@ -32,12 +32,34 @@ namespace PrimeTween {
public float endDelay;
[Tooltip(Constants.unscaledTimeTooltip)]
public bool useUnscaledTime;
public bool useFixedUpdate;
[Obsolete("use '" + nameof(updateType) + "' instead.")]
public bool useFixedUpdate {
get => updateType == UpdateType.FixedUpdate || _useFixedUpdate;
set {
_updateType = value ? _UpdateType.FixedUpdate : _UpdateType.Update;
_useFixedUpdate = value;
}
}
[SerializeField, UnityEngine.Serialization.FormerlySerializedAs("useFixedUpdate")]
[HideInInspector]
bool _useFixedUpdate;
public UpdateType updateType {
get => _useFixedUpdate ? UpdateType.FixedUpdate : new UpdateType(_updateType);
set {
_updateType = value.enumValue;
_useFixedUpdate = value == UpdateType.FixedUpdate;
}
}
[SerializeField, Tooltip(Constants.updateTypeTooltip)]
internal _UpdateType _updateType;
[NonSerialized] internal ParametricEase parametricEase;
[NonSerialized] internal float parametricEaseStrength;
[NonSerialized] internal float parametricEasePeriod;
internal 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) {
internal TweenSettings(float duration, Ease ease, Easing? customEasing, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, UpdateType updateType = default) {
this.duration = duration;
var curve = customEasing?.curve;
if (ease == Ease.Custom && customEasing?.parametricEase == ParametricEase.None) {
@ -56,7 +78,8 @@ namespace PrimeTween {
parametricEase = customEasing?.parametricEase ?? ParametricEase.None;
parametricEaseStrength = customEasing?.parametricEaseStrength ?? float.NaN;
parametricEasePeriod = customEasing?.parametricEasePeriod ?? float.NaN;
this.useFixedUpdate = useFixedUpdate;
_useFixedUpdate = updateType == UpdateType.FixedUpdate;
_updateType = updateType.enumValue;
}
#if PRIME_TWEEN_DOTWEEN_ADAPTER
@ -68,12 +91,12 @@ namespace PrimeTween {
}
#endif
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, Ease ease = Ease.Default, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, UpdateType updateType = default)
: this(duration, ease, null, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, updateType) {
}
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) {
public TweenSettings(float duration, Easing easing, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, UpdateType updateType = default)
: this(duration, easing.ease, easing, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, updateType) {
}
internal static void setCyclesTo1If0(ref int cycles) {
@ -94,7 +117,7 @@ namespace PrimeTween {
parametricEase = other.parametricEase;
parametricEaseStrength = other.parametricEaseStrength;
parametricEasePeriod = other.parametricEasePeriod;
useFixedUpdate = other.useFixedUpdate;
updateType = other.updateType;
}
internal const float minDuration = 0.0001f;
@ -173,6 +196,49 @@ namespace PrimeTween {
}
}
[Serializable]
public struct UpdateType : IEquatable<UpdateType> {
/// Uses <see cref="PrimeTweenConfig.defaultUpdateType"/> to control the default Unity's event function, which updates the animation.
public static readonly UpdateType Default = new UpdateType(_UpdateType.Default);
/// Updates the animation in MonoBehaviour.Update().<br/>
/// If the animation has 'startValue' and doesn't have a start delay, the 'startValue' is applied in <see cref="PrimeTweenManager.LateUpdate"/>.
/// This ensures the animation is rendered at the 'startValue' in the same frame it's created.
public static readonly UpdateType Update = new UpdateType(_UpdateType.Update);
/// Updates the animation in MonoBehaviour.LateUpdate().<br/>
/// If the animation has 'startValue' and doesn't have a start delay, the 'startValue' is applied in <see cref="PrimeTweenManager.LateUpdate"/>.
/// This ensures the animation is rendered at the 'startValue' in the same frame it's created.
public static readonly UpdateType LateUpdate = new UpdateType(_UpdateType.LateUpdate);
/// Updates the animation in MonoBehaviour.FixedUpdate().<br/>
/// Unlike Update and LateUpdate animations, FixedUpdate animations don't apply the 'startValue' before the first frame is rendered.
/// They receive their first update in the first FixedUpdate() after creation.
public static readonly UpdateType FixedUpdate = new UpdateType(_UpdateType.FixedUpdate);
[SerializeField]
internal _UpdateType enumValue;
internal UpdateType(_UpdateType enumValue) { this.enumValue = enumValue; }
[Obsolete("use 'UpdateType.FixedUpdate' instead.")]
public static implicit operator UpdateType(bool isFixedUpdate) => isFixedUpdate ? FixedUpdate : Update;
public static bool operator==(UpdateType lhs, UpdateType rhs) => lhs.enumValue == rhs.enumValue;
public static bool operator !=(UpdateType lhs, UpdateType rhs) => lhs.enumValue != rhs.enumValue;
public bool Equals(UpdateType other) => enumValue == other.enumValue;
public override bool Equals(object obj) => obj is UpdateType other && Equals(other);
public override int GetHashCode() => ((int)enumValue).GetHashCode();
}
internal enum _UpdateType : byte {
[Tooltip("Uses 'PrimeTweenConfig.defaultUpdateType' to control the default Unity's event function, which updates the animation.")]
Default,
[Tooltip("Updates the animation in MonoBehaviour.Update().\n\n" +
"If the animation has 'startValue' and doesn't have a start delay, the 'startValue' is applied in 'PrimeTweenManager.LateUpdate'. This ensures the animation is rendered at the 'startValue' in the same frame it's created.")]
Update,
[Tooltip("Updates the animation in MonoBehaviour.LateUpdate().\n\n" +
"If the animation has 'startValue' and doesn't have a start delay, the 'startValue' is applied in 'PrimeTweenManager.LateUpdate'. This ensures the animation is rendered at the 'startValue' in the same frame it's created.")]
LateUpdate,
[Tooltip("Updates the animation in 'MonoBehaviour.FixedUpdate()'.\n\n" +
"Unlike Update and LateUpdate animations, FixedUpdate animations don't apply the 'startValue' before the first frame is rendered. They receive their first update in the first FixedUpdate() after creation.")]
FixedUpdate
}
/// <summary>The standard animation easing types. Different easing curves produce a different animation 'feeling'.<br/>
/// 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.</summary>

View File

@ -38,20 +38,20 @@ namespace PrimeTween {
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, bool useFixedUpdate = false)
: this(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, useFixedUpdate)) {
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, UpdateType updateType = default)
: this(endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, updateType)) {
}
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 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, UpdateType updateType = default)
: this(startValue, endValue, new TweenSettings(duration, ease, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, updateType)) {
}
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 endValue, float duration, Easing customEase, int cycles = 1, CycleMode cycleMode = CycleMode.Restart, float startDelay = 0, float endDelay = 0, bool useUnscaledTime = false, UpdateType updateType = default)
: this(endValue, new TweenSettings(duration, customEase, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, updateType)) {
}
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)) {
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, UpdateType updateType = default)
: this(startValue, endValue, new TweenSettings(duration, customEase, cycles, cycleMode, startDelay, endDelay, useUnscaledTime, updateType)) {
}
/// <summary>Use this method to choose the direction of an animation based on the '<paramref name="toEndValue"/>' parameter.</summary>

View File

@ -4,7 +4,7 @@ using UnityEngine;
public class DestructionOrderTest : MonoBehaviour {
void OnDestroy() {
print($"PrimeTweenManager.Instance == null: {PrimeTweenManager.Instance == null}");
print($"PrimeTweenManager.HasInstance: {PrimeTweenManager.HasInstance}, PrimeTweenManager._instance != null:{PrimeTweenManager._instance != null}");
Tween.StopAll(transform);
Tween.CompleteAll(transform);
Tween.Custom(0, 1, 1, delegate {});

View File

@ -2,23 +2,73 @@
// ReSharper disable NotAccessedField.Local
// ReSharper disable UnusedMember.Local
// ReSharper disable PartialTypeWithSinglePart
using System;
using PrimeTween;
using UnityEngine;
using Assert = NUnit.Framework.Assert;
using AssertionException = UnityEngine.Assertions.AssertionException;
[ExecuteInEditMode]
public partial class EditModeTest : MonoBehaviour {
[SerializeField] TweenSettings _settings = new TweenSettings(1, AnimationCurve.Linear(0, 0, 1, 1));
Tween tween = test();
Sequence sequence = Sequence.Create();
static Tween test() {
Assert.IsTrue(Constants.noInstance, "This test is designed only for Edit mode.");
PrimeTweenConfig.SetTweensCapacity(10);
Assert.Throws<AssertionException>(() => PrimeTweenConfig.warnZeroDuration = false);
[SerializeField] TweenSettings _settings = CreateSettings();
static TweenSettings CreateSettings() {
TweenSettings res = default;
if (!PrimeTweenManager.HasInstance) {
ExpectConstructorException(() => res = new TweenSettings(1, AnimationCurve.Linear(0, 0, 1, 1)));
}
return res;
}
Tween tween = TestConstructor();
static Tween TestConstructor() {
if (PrimeTweenManager.HasInstance) {
if (PrimeTweenManager.Instance.tweensCount > 0) {
ExpectConstructorException(() => Tween.StopAll());
}
return TestRegular();
}
ExpectConstructorException(() => Sequence.Create());
ExpectConstructorException(() => PrimeTweenConfig.SetTweensCapacity(PrimeTweenManager.Instance.currentPoolCapacity + 1));
ExpectConstructorException(() => PrimeTweenConfig.warnZeroDuration = !PrimeTweenConfig.warnZeroDuration);
ExpectConstructorException(() => Tween.GlobalTimeScale(1f, 0.1f));
ExpectConstructorException(() => Tween.GetTweensCount());
ExpectConstructorException(() => {
Sequence.Create()
.ChainCallback(() => {})
.InsertCallback(0f, delegate {})
.Group(StartTween())
.Chain(StartTween())
.Insert(0f, Sequence.Create())
.Insert(0, StartTween());
});
ExpectConstructorException(() => Tween.Delay(new object(), 1f, () => {}));
ExpectConstructorException(() => Tween.Delay(new object(), 1f, _ => {}));
ExpectConstructorException(() => Tween.Delay(1f, () => { }));
ExpectConstructorException(() => Tween.Custom(0, 1, 1, delegate {}));
return default;
}
static void ExpectConstructorException(Action action) {
try {
action();
// Assert.Fail(nameof(action) + " should throw when called from constructor."); // calling Unity API is allowed from constructor when Editor is opening for the first time, so this assertion is commented out
} catch (Exception e) {
string message = e.Message;
Assert.IsTrue(message.Contains("is not allowed to be called from a MonoBehaviour constructor"), message);
}
}
static void test() {
Tween.StopAll();
Tween.GlobalTimeScale(0.5f, 0.1f);
TestRegular();
}
static Tween TestRegular() {
PrimeTweenConfig.SetTweensCapacity(PrimeTweenManager.Instance.currentPoolCapacity + 1);
Assert.DoesNotThrow(() => PrimeTweenConfig.warnZeroDuration = false);
PrimeTweenConfig.warnEndValueEqualsCurrent = false;
Tween.GlobalTimeScale(1f, 0.1f);
PrimeTweenConfig.warnEndValueEqualsCurrent = true;
Tween.GetTweensCount();
Sequence.Create()
.ChainCallback(() => {})
@ -27,7 +77,7 @@ public partial class EditModeTest : MonoBehaviour {
.Chain(StartTween())
.Insert(0f, Sequence.Create())
.Insert(0, StartTween());
Tween.Delay(new object(), 1f, () => { });
Tween.Delay(new object(), 1f, () => {});
Tween.Delay(new object(), 1f, _ => {});
Tween.Delay(1f, () => { });
return Tween.Custom(0, 1, 1, delegate {});
@ -45,8 +95,8 @@ public partial class EditModeTest : MonoBehaviour {
/*[UnityEditor.InitializeOnLoad]
public partial class EditModeTest {
static EditModeTest() => test();
EditModeTest() => test();
static EditModeTest() => TestConstructor();
EditModeTest() => TestConstructor();
[RuntimeInitializeOnLoadMethod]
static void runtimeInitOnLoad() => test();

View File

@ -45,7 +45,8 @@ public partial class Tests {
static void expectOnCompleteIgnored() => LogAssert.Expect(LogType.Error, new Regex(Constants.onCompleteCallbackIgnored));
static void expectTweenWasStoppedBecauseException() {
LogAssert.Expect(LogType.Error, new Regex("Tween was stopped because of exception"));
LogAssert.Expect(LogType.Exception, new Regex(".*"));
LogAssert.Expect(LogType.Warning, new Regex("Tween was stopped because of exception"));
}
}
#endif

View File

@ -357,11 +357,11 @@ public partial class Tests {
LogAssert.Expect(LogType.Error, new Regex("'isPaused' was ignored after adding"));
LogAssert.Expect(LogType.Error, new Regex("'timeScale' was ignored after adding"));
LogAssert.Expect(LogType.Error, new Regex("'useUnscaledTime' was ignored after adding"));
LogAssert.Expect(LogType.Error, new Regex("'useFixedUpdate' was ignored after adding"));
LogAssert.Expect(LogType.Error, new Regex("'updateType' was ignored after adding"));
}
static Sequence createSequenceWithNonDefaultSettings() {
var s2 = Sequence.Create(useUnscaledTime: true, useFixedUpdate: true);
var s2 = Sequence.Create(useUnscaledTime: true, updateType: UpdateType.FixedUpdate);
s2.isPaused = true;
s2.timeScale = 0.5f;
return s2;

View File

@ -204,7 +204,7 @@ public partial class Tests {
[UnityTest]
public IEnumerator FramePacing() {
Tween.StopAll();
const int fps = 240;
const int fps = 120;
Application.targetFrameRate = fps;
QualitySettings.vSyncCount = 0;
Assert.AreEqual(fps, Application.targetFrameRate);
@ -428,7 +428,7 @@ public partial class Tests {
[Test]
public async Task AwaitExceptions() {
LogAssert.Expect(LogType.Error, new Regex("Tween was stopped because of exception"));
expectTweenWasStoppedBecauseException();
await Tween.Custom(this, 0f, 1f, 1f, delegate {
throw new Exception();
});
@ -1019,6 +1019,7 @@ public partial class Tests {
static void checkTweensAreOrdered() {
checkOrder(PrimeTweenManager.Instance.tweens);
checkOrder(PrimeTweenManager.Instance.lateUpdateTweens);
checkOrder(PrimeTweenManager.Instance.fixedUpdateTweens);
void checkOrder(List<ReusableTween> tweens) {
Assert.IsTrue(tweens.OrderBy(_ => _.id).SequenceEqual(tweens));
@ -2703,5 +2704,96 @@ public partial class Tests {
Assert.AreEqual(startValue, transform.position);
seq.Stop();
}
[Test]
public void UpdateTypes() {
var iniUpdateType = PrimeTweenConfig.defaultUpdateType;
{
Tween.StopAll();
var t = Tween.Position(transform, default, new TweenSettings(1f, updateType: default)).tween;
Assert.AreEqual(_UpdateType.Update, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.tweens.Single());
}
{
Tween.StopAll();
var t = Tween.Position(transform, default, new TweenSettings(1f, updateType: UpdateType.Update)).tween;
Assert.AreEqual(_UpdateType.Update, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.tweens.Single());
}
{
Tween.StopAll();
var t = Tween.Position(transform, default, new TweenSettings(1f, updateType: UpdateType.LateUpdate)).tween;
Assert.AreEqual(_UpdateType.LateUpdate, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.lateUpdateTweens.Single());
}
{
Tween.StopAll();
var t = Tween.Position(transform, default, new TweenSettings(1f, updateType: UpdateType.FixedUpdate)).tween;
Assert.AreEqual(_UpdateType.FixedUpdate, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.fixedUpdateTweens.Single());
}
{
Tween.StopAll();
UpdateType updateType = default;
updateType.enumValue = (_UpdateType)100;
Tween.Position(transform, default, new TweenSettings(1f, updateType: updateType));
LogAssert.Expect(LogType.Error, "Invalid update type: 100");
}
{
Tween.StopAll();
var t = Tween.ShakeLocalPosition(transform, new ShakeSettings(Vector3.one, updateType: default)).tween;
Assert.AreEqual(_UpdateType.Update, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.tweens.Single());
}
{
Tween.StopAll();
var t = Tween.ShakeLocalPosition(transform, new ShakeSettings(Vector3.one, updateType: UpdateType.Update)).tween;
Assert.AreEqual(_UpdateType.Update, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.tweens.Single());
}
{
Tween.StopAll();
var t = Tween.ShakeLocalPosition(transform, new ShakeSettings(Vector3.one, updateType: UpdateType.LateUpdate)).tween;
Assert.AreEqual(_UpdateType.LateUpdate, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.lateUpdateTweens.Single());
}
{
Tween.StopAll();
var t = Tween.ShakeLocalPosition(transform, new ShakeSettings(Vector3.one, updateType: UpdateType.FixedUpdate)).tween;
Assert.AreEqual(_UpdateType.FixedUpdate, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.fixedUpdateTweens.Single());
}
{
Tween.StopAll();
PrimeTweenConfig.defaultUpdateType = UpdateType.FixedUpdate;
var t = Tween.Position(transform, default, new TweenSettings(1f, updateType: default)).tween;
Assert.AreEqual(_UpdateType.FixedUpdate, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.fixedUpdateTweens.Single());
}
{
Tween.StopAll();
PrimeTweenConfig.defaultUpdateType = UpdateType.LateUpdate;
var t = Tween.Position(transform, default, new TweenSettings(1f, updateType: default)).tween;
Assert.AreEqual(_UpdateType.LateUpdate, t.settings._updateType);
Assert.AreEqual(t, PrimeTweenManager.Instance.lateUpdateTweens.Single());
}
Tween.StopAll();
#pragma warning disable CS0618 // Type or member is obsolete
Assert.AreEqual(false, new TweenSettings(1f, updateType: default).useFixedUpdate);
Assert.AreEqual(false, new TweenSettings(1f, updateType: UpdateType.Update).useFixedUpdate);
Assert.AreEqual(false, new TweenSettings(1f, updateType: UpdateType.LateUpdate).useFixedUpdate);
Assert.AreEqual(true, new TweenSettings(1f, updateType: UpdateType.FixedUpdate).useFixedUpdate);
#pragma warning restore CS0618 // Type or member is obsolete
Assert.AreEqual(UpdateType.Default, new TweenSettings(1f, updateType: default).updateType);
PrimeTweenConfig.defaultUpdateType = UpdateType.Update;
Assert.AreEqual(UpdateType.Default, new TweenSettings(1f, updateType: default).updateType);
PrimeTweenConfig.defaultUpdateType = UpdateType.LateUpdate;
Assert.AreEqual(UpdateType.Update, new TweenSettings(1f, updateType: UpdateType.Update).updateType);
Assert.AreEqual(UpdateType.FixedUpdate, new TweenSettings(1f, updateType: UpdateType.FixedUpdate).updateType);
PrimeTweenConfig.defaultUpdateType = iniUpdateType;
}
}
#endif

View File

@ -1,3 +1,27 @@
## [1.3.1] - 2025-04-27
### Added
- Add Edit mode support, so animations can be played in Editor without entering the Play mode. https://github.com/KyryloKuzyk/PrimeTween/discussions/62
- PrimeTween can now be installed via Unity Package Manager.
- Demo project: add 'Play Animation' button to Inspector to preview animations in Edit mode without entering Play mode.
- Add the experimental `ResetAfterComplete()` method to reset animations to the initial value before completion. https://github.com/KyryloKuzyk/PrimeTween/discussions/153
- Add the experimental `PrimeTweenConfig.ManualInitialize()` to initialize PrimeTween before `RuntimeInitializeLoadType.BeforeSceneLoad`. https://github.com/KyryloKuzyk/PrimeTween/issues/150
- Add `Tween.VisualElementOpacity()` method to animate the `VisualElement.style.opacity` property. Also, extend `Tween.Color/Alpha` methods to also work with VisualElement.
### Changed
- Passing `null` to `OnComplete()` is now allowed and no longer results in an error. https://github.com/KyryloKuzyk/PrimeTween/discussions/164
### Fixed
- Fixed: PrimeTween doesn't work after scripts are recompiled while playing in Editor when the 'Recompile And Continue Playing' setting is enabled.
## [1.3.0] - 2025-04-04
### Added
- Add support for updating animations in LateUpdate with the help of the new 'UpdateType updateType' parameter. The available options are Update, LateUpdate, and FixedUpdate. https://github.com/KyryloKuzyk/PrimeTween/issues/138
- Add 'Vector3' overloads to RotationAtSpeed() and LocalRotationAtSpeed() methods.
- Add 'TextFontSize()' to animate 'TextMeshPro.fontSize' property. https://github.com/KyryloKuzyk/PrimeTween/discussions/129
- Add a message to the exception thrown when an invalid ease is provided to `StandardEasing.Evaluate()`. https://github.com/KyryloKuzyk/PrimeTween/issues/151
### Changed
- Change 'bool useFixedUpdate' to 'UpdateType updateType'. The new version comes with an automatic script updater. Please back up your project before updating.
- Make the exception stack trace clickable when custom tween throws an exception. https://github.com/KyryloKuzyk/PrimeTween/issues/119
- Prevent allocations in development builds when PRIME_TWEEN_SAFETY_CHECKS is enabled.
## [1.2.2] - 2024-12-05
### Fixed
- Fixed: TextMeshPro animations are not available in Unity 2023. Bug report: https://discussions.unity.com/t/primetween-high-performance-animations-and-sequences/926420/365

View File

@ -1,10 +1,12 @@
PrimeTween is licensed under the Asset Store EULA:
https://unity3d.com/legal/as_terms
It's **allowed**:
- Use PrimeTween and modify its source code in **free and commercial** products distributed in binary format (apps and games).
- Use PrimeTween in derivative (free and commercial) projects (templates, libraries, GitHub projects) that depend on PrimeTween, but only if PrimeTween is installed as an [**NPM package**](https://github.com/KyryloKuzyk/PrimeTween#install-via-unity-package-manager-upm).
In short, it's allowed to use and modify PrimeTween in products distributed in binary format (apps and games).
But it's not allowed to redistribute it or create derivative libraries based on PrimeTween.
It's **not allowed**:
- Distribute PrimeTween's source code and tarball (.tgz) archive inside derivative products. To build templates or libraries that depend on PrimeTween, use the NPM installation method instead.
- Distribute, resell, or claim PrimeTween's ownership even if modified.
- Resell PrimeTween by itself or as part of another library or asset pack.
To create other libraries that depend on PrimeTween, please refer to this forum post:
https://forum.unity.com/threads/1479609/page-4#post-9587461
**In short**: you can use PrimeTween in your Unity projects, including commercial ones, but you cant repackage or resell PrimeTween itself.
Copyright: Kyrylo Kuzyk 2023
Copyright: Kyrylo Kuzyk 2023

View File

@ -1,7 +1,7 @@
{
"name": "com.alicizax.kyrylokuzyk.primetween",
"name": "com.kyrylokuzyk.primetween",
"displayName": "PrimeTween",
"version": "1.2.2",
"version": "1.3.1",
"unity": "2018.4",
"author": {
"name": "Kyrylo Kuzyk"
@ -32,9 +32,14 @@
"Animate"
],
"documentationUrl": "https://github.com/KyryloKuzyk/PrimeTween",
"changelogUrl": "https://github.com/KyryloKuzyk/PrimeTween/blob/main/changelog.md",
"_upm": {
"changelog": "### Added\n- Add Edit mode support, so animations can be played in Editor without entering the Play mode. https://github.com/KyryloKuzyk/PrimeTween/discussions/62\n- PrimeTween can now be installed via Unity Package Manager.\n- Demo project: add 'Play Animation' button to Inspector to preview animations in Edit mode without entering Play mode.\n- Add the experimental `ResetAfterComplete()` method to reset animations to the initial value before completion. https://github.com/KyryloKuzyk/PrimeTween/discussions/153\n- Add the experimental `PrimeTweenConfig.ManualInitialize()` to initialize PrimeTween before `RuntimeInitializeLoadType.BeforeSceneLoad`. https://github.com/KyryloKuzyk/PrimeTween/issues/150\n- Add `Tween.VisualElementOpacity()` method to animate the `VisualElement.style.opacity` property. Also, extend `Tween.Color/Alpha` methods to also work with VisualElement.\n\n### Changed\n- Passing `null` to `OnComplete()` is now allowed and no longer results in an error. https://github.com/KyryloKuzyk/PrimeTween/discussions/164\n\n### Fixed\n- Fixed: PrimeTween doesn't work after scripts are recompiled while playing in Editor when the 'Recompile And Continue Playing' setting is enabled.\n"
},
"repository": {
"url": "git@bitbucket.org:stampedegames/primetween.git",
"type": "git",
"revision": "e9627419e109fdbce619483122dc0f83a529b673"
}
"revision": "27eb75868b655a65d221346120940366e3e9127c"
},
"_fingerprint": "ce784e871a4e69c031f1e786977e82a7"
}

View File

@ -6,11 +6,12 @@ PrimeTween is a high-performance, **allocation-free** animation library for Unit
[**Performance comparison with other tween libraries.**](https://github.com/KyryloKuzyk/PrimeTween/discussions/10)
**[Asset Store](https://assetstore.unity.com/packages/slug/252960)** | **[Forum](https://forum.unity.com/threads/1479609/)** | **[FAQ](https://github.com/KyryloKuzyk/PrimeTween/discussions)** | **[YouTube](https://www.youtube.com/watch?v=MuMKwxOzc3M)**
**[Asset Store](https://assetstore.unity.com/packages/slug/252960)** | **[Forum](https://discussions.unity.com/t/primetween-high-performance-animations-and-sequences/926420)** | **[FAQ](https://github.com/KyryloKuzyk/PrimeTween/discussions)** | **[YouTube](https://www.youtube.com/watch?v=MuMKwxOzc3M)**
Table of Contents
---
- [Getting started](#getting-started)
* [Installation](#installation)
* [Animations](#animations)
* [Shakes](#shakes)
* [Callbacks](#callbacks)
@ -28,7 +29,8 @@ Table of Contents
+ [OnUpdate](#onupdate)
+ [Speed-based animations](#speed-based-animations)
+ [Custom easing](#custom-easing)
+ [FixedUpdate](#fixedupdate)
+ [LateUpdate/FixedUpdate](#lateupdatefixedupdate)
+ [Install via Unity Package Manager (UPM)](#install-via-unity-package-manager-upm)
- [Zero allocations with delegates](#zero-allocations-with-delegates)
- [Debugging tweens](#debugging-tweens)
- [Migrating from DOTween to PrimeTween](#migrating-from-dotween-to-primetween)
@ -42,7 +44,8 @@ Getting started
---
### Installation
Import PrimeTween from [Asset Store](https://assetstore.unity.com/packages/slug/252960).
Import PrimeTween from [Asset Store](https://assetstore.unity.com/packages/slug/252960).
Optional: install via Unity [Package Manager](#install-via-unity-package-manager-upm) (UPM).
### Animations
Without further ado, let's jump straight to the code!
@ -356,19 +359,46 @@ Available parametric eases:
- Easing.BounceExact(float amplitude): customizes the exact amplitude of the first bounce in meters/angles.
- Easing.Elastic(float strength, float period = 0.3f): customizes the strength and oscillation period of Ease.OutElastic.
### FixedUpdate
Use `useFixedUpdate` parameter to update an animation in the FixedUpdate().
### LateUpdate/FixedUpdate
Use `updateType` parameter to chose which Unity even function will update the animation. The available options are Update, LateUpdate, and FixedUpdate.
```csharp
// Use TweenSettings or TweenSettings<T> struct to pass the 'useFixedUpdate' parameter to static 'Tween.' methods
Tween.PositionX(transform, endValue: 10f, new TweenSettings(duration: 1f, useFixedUpdate: true));
// Use TweenSettings or TweenSettings<T> struct to pass the 'updateType' parameter to static 'Tween.' methods
Tween.PositionX(transform, endValue: 10f, new TweenSettings(duration: 1f, updateType: UpdateType.LateUpdate));
var tweenSettingsFloat = new TweenSettings<float>(endValue: 10f, duration: 1f, useFixedUpdate: true);
var tweenSettingsFloat = new TweenSettings<float>(endValue: 10f, duration: 1f, updateType: UpdateType.FixedUpdate);
Tween.PositionX(transform, tweenSettingsFloat);
// To update the Sequence in FixedUpdate(), pass the 'useFixedUpdate' parameter to Sequence.Create()
Sequence.Create(useFixedUpdate: true);
// To update the Sequence in FixedUpdate(), pass the 'updateType' parameter to Sequence.Create()
Sequence.Create(updateType: UpdateType.FixedUpdate);
```
### Install via Unity Package Manager (UPM)
The Package Manager installation method allows to include PrimeTween in derivative (free and commercial) projects (templates, libraries, GitHub repositories). For more info, see the [license](https://github.com/KyryloKuzyk/PrimeTween?tab=License-1-ov-file).
This installation method also helps to clean the project structure.
- Open 'Edit / Project Settings / Package Manager'.
- Add a new Scoped Registry with Name: `npm` URL: `https://registry.npmjs.org` Scope(s): `com.kyrylokuzyk`.
- Go to 'Window / Package Manager / Packages / My Registries'.
- Install the PrimeTween package.
Or modify the `Packages/manifest.json' file manually:
```json
{
"dependencies": {
"com.kyrylokuzyk.primetween": "1.3.1",
...
},
"scopedRegistries": [
{
"name": "npm",
"url": "https://registry.npmjs.org/",
"scopes": [
"com.kyrylokuzyk"
]
}
]
}
```
Zero allocations with delegates
---
C# delegates is a powerful language feature essential for game development. It gives us the ability to receive callbacks and pass methods to other methods. But when delegates are used in hot code paths carelessly, they can create [performance issues](https://www.jacksondunstan.com/articles/3765).
@ -528,9 +558,9 @@ sequence.SetLoops(2, LoopType.Yoyo) --> Sequence.Create(cycles: 2, CycleMode.Yo
tween.SetUpdate(true) --> Tween.Position(..., useUnscaledTime: true)
sequence.SetUpdate(true) --> Sequence.Create(..., useUnscaledTime: true)
tween.SetUpdate(UpdateType.Fixed) --> Tween.Position(..., new TweenSettings(1f, useFixedUpdate: true))
sequence.SetUpdate(UpdateType.Fixed) --> Sequence.Create(useFixedUpdate: true)
--> github.com/KyryloKuzyk/PrimeTween#fixedupdate
tween.SetUpdate(UpdateType.Fixed) --> Tween.Position(..., new TweenSettings(1f, updateType: UpdateType.Fixed))
sequence.SetUpdate(UpdateType.Fixed) --> Sequence.Create(updateType: UpdateType.Fixed)
--> github.com/KyryloKuzyk/PrimeTween#lateupdatefixedupdate
tween.Kill(false) --> tween.Stop()
tween.Kill(true) --> tween.Complete()
@ -558,19 +588,18 @@ await tween.AsyncWaitForCompletion() --> await tween
await sequence.AsyncWaitForCompletion() --> await sequence
transform.DOMoveX(to, 1).From(from) --> Tween.PositionX(transform, from, to, 1)
tween.From(from, setImmediately: true) --> manually set the animated value to 'from': forum.unity.com/threads/1479609/page-4#post-9515827
tween.SetDelay(1f).OnStart(callback) --> Tween.Delay(1, callback).Chain(tween)
sequence.OnStart(callback) --> sequence.ChainCallback(callback) // at the beginning of the sequence
trans.DOMove(pos, speed).SetSpeedBased() --> Tween.PositionAtSpeed(trans, pos, speed)
textMeshPro.DOText(...) --> forum.unity.com/threads/1479609/page-4#post-9529051
textMeshPro.DOText(...) --> discussions.unity.com/t/926420/159
--> or see TypewriterAnimatorExample.cs in Demo
text.DOCounter() --> forum.unity.com/threads/1479609/page-2#post-9387887
transform.DOJump() --> forum.unity.com/threads/1479609/#post-9226566
transform.DOPath() --> forum.unity.com/threads/1479609/page-4#post-9522451
transform.DOLookAt() --> forum.unity.com/threads/1479609/page-4#post-9557785
text.DOCounter() --> discussions.unity.com/t/926420/80
transform.DOJump() --> discussions.unity.com/t/926420/4
transform.DOPath() --> discussions.unity.com/t/926420/158
transform.DOLookAt() --> discussions.unity.com/t/926420/189
tween.SetId() --> github.com/KyryloKuzyk/PrimeTween/discussions/26#discussioncomment-7700985
target.DOBlendable___(...) --> Tween.___Additive(target, ...) // experimental
--> github.com/KyryloKuzyk/PrimeTween/discussions/55
@ -578,7 +607,7 @@ target.DOBlendable___(...) --> Tween.___Additive(target, ...) // experiment
Support
---
Join the discussion on [Unity Forum](https://forum.unity.com/threads/1479609/).
Join the discussion on [Unity Discussions](https://discussions.unity.com/t/primetween-high-performance-animations-and-sequences/926420).
Please submit bug reports [here](https://github.com/KyryloKuzyk/PrimeTween/issues).
Submit your questions and feature requests [here](https://github.com/KyryloKuzyk/PrimeTween/discussions).
If you want to contact me privately, please drop me an email: kuzykkirill@gmail.com