Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

PrimeTween · High-Performance Animations and Sequences

Discussion in 'Assets and Asset Store' started by KyryloKuzyk, Aug 15, 2023.

  1. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Download on Asset Store for FREE

    PrimeTween is a high-performance, allocation-free animation library for Unity.

    Animate anything with just one line of code, tweak all animation properties directly from the Inspector, and create complex animation sequences. No runtime memory allocations, ever.

    Highlights
    • Zero-allocation animations, shakes, delays, and sequences.
    • Unity Package Manager (UPM) ready.
    • Simple and consistent API. Animate anything with one line of code.
    • Full source code is available.
    • Reliable, covered by tests, and ready for production.



    Features
    • Easing functions: choose a standard easing function or provide a custom animation curve for animations.
    • Shakes: shake the camera, Transform, or any arbitrary property.
    • Callbacks: execute custom code when animations complete.
    • Sequences: combine animations, delays, and callbacks into complex sequences.
    • Inspector integration: all animation properties are tweakable from the Inspector. Tweak start and end values, duration, cycles, etc. Choose a standard easing function or provide a custom animation curve. All without re-compiling the code.
    • Coroutines: wait for animations and sequences in coroutines.
    • Async/await: await animations and sequences in async methods.
    • Cycles: repeat, play animations back and forth like a yoyo, or cycle them indefinitely.
    • Frame perfect: 1-second animation always takes 60 frames on 60 FPS.

    Documentation | FAQ | Support

    Download on Asset Store for FREE
     
    Last edited: Sep 11, 2023
  2. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Hey everyone!

    Just like with the AR Foundation Remote plugin, I created PrimeTween because I had a personal need for such a tool. And I also saw some fun interesting technical problems I would like to solve.

    Speaking with fellow devs, I realized that I'm not the only one who is seeking an alternative to DOTween. DOTween is cool and a de-facto industry standard, but the community needed a refreshment.

    So I thought: if I'm making a brand new tween library, why not make it high-performance and allocation-free? Just because it's fun to solve hard technical problems.

    Eight months and hundreds of hours of work later I'm proud to announce PrimeTween! I spent so much time designing a simple and consistent API and I'm eager to hear the feedback.


    PrimeTween blog posts:

    Performance comparison with other tween libraries.
    Why PrimeTween doesn't use extension methods?

    Is it safe to destroy an object with a running tween on it?
     
    Last edited: Sep 15, 2023
  3. Landcaster

    Landcaster

    Joined:
    Nov 11, 2015
    Posts:
    12
    Hello, looks great! Thank you for asset, but I have 3 questions:
    1) What about DoJump?
    2) What about loops, loop types
    3) Do you have analogue of setUpdates(true)?
     
    Denis1122 and KyryloKuzyk like this.
  4. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    There is no built-in DOJump() equivalent in PrimeTween but it can be easily added with this Jump() method:
    Code (CSharp):
    1. public static class PrimeTweenExtensions {
    2.     public static Sequence Jump([NotNull] Transform target, Vector3 endValue, float duration, float height, int numJumps = 1) {
    3.         Assert.IsTrue(height > 0f);
    4.         Assert.IsTrue(numJumps >= 1, nameof(numJumps) + "should be >= 1.");
    5.         var jumpsSequence = Sequence.Create();
    6.         var iniPosY = target.position.y;
    7.         var deltaJump = (endValue.y - iniPosY) / numJumps;
    8.         var jumpDuration = duration / (numJumps * 2);
    9.         for (int i = 0; i < numJumps; i++) {
    10.             var from = iniPosY + i * deltaJump;
    11.             var to = iniPosY + (i + 1) * deltaJump;
    12.             jumpsSequence.Chain(Tween.PositionY(target, Mathf.Max(from, to) + height, jumpDuration, Ease.OutQuad))
    13.                 .Chain(Tween.PositionY(target, to, jumpDuration, Ease.InQuad));
    14.         }
    15.         var result = Sequence.Create()
    16.             .Group(jumpsSequence);
    17.         if (!Mathf.Approximately(target.position.x, endValue.x)) {
    18.             result.Group(Tween.PositionX(target, endValue.x, duration, Ease.Linear));
    19.         }
    20.         if (!Mathf.Approximately(target.position.z, endValue.z)) {
    21.             result.Group(Tween.PositionZ(target, endValue.z, duration, Ease.Linear));
    22.         }
    23.         return result;
    24.     }
    25. }
    I should warn that the above code may not be suitable for every 'jump' use case. For example, the above example will work correctly only if the Jump() method is the only method that animates the object's position. Any other animation (shake, for example) will conflict with the jump animation.

    Also, one may want to modify the above code to change ease functions or to adapt it to non-vertical gravity. Because there is no one-size-fits-all solution, I decided not to include Jump() as a built-in method. The above Jump() example is simply a collection of tweens, and I believe it's better for PrimeTween users to understand that by seeing the implementation and modifying it to their own needs.


    PrimeTween supports loops and loop types. But I gave them a better naming in my opinion - I called them cycles and CycleMode. Because 'loop' is something that 'loops', i.e. finishes at the same place where it starts. But this is not always the case with animations.
    Cycles documentation.

    Here is the usage of cycles in PrimeTween:
    Code (CSharp):
    1. // Animate position.y to 10 and back (like a yo-yo)
    2. Tween.PositionY(transform, endValue: 10, duration: 1, Ease.OutQuad, cycles: 2, CycleMode.Yoyo);

    Of course! In PrimeTween you simply pass the 'useUnscaledTime' parameter to 'Tween.' methods, like this:
    Code (CSharp):
    1. Tween.PositionY(transform, endValue: 10, duration: 1, useUnscaledTime: true);
    PrimeTween doesn't use extension methods for a lot of reasons, but this deserves a separate blog post.
     
    Last edited: Jan 25, 2024
    akuno likes this.
  5. Tr0sT

    Tr0sT

    Joined:
    Aug 19, 2013
    Posts:
    3
    It's free, but not open source - why? it's so much easier to install and update packages from the package manager than from the Asset Store...
     
    KyryloKuzyk likes this.
  6. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    The biggest reason for me was discoverability. Being popular on Asset Store is much more valuable than being popular on Git Hub. Asset Store is a one-stop shop for the majority of Unity developers, so being popular on Asset Store is key to product adoption.

    Secondly, being open-source doesn't automatically mean that users can install and update the package from the Package Manager. From my side, it requires setting up scoped registries, and they have some limitations. And from the user's perspective, they will have to manually add the scoped registry to the Package Manager. While I see great value in this as a developer, I still believe that installing via Asset Store is much simpler and more accessible for an average user.

    Thirdly, while PrimeTween is free right now, I would like to have the opportunity to become paid in the future. It doesn't mean it will happen any time soon, but I would like to have such a choice available.

    All the above reasons don't mean PrimeTween is closed-source. After you download it on Asset Store, you have full source code with no nasty DLLs. And it also installs via Package Manager to keep users' projects structured.
     
    boscohyun likes this.
  7. Tr0sT

    Tr0sT

    Joined:
    Aug 19, 2013
    Posts:
    3
    You don't need to do a scope registry, it's possible to install the package directly from git (https://github.com/Cysharp/UniTask#install-via-git-url). The package is installed as a local package anyway, so it's easy to change the local archive to the git link. And you don't have to remove it from the asset store.

    Can't argue against the third point. But you will also be able to remove it from the github later. It's up to you :)
     
    KyryloKuzyk likes this.
  8. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Yes, this is a very handy method of installation. But, unfortunately, it doesn't support updates via Package Manager. To update such a package you have to re-download it somehow. One method I know is to go to the packages cache and delete the folder, which will trigger the re-download. But this method feels a bit hacky to me.
    Once you have at least one fork, you can't remove the project from Git Hub entirely. And even if you could do so, it would be very unethical toward all other contributors and other forks maintainers.

    Edit: GitHub discussion thread on this topic: https://github.com/KyryloKuzyk/PrimeTween/discussions/9
     
    Last edited: Oct 25, 2023
  9. Landcaster

    Landcaster

    Joined:
    Nov 11, 2015
    Posts:
    12
    Thank you for your answer, don't forget DoLocalJump too please)
    What if I want to make an item DoJump or DoMove but target object moving? In DoTween I usually make item child of target and then run DoLocaJump, but item rotates with target until the end of animation.
     
    N8W1nD and KyryloKuzyk like this.
  10. Landcaster

    Landcaster

    Joined:
    Nov 11, 2015
    Posts:
    12
    Code (CSharp):
    1. public static Sequence Jump(Transform target, Vector3 endValue, float jumpPower, int numJumps, float duration) {
    2.     Assert.IsTrue(numJumps >= 1);
    3.     var jumpsSequence = Sequence.Create();
    4.     var iniPosY = target.position.y;
    5.     var deltaJump = (endValue.y - iniPosY) / numJumps;
    6.     var jumpDuration = duration / (numJumps * 2);
    7.     for (int i = 0; i < numJumps; i++) {
    8.         var from = iniPosY + i * deltaJump;
    9.         var to = (i + 1) * deltaJump;
    10.         jumpsSequence.Chain(Tween.PositionY(target, Mathf.Max(from, to) + jumpPower, jumpDuration, Ease.OutQuad))
    11.             .Chain(Tween.PositionY(target, to, jumpDuration, Ease.InQuad));
    12.     }
    13.     return Tween.PositionX(target, endValue.x, duration, Ease.Linear)
    14.         .Group(Tween.PositionZ(target, endValue.z, duration, Ease.Linear))
    15.         .Group(jumpsSequence);
    16. }

    How I can add OnComplete callback here?
     
    KyryloKuzyk likes this.
  11. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Method
    Jump()
    returns a Sequence, so you can simply use the
    .ChainCallback(() => print("Done!"))
    method to chain any number of callbacks to the Sequence.

    Sequences in PrimeTween don't currently support
    OnComplete()
    to make the API simpler. But
    ChainCallback()
    works exactly the same as
    OnComplete()
    for sequences with one cycle. I'll consider adding
    OnComplete()
    if there will be a demand for it.
     
  12. Landcaster

    Landcaster

    Joined:
    Nov 11, 2015
    Posts:
    12
    Thank you very much, all works!
     
    KyryloKuzyk likes this.
  13. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    705
    I like the concept of this library very much. It's very tidy.

    I've been using unity-tween before. I think that has a slightly better (i.e. concise?) syntax thanks to its use of extension methods (some people like them, some hate them), but it's nowhere as consistent, nor robust, as PrimeTween! I also normally dislike fluent syntax (only exception really is Tweens), so it's nice to not have to bother with it anymore. :) Of course use of Fluent Syntax for "Chain" is totally good and sensible.

    Side note: I don't think DOTween was ever a contender, I shipped a few games with it and frankly, DOTween is pretty bad.

    Anyway - very cool job, Kyrylo! I'll use your lib to test some new UI stuff I am prototyping. It might be one of the best I have ever had the pleasure using!

    And already need to praise the excellent use of existing interfaces/superclasses, such as Graphic. I can Tween all my SVG Image stuff right out of the box!

    Small tip - instead of making TweenAwaiter "obsolete" to "hide" it from IDE suggestions (spoiler: it wasn't hidden, it was just at the bottom of the list ;)), you can use System.ComponentModel.EditorBrowsableAttribute, for example:
    [EditorBrowsable(EditorBrowsableState.Never)]
     
    Last edited: Aug 19, 2023
    KyryloKuzyk likes this.
  14. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    705
    Question, I've been trying to replicate this (contrived) example from DOTween Documentation:
    Code (DOTween):
    1. var mySequence = DOTween.Sequence();
    2. mySequence
    3.     .Append(transform.DOMoveX(45, 1))
    4.     .Append(transform.DORotate(new Vector3(0,180,0), 1))
    5.     .PrependInterval(1)
    6.     .Insert(0, transform.DOScale(new Vector3(3,3,3), mySequence.Duration()));
    I noticed that perhaps Sequence.Create could use another overload that takes a Sequence, but maybe you have a much cleaner way to do this self-referential grouping? This i is also not 100% equivalent, but I could start the 2nd sequence with the delay instead.
    Code (PrimeTween):
    1. var mySequence = Tween.Delay(1)
    2.     .Chain(Tween.PositionX(target: transform, endValue: 45, duration: 1))
    3.     .Chain(Tween.Rotation(target: transform, endValue: Quaternion.Euler(x: 0, y: 0, z: 180), duration: 1));
    4.  
    5. mySequence = Sequence.Create()
    6.    .Chain(mySequence)
    7.    .Group(Tween.LocalScale(target: transform, endValue: Vector3.one * 3, duration: mySequence.durationTotal));
    Overall, I noticed it's more verbose (if more explicit) than libraries that rely on extension methods. I'm looking forward to that blog post. :cool:
    Code (Proposed):
    1. var mySequence = Tween.Delay(1)
    2.     .Chain(transform.PositionX(endValue: 45, duration: 1))
    3.     .Chain(transform.Rotation(endValue: Quaternion.Euler(x: 0, y: 0, z: 180), duration: 1));
    4.  
    5. Sequence.Create()
    6.     .Chain(other: mySequence)
    7.     .Group(transform.LocalScale(endValue: Vector3.one*3, duration: mySequence.durationTotal));
    But there's always static imports. (which I personally hate, however)
    Code (StaticImport):
    1. using static Tween; //at top of file
    2. var seq = Delay(1)
    3.     .Chain(PositionX(target: transform, endValue: 45, duration: 1))
    4.     .Chain(Rotation(target: transform, endValue: Quaternion.Euler(x: 0, y: 0, z: 180), duration: 1));
    5.  
    6. Sequence.Create()
    7.     .Chain(other: mySequence)
    8.     .Group(LocalScale(target: transform, endValue: Vector3.one * 3, duration: seq.durationTotal));
     
    Last edited: Aug 20, 2023
    KyryloKuzyk likes this.
  15. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    @Thygrrr Huge thanks! Designing a simple and consistent API was one of my main priorities and I'm glad to hear that it pays off. I spent almost as much time designing and iterating on API as on actually writing the code :)

    I marked TweenAwaiter with an Obsolete attribute for the same reason - to make API's surface smaller and decrease the complexity for users. Huge thanks for suggesting the EditorBrowsable attribute! Although it doesn't work with my Rider IDE (maybe there is a setting I don't know about), I'll add this attribute for Visual Studio users.

    Code (CSharp):
    1. var mySequence = DOTween.Sequence();
    2. mySequence
    3.     .Append(transform.DOMoveX(45, 1))
    4.     .Append(transform.DORotate(new Vector3(0,180,0), 1))
    5.     .PrependInterval(1)
    6.     .Insert(0, transform.DOScale(new Vector3(3,3,3), mySequence.Duration()));
    Yeah, this example is quite contrived, I agree :)
    I would rewrite it to something like this in PrimeTween. I grouped the 'moveAndRotate' to a scale tween, so they run in parallel to one another. And I used the 'startDelay' parameter instead of Tween.Delay:
    Code (CSharp):
    1. var moveAndRotate = Tween.PositionX(transform, 45, 1, startDelay: 1)
    2.     .Chain(Tween.Rotation(transform, Quaternion.Euler(x: 0, y: 0, z: 180), 1));
    3. var sequence = Tween.LocalScale(transform, Vector3.one * 3, moveAndRotate.duration).Group(moveAndRotate);

    Yes, in general, PrimeTween is more verbose. I believe that readability is more important than terseness. Although, there are cases where PrimeTween can be even shorter than using extension methods:
    Code (CSharp):
    1. transform.DOMove(targetPos, duration).SetEase(Ease.InOutElastic).SetLoops(-1, LoopType.Yoyo);
    2. Tween.Position(transform, targetPos, duration, Ease.InOutElastic, cycles, CycleMode.Yoyo);
    But for those who can't live without extension methods, PrimeTween can mimic DOTween's API with the help of adapter. This way, users can continue using extension methods with all the performance and consistency benefits of PrimeTween.
     
    Last edited: Aug 20, 2023
  16. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Thygrrr likes this.
  17. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    705
    Awesome, thanks for taking the time to explain!
    (and I pretty much agree, despite my apparent preference)

    Small bug report - this is more of a "Unity" problem, but certain domain reload scenarios cause tween singleton to fail. (in the latest 2022.3.7f1 LTS)

    (it happens occasionally when I add a script, then hit F5 to recompile (I think CTRL-R is the default). I have Asset Refresh off. It's difficult to reproduce. I'll try to extract a more meaningful stack trace next if I get the chance, I was busy with another bug in that session.)


    upload_2023-8-21_11-56-11.png
     
  18. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    I would very much appreciate a full stack trace!
    Can you please also check that you're not calling PrimeTween's API in these cases?
    - in Edit mode (when a scene is not playing)
    - MonoBehaviour's constructor
    - class static constructor
    - field constructor
    - a method marked with RuntimeInitializeOnLoadMethod or InitializeOnLoadMethod attribute

    Using PrimeTween in these cases is not valid because a lot of Unity APIs don't work either here. Although I should log a friendly error instead of throwing a null-ref.
     
  19. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    705
    I checked, finally found it. It is my mistake, I had a (rarely executed) code path where a leftover Tween was started in a function that was called by an OnValidate method.

    Maybe a warning in the getter for `PrimeTweenManager.instance` for `!Application.isPlaying` could be useful. (or the manager just refusing to exist/instantiate in edit mode, might be cleaner)
     
    KyryloKuzyk likes this.
  20. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    @Thygrrr Thank you for confirming this! I'll make sure to print a warning when PrimeTween is used in Edit mode.
     
  21. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Version 1.0.4 is out!

    Now PrimeTween's API will log a warning instead of throwing null ref when used in Edit mode. Thanks to @Thygrrr for reporting the issue!

    And I've added a new experimental feature called 'Additive tweens'. Additive tweens are equivalent to 'blendable' tweens. In my opinion, this naming is better because it better represents the behavior: such tweens 'accumulate' or in other words 'add up to one another'.

    For example, you have a coins indicator, and you wish to animate it when a player receives coins. Using Tween.LocalScaleAdditive() will let you scale the coins indicator more than 1.5x if the player collects several coins at once:
    Code (CSharp):
    1. Tween.LocalScaleAdditive(coinsIndicator, deltaValue: Vector3.one * 0.5f, duration: 0.2f, cycles: 2, cycleMode: CycleMode.Rewind);
    It's currently in the experimental stage because I'm not sure about the API and it may change before the official release. To enable the experimental support, add the
    PRIME_TWEEN_EXPERIMENTAL
    define to the
    ProjectSettings/Player/Script Compilation
    .

    More info:
    https://forum.unity.com/threads/pri...ons-and-sequences.1479609/page-4#post-9540721
     
    Last edited: Mar 19, 2024
    Wokarol likes this.
  22. ThatsMildlyRaven

    ThatsMildlyRaven

    Joined:
    Aug 9, 2013
    Posts:
    4
    The asset seems great, but it has one deal breaker for me right now (unless this feature is there and I'm just missing how to use it). In my project I frequently use DOTweens custom timescale feature, letting you change the timescale to whatever value you want on individual tweens. Is there an analogue to this in PrimeTween? Thanks.
     
    KyryloKuzyk likes this.
  23. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    @ThatsMildlyRaven Yes, you're right, there is currently no support for the Custom Timescale feature. But adding it would be very easy.

    Can you please provide a usage example for this feature so I can understand it better before starting to work on it?

    As I imagine it, you would want this feature to implement some kind of local slo-mo effect, right? For example, a magic object that can slow down objects as they approach it. Am I thinking in the right direction? If yes, then I assume you would also want to animate the custom timescale of those objects to create a smooth timescale transition.

    Another common use case for a custom timescale is UI elements, as you want them to continue working as usual during slow-mo or when the game is on pause. In this case, such UI elements can be animated with the help of 'useUnscaledTime = ture' parameter that you pass to PrimeTween methods.
     
    Last edited: Aug 25, 2023
    ThatsMildlyRaven likes this.
  24. ThatsMildlyRaven

    ThatsMildlyRaven

    Joined:
    Aug 9, 2013
    Posts:
    4
    Yes, the local slo-mo effect with a smooth timescale transition is exactly my use case. I use it to make sure any tweens on enemy characters are also slowed by the correct amount when they're hit with an ability that slows their movement.
     
    KyryloKuzyk likes this.
  25. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    ThatsMildlyRaven likes this.
  26. ThatsMildlyRaven

    ThatsMildlyRaven

    Joined:
    Aug 9, 2013
    Posts:
    4
    Thanks, really appreciate it!
     
  27. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Version 1.0.5 is out!
    ## [1.0.5] - 2022-08-29
    ### Added
    - Experimental: add tween.timeScale; Tween.TweenTimeScale(); Tween.GlobalTimeScale().
    ### Fixed
    - Fixed: onComplete callback should not be called if Tween.OnComplete<T>(_target_, ...) has been destroyed.
    - Fixed: 'additive' tweens don't work correctly.

    @ThatsMildlyRaven In this new version I added custom timeScale support. Now you can change tween's timeScale via the
    tween.timeScale
    property. To animate the tween's timeScale smoothly, use the
    Tween.TweenTimeScale(Tween tween, ...)
    method.

    I also added the
    Tween.GlobalTimeScale()
    method to animate the global Unity's Time.timeScale property.

    Custom timeScale is currently an experimental feature that can enabled by adding the PRIME_TWEEN_EXPERIMENTAL define to the ProjectSettings/Player/Script Compilation.
     
    Last edited: Aug 29, 2023
    ThatsMildlyRaven likes this.
  28. ThatsMildlyRaven

    ThatsMildlyRaven

    Joined:
    Aug 9, 2013
    Posts:
    4
    Working great so far! Thanks again, I'll make sure to leave a positive review.
     
    KyryloKuzyk likes this.
  29. issamB1975

    issamB1975

    Joined:
    Apr 4, 2019
    Posts:
    3
    Hi,

    any chance to port this into unreal please ?

    i am in love <3
     
    KyryloKuzyk likes this.
  30. BBIT-SOLUTIONS

    BBIT-SOLUTIONS

    Joined:
    Feb 21, 2013
    Posts:
    39
    That looks awesome! Would be interesting to know the differences and/or benefits compared to LeanTween.
     
    KyryloKuzyk likes this.
  31. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Hi @issamB1975
    Making a good tween library requires deep engine knowledge, which I don't currently have with Unreal. Although the idea of having a multi-engine library is a thrilling one.
     
  32. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Thank you! This question deserves several big blog posts (which I'm currently working on), but quick takeaways would be the following.

    1. GC Allocations.
    LeanTween always allocates a small amount of GC garbage every time you start an animation. These small GCAllocs can accumulate over time and result in micro-freezes and frame-rate drops.
    PrimeTween is designed with zero allocations in mind. That is, it never allocates memory after the initialization and doesn't put pressure on garbage collection.

    2. Runtime performance.
    PrimeTween is several times faster in terms of runtime performance. Please see the performance comparison article.

    3. Inspector integration.
    In PrimeTween it's possible to tweak all animation properties from the Inspector without the need to recompile the code after every change. This also gives creative freedom to non-coders in your team.

    4. Animation types.
    In contrast to LeanTween, PrimeTween can animate UI, Camera properties, Material properties, UI Toolkit components, and AudioSource properties out of the box. Also, PrimeTween can do screen shakes and can shake any transform or any property.

    5. Package Manager support.
    PrimeTweem installs via Package Manager, which makes the users' projects clean and structured.

    6. Other reasons.
    I can name a lot more reasons, but I feel like my opinion is biased here for obvious reasons :)
    - PrimeTween's documentation is simpler and more accessible.
    - PrimeTween is fully covered by autotests and extensively tested with the latest Unity versions, while LeanTween received its last update two years ago.
    - PrimeTween has a structured API that you can learn even without the documentation by typing 'PrimeTween.' and exploring a few simple concepts. While LeanTween doesn't even have a namespace.
    - PrimeTween doesn't use extension methods, which makes the API simpler and the user's code more readable.

    To be totally fair, PrimeTween currently doesn't have these features, but it's totally possible to add them in the future:
    - The ability to follow one transform with another.
    - Moving along Spline or Bezier Curve.
     
    Last edited: Sep 30, 2023
    xaldin-76, akuno and hopeful like this.
  33. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Version 1.0.8 is out!
    ## [1.0.8] - 2022-09-06
    ### Added
    - Tween.Delay(), tween.OnComplete(), and sequence.ChainDelay() methods now accept the 'warnIfTargetDestroyed' parameter to control whether PrimeTween should log the error about tween's target destruction. More info: https://github.com/KyryloKuzyk/PrimeTween/discussions/4
    ### Fixed
    - Fix compilation error with PRIME_TWEEN_DOTWEEN_ADAPTER.

    ## [1.0.7] - 2022-09-01
    ### Added
    - Add 'UI Toolkit' support (com.unity.modules.uielements). All animations from the [ITransitionAnimations](https://docs.unity3d.com/2023.2/Doc...ments.Experimental.ITransitionAnimations.html) interface are supported.
    - Experimental: add Tween.PositionOutBounce/LocalPositionOutBounce() methods. These methods add the ability to fine-tune the Ease.OutBounce by specifying the exact bounce amplitude, number of bounces, and bounce stiffness.
    - Log error when properties of dead tweens and sequences are used.
    ### Changed
    - Rename 'Tween.LocalScale()' methods to 'Tween.Scale()'.
    - Rename 'IsAlive' to 'isAlive'.
    - Rename 'IsPaused' to 'isPaused'.

    The most notable addition is the 'warnIfTargetDestroyed' that you can specify for tween.OnComplete(), Tween.Delay(), and sequence.ChainDelay() methods.

    In PrimeTween it's safe to destroy objects or unload a scene when tweens are running. But what to do in the case of OnComplete() callback? Object destruction interrupts the animation, so animation never 'completes', and OnComplete() callback will never be called. This can lead to unexpected bugs in your code, so PrimeTween has a simple safety mechanism - it logs an error in this case. And if you're not doing anything important in the OnComplete() callback, you can disable the error with 'warnIfTargetDestroyed: false'. Learn more in this article:

    Is it safe to destroy an object with a running tween on it?
     
    Last edited: Sep 7, 2023
    akuno likes this.
  34. BBIT-SOLUTIONS

    BBIT-SOLUTIONS

    Joined:
    Feb 21, 2013
    Posts:
    39
    @KyryloKuzyk Thank you very much, for this extensive answer :)

    LeanTween is currently my go to tweening system, so that's why I'm a bit "biased" :-D It's just mainly because DOTween is indeed ways too heavy, and I'm really happy with it, but it has a few downsides. So definitely will try out PrimeTween on my next project.

    But here a few points, especially when you plan to make blog-posts. (Btw: I'm a happy owner and user of both your AR Foundation Remote tools, so i know, that stuff you develop has absolute high quality).

    You should emphasize these points:
    • PrimeTween is actively updated (where LeanTween's latest one is nearly 2 years old)
    • It seems that your way of Sequencing (grouping and chaining) will be ways easier and intuitive and directly "implemented" into the package.
    • The integration with Coroutines and Async/Awaits sounds also very cool
    But i think you should be a bit careful about:
    • Allocations and Performance stuff, because i think here especially LeanTween also has benefits compared to DOTween. So maybe you are right, but for the blog-post / marketing you maybe should use real Profiler data from LeanTween instead of considering DOTween ones ;-)
    • Animation Types: LeanTween also can tween everything, by using the value() method. That looks pretty the same like your Custom() method, so not a big difference here i guess. But e.g. CameraShaking with one line sounds awesome!

    So all in all, you have so much good ideas and your documentation is indeed already in this early state ways better than LeanTweens. Can't wait to see more updates and use it soon!
     
    KyryloKuzyk likes this.
  35. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    I'm please to hear that, thank you :)

    Yes, of course, the only correct way is to actually measure!

    Yes, I agree, you can animate all of that with LeanTween too. The main difference is the ease of use because it's faster to type and easier to read with PrimeTween:
    Code (CSharp):
    1. Tween.UIAnchoredPosition(rectTransform, Vector2.one, duration);
    2. LeanTween.value(gameObject, callOnUpdate: val => rectTransform.anchoredPosition = val, rectTransform.anchoredPosition, Vector2.one, duration);
    Also, LeanTween.value() always allocates a small amount of GCAlloc because of delegate allocation in the
    callOnUpdate
    .
     
    Last edited: Sep 11, 2023
    BBIT-SOLUTIONS likes this.
  36. akuno

    akuno

    Joined:
    Dec 14, 2015
    Posts:
    88
    KyryloKuzyk likes this.
  37. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    I've finally finished with an article comparing PrimeTween's performance with DOTween. I tried to be as fair as possible and compared the most common use cases.

    Correct performance measurement is hard, so I used the official Performance testing API. All the tests can be replicated in a matter of minutes by cloning the Benchmarks repo.

    I welcome everyone to study the numbers in the article, but quick summary is:
    • PrimeTween is about 5x faster in starting animations, which means that starting an animation has almost no overhead in comparison to actually running it. After all, there is little sense in processing animations quickly if you can't start them quickly in the first place.
    • PrimeTween is 1.4 - 2.0x faster in the most common use cases in terms of runtime performance. This is the performance gain that occurs every frame.
    • PrimeTween never allocates garbage.

    Performance Comparison with DOTween
     
    Last edited: Sep 13, 2023
    Johannski, hopeful and MUGIK like this.
  38. RisingSunStudios

    RisingSunStudios

    Joined:
    May 23, 2018
    Posts:
    34
    Hey thanks so much for this - DOTween allocations has made me look at alternatives and this looks perfect except for one feature that I believe may be missing: speed-based tweens. DOTween allows you to specify a speed rather than a duration and I'm using that extensively in my current project.

    Is there a similar feature in PrimeTween that I may have overlooked? I've enabled the DOTween adapter and the SetSpeedBased() method appears to be missing on a Tweener.
     
    KyryloKuzyk likes this.
  39. RisingSunStudios

    RisingSunStudios

    Joined:
    May 23, 2018
    Posts:
    34
    KyryloKuzyk likes this.
  40. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    @RisingSunStudios Thank you!
    I'm currently working on this feature and will release it soon. Am I right that this feature is only needed for position and rotation animations? Or do you also use the speed-based setting for other animations too?
     
  41. RisingSunStudios

    RisingSunStudios

    Joined:
    May 23, 2018
    Posts:
    34
    That is great to hear! So far it has only been required for position and rotations
     
    KyryloKuzyk likes this.
  42. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Version 1.0.10 is out!
    ## [1.0.10] - 2022-09-13
    ### Added
    - Add Tween.XxxAtSpeed() methods to create animations based on speed instead of duration.
    - Add Tween.GetTweensCount() method.
    ### Changed
    - Improve performance.

    ## [1.0.9] - 2022-09-08
    ### Changed
    - Tween.StopAll(null) and Tween.CompleteAll(null) now immediately clean the internal tweens list, so PrimeTweenConfig.SetTweensCapacity() can be called immediately after that.
    ### Fixed
    - Fixed: Tween.ShakeLocalRotation() doesn't work correctly.
    @RisingSunStudios This version adds speed-based tweens with the help of the new Tween.XxxAtSpeed() methods. Usage example:
    Code (CSharp):
    1. Tween.PositionAtSpeed(transform, endValue, speed);
    To keep an API simple and consistent, there is no internal concept of speed-based tweens in PrimeTween. Instead, all speed-based position and rotation methods calculate the duration based on startValue, endValue, and speed with the help of a simple formula:
    duration = distance(startValue, endValue) / speed
    .

    If startValue is not provided, such tweens will use the current animated value as the startValue. This is needed to calculate the duration immediately at the moment of creation and to ensure the correctness of tween.duration and sequence.duration APIs. To chain speed-based tweens correctly, it's necessary to specify the startValue explicitly for all subsequent speed-based tweens:
    Code (CSharp):
    1. Tween.LocalPositionAtSpeed(transform, endValue: midPos, speed)
    2.     // Set 'startValue: midPos' to continue the movement from the 'midPos' instead of the initial 'transform.position'
    3.     .Chain(Tween.LocalPositionAtSpeed(transform, startValue: midPos, endValue: endPos, speed));
     
    Last edited: Sep 13, 2023
    RisingSunStudios, yuliyF and akuno like this.
  43. thomas_superFASTgames

    thomas_superFASTgames

    Joined:
    Jul 15, 2017
    Posts:
    23
    Apologies if I missed it, but is there a way to contribute for your efforts? My only complaint is that it is free!
     
    KyryloKuzyk likes this.
  44. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    Thank you, I'm delighted to hear that! Just created a co-fi account, you can find the link on GitHub page.
     
  45. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    I just finished the blog post comparing the most popular tween engines for Unity, including LeanTween.

    LeanTween actually has quite a decent performance in comparison to DOTween. In terms of runtime performance, it's slightly faster in some cases, and slightly slower in others. But LeanTween starts animations twice as quickly as DOTween. Also, LeanTween is 2.5x better with GC allocations: 292B vs. 734B for animations, and 146B vs. 584B for delays.

    However, LeanTween has a much worse performance of O(n^2) in starting sequences. It may be unnoticeable if your project uses a couple of hundreds of sequences, but you should keep the exponential complexity in mind.

    Performance comparison with other tween libraries.
     
    Last edited: Sep 15, 2023
  46. RisingSunStudios

    RisingSunStudios

    Joined:
    May 23, 2018
    Posts:
    34
    That's brilliant, thank you! It would be nice to include similar for the individual move and rotation components, PositionXAtSpeed etc but completely understand if you don't want to add the bloat - it's pretty simple to calculate ourselves :)
     
    yuliyF and KyryloKuzyk like this.
  47. BBIT-SOLUTIONS

    BBIT-SOLUTIONS

    Joined:
    Feb 21, 2013
    Posts:
    39
    Awesome comparison! Thank you :)
     
    KyryloKuzyk likes this.
  48. yuliyF

    yuliyF

    Joined:
    Nov 15, 2012
    Posts:
    197
    Hi, can you add function like this:
    LeanTween.isTweening(gameObject)

    checking object has tween or not. Sometimes useful))
     
    KyryloKuzyk likes this.
  49. KyryloKuzyk

    KyryloKuzyk

    Joined:
    Nov 4, 2013
    Posts:
    1,150
    There is no direct equivalent because tweens in PrimeTween operate on particular components instead of GameObjects.
    I added the
    Tween.GetTweensCount(object onTarget)
    method in version 1.0.10 that can be used to determine if any tween is running on a target. But instead of passing the gameObject, you should specify the exact component you're interested in. In the case of transform it would be:
    Code (CSharp):
    1. static bool IsTweening(Transform transform) {
    2.     return Tween.GetTweensCount(transform) > 0;
    3. }
    I should mention that the Tween.GetTweensCount() method is relatively slow because it loops through all currently running tweens. For a better performance, I would recommend storing the tween reference and checking the tween.isAlive property.
     
    yuliyF and DragonCoder like this.
  50. yuliyF

    yuliyF

    Joined:
    Nov 15, 2012
    Posts:
    197
    How can I do infinity rotation on loader as example?
    Tween.Rotation(LoadIco.transform, endValue: Quaternion.Euler(0, 0, 360), 1f, Ease.Default, cycles: -1, cycleMode: CycleMode.Rewind);

    doesn't work (
    And then stop it:
    LoadIco.SetActive(false);
    - is ok?
     
    KyryloKuzyk likes this.