Skip to content

AnnulusGames/MagicTween

Repository files navigation

Magic Tween

Extremely fast, GC-free and customizable tween library implemented with Unity ECS

license

日本語版READMEはこちら

Overview

Magic Tween is a high-performance tweening library implemented with Unity Entity Component System (ECS).

In addition to powerful tweening functionality compatible with traditional components, it also offers even higher-performance APIs for ECS.

Note

MagicTween is currently provided as an experimental library. If you need a higher performance and stable tween library, I recommend using LitMotion. LitMotion is built with minimal dependencies and a modern design, offering extremely high performance and full functionality.

Table of Contents

Features

  • High-performance tweening library implemented in ECS.
  • No GC allocation when creating Tween. (Excluding some special tweens)
  • Add extension methods compatible with many components.
  • Transform-optimized fast tween support
  • Tween anything with Tween.To.
  • Apply various settings using method chaining.
  • Create complex animations with Sequences.
  • Add processing via callbacks.
  • Supports Tween waits with coroutines
  • Support for tweening TextMesh Pro.
  • Support for conversion to Observable with UniRx.
  • Support for async/await with UniTask.
  • Create your own type of tween using a custom TweenPlugin.
  • Even higher-performance implementation for ECS.

Performance

When tweening the float values of a regular class with Tween.To(), it operates 2 to 5 times faster than other libraries. When tweening the float values within ECS components using Tween.Entity.To(), even faster performance can be achieved.

Furthermore, there are no additional GC allocations generated for each tween creation (excluding tweens involving strings).

Enabling MAGICTWEEN_ENABLE_TRANSFORM_JOBS allows you to create tweens specialized for Transforms. This dramatically improves performance when tweening a large number of Transforms.

For more details on performance, please refer to the README in the MagicTween.Benchmarks project.

Samples

The MagicTween.Samples project includes several samples implemented using Magic Tween. For more details, please refer to the README in the project.

Setup

Requirement

  • Unity 2022.1 or higher
  • Entities 1.0.0 or higher
  • Burst 1.8.8 or higher

Install

  1. Open the Package Manager from Window > Package Manager
  2. "+" button > Add package from git URL
  3. Enter the following to install
https://github.com/AnnulusGames/MagicTween.git?path=/MagicTween/Assets/MagicTween

or open Packages/manifest.json and add the following to the dependencies block.

{
    "dependencies": {
        "com.annulusgames.magic-tween": "https://github.com/AnnulusGames/MagicTween.git?path=/MagicTween/Assets/MagicTween"
    }
}

Migration Guides

Magic Tween is an actively developed library, and there is a possibility of breaking changes with each version. For information on migrating from previous versions, please refer to the Migration Guides.

Basic Usage

By introducing Magic Tween, numerous extension methods for creating tweens on traditional Unity components are added. Below is an example of animating the position of a Transform using these extension methods:

// Move from the current position to (1, 2, 3) over 5 seconds
transform.TweenPosition(
    new Vector3(1f, 2f, 3f), // Target value
    5f // Duration of the change
);

// Move from (0, 0, 0) to (1, 2, 3) over 5 seconds
transform.TweenPosition(
    new Vector3(0f, 0f, 0f), // Starting value
    new Vector3(1f, 2f, 3f), // Target value
    5f // Duration of the change
);

Warning Do not play multiple tweens on the same parameter simultaneously. This can lead to unexpected behavior due to overlapping value changes. (Tweens on different axes, such as TweenPositionX and TweenPositionY, will work.)

Warning Creating tweens in the editor is not supported.

Extension Methods

Magic Tween provides extension methods for most components included in Unity. These extension methods allow for more concise and optimized code compared to Tween.To(). It's recommended to use extension methods when available.

You can find a list of available extension methods here (a wiki is currently being created).

Classification

The extension methods added for tweening are classified into several categories:

Method Name Description
Tween... Creates a tween for a specific field/property.
Punch... Creates a tween to vibrate the value of a specific field/property.
Shake... Creates a tween to randomly vibrate the value of a specific field/property.
Set... Adds settings to customize the behavior of the tween.
On... Adds callbacks at specific timings of the tween.
Log... Outputs information about the tween and its callbacks to the console.
WaitFor... Waits for the tween in a coroutine.
AwaitFor... Awaits the tween using async/await. Requires UniTask to use this extension method.

Tweening Custom Value

You can animate custom value using the Tween.To() method:

float foo;

float endValue = 10f;
float duration = 2f;

Tween.To(
    () => foo,
    x => foo = x,
    endValue,
    duration
);

If you want to use only values without defining variables, you can use Tween.FromTo():

float startValue = 0f;
float endValue = 10f;
float duration = 2f;

Tween.FromTo(
    x => Debug.Log("current value: " + x),
    startValue,
    endValue,
    duration
);

You can create tweens that follow a curve passing through multiple points using Tween.Path():

Vector3 foo;

Vector3[] points;
float duration = 2f;

Tween.Path(
    () => foo,
    x => foo = x,
    points,
    duration
);

Avoiding Allocations

The To and FromTo methods mentioned above cause allocations because they capture external variables. To reduce unnecessary allocations when performing tweens on an object, you can specify the target object as the first argument to avoid lambda expression allocations:

// A class with a field named 'foo'
ExampleClass target;

float endValue = 10f;
float duration = 2f;

// Pass the object as the first argument to avoid allocations
Tween.To(
    target,
    obj => obj.foo,
    (obj, x) => obj.foo = x,
    endValue,
    duration
);

Tween Control

In general, created tweens play and are destroyed automatically, so there's no need for explicit control. However, there are situations where manual control is necessary, such as when dealing with tweens that loop indefinitely. In such cases, you can control tweens through the Tween struct:

Tween tween = transform.TweenPosition(Vector3.up, 2f);

// Start/resume the tween
tween.Play();

// Pause the tween
tween.Pause();

// Restart the tween from the beginning
tween.Restart();

// Complete the tween
tween.Complete();

// Kill the tween
tween.Kill();

// Complete and kill the tween
tween.CompleteAndKill();

You can also perform operations on all playing tweens collectively. If you specify an ID as an argument, you can target only the tweens with matching IDs:

// Kill all playing tweens
Tween.KillAll();

// Complete all tweens with an ID of 1
Tween.CompleteAll(1);

// Pause all tweens with an ID of "Alpha"
Tween.PauseAll("Alpha");

Getting Tween Information

You can check if a tween is currently active using IsActive(). When performing operations on tweens, it's a good practice to check for activity, especially when there's a possibility that the tween may not be active:

// Kill the tween if it's active
if (tween.IsActive()) tween.Kill();

You can also retrieve the duration of a tween using GetDuration():

float duration = tween.GetDuration();

Adding Settings

You can customize the behavior of a tween using the Set methods. These methods can be chained together for concise code. The following code is an example of applying custom settings to a tween:

transform.TweenLocalScale(Vector3.one * 2f, 5f)
    .SetEase(Ease.OutSine) // Set the easing function to OutSine
    .SetLoops(3, LoopType.Restart) // Repeat 3 times with restart behavior
    .SetDelay(1f); // Delay the start by 1 second

SetEase

Sets the easing function to use for the tween. You can also use your own easing function by passing an AnimationCurve.

SetLoops

Sets the number of times the tween should loop. By default, it's set to 1. You can create a tween that loops infinitely by setting it to -1. You can also specify the loop behavior using the second argument, LoopType.

SetPlaybackSpeed

Sets the playback speed of the tween. The default is 1, and negative values are not supported.

SetDelay

Sets a delay in seconds before the tween starts.

SetIgnoreTimeScale

Ignores the effect of TimeScale.

SetRelative

Sets the end value as a relative value from the start value.

SetInvert

Swaps the start and end values. You can adjust the behavior using the InvertMode.

InvertMode Behavior
InvertMode.None Moves from the start value to the end value as usual.
InvertMode.Immediate Moves to the end value as soon as the tween starts and then transitions towards the start value.
InvertMode.AfterDelay Waits until the tween starts, then moves to the end value and transitions towards the start value.

SetId

Assigns an ID to the tween. This allows you to operate on tweens with the same ID in bulk when performing operations like KillAll. You can pass an int or a string with a length of 32 bytes or less as an ID (default is int 0 or an empty string).

SetLink

Links the tween's lifecycle to a GameObject. You can change the behavior by setting the LinkBehaviour as the second argument. However, regardless of the option set, Kill will be called on OnDestroy.

SetAutoPlay

Sets whether the tween should automatically play (default is true). If set to false, you need to manually call Play() to start the tween.

SetAutoKill

Sets whether the tween should automatically be killed at the end (default is true). If set to false, you need to manually call Kill() to remove the tween. This option is useful when you want to reuse the same tween multiple times.

SetFrequency (Punch, Shake)

Available options for Punch and Shake tweens to set the frequency of vibration (default is 10).

SetDampingRatio (Punch, Shake)

Available options for Punch and Shake tweens to set the damping ratio of vibration. A value of 1 will completely dampen the vibration at the end, and a value of 0 will result in no damping (default is 1).

SetRandomSeed (Shake)

Available option for Shake tweens to set the seed value for the random numbers used in vibration. This option must be applied before playback.

SetPathType (Path)

Available option for Path-based tweens to set how the points are connected.

PathType Behavior
PathType.Linear Connects each point using a straight line.
PathType.CatmullRom Connects each point using a Catmull-Rom spline curve.

SetClosed (Path)

Available option for Path-based tweens to set whether the path is closed, allowing it to return to the starting point.

SetRoundingMode (int, int2, int3, int4, long)

Sets the rounding mode for decimal values. This option is applicable only to integer-based types.

RoundingMode Behavior
RoundingMode.ToEven Default setting. Rounds the value to the nearest integer, and if the value is midway, it rounds to the nearest even integer.
RoundingMode.AwayFromZero Rounds the value to the nearest integer, and if the value is midway, it rounds away from zero.
RoundingMode.ToZero Rounds the value towards zero.
RoundingMode.ToPositiveInfinity Rounds the value towards positive infinity.
RoundingMode.ToNegativeInfinity Rounds the value towards negative infinity.

SetScrambleMode (string)

Allows you to fill unrevealed characters with random characters. This option is only applicable to string tweens.

ScrambleMode Behavior
ScrambleMode.None Default setting. Nothing is displayed for unrevealed parts.
ScrambleMode.Uppercase Fills unrevealed parts with random uppercase letters.
ScrambleMode.Lowercase Fills unrevealed parts with random lowercase letters.
ScrambleMode.Numerals Fills unrevealed parts with random numbers.
ScrambleMode.All Fills unrevealed parts with random uppercase letters, lowercase letters, or numbers.
(ScrambleMode.Custom) Fills unrevealed parts with random numbers from the specified string. This option cannot be explicitly set and is determined when passing a string as an argument to SetScrambleMode.

SetRichTextEnabled (string)

Enables RichText support, allowing text with RichText tags to be animated with proper character advancement. This option is only applicable to string tweens.

Callbacks

When you want to perform some actions at specific times, such as the start or completion of a tween, you can use the On-series methods. Callback methods, like other settings, can be written using method chaining.

transform.TweenPosition(new Vector3(1f, 2f, 3f), 5f)
    .SetLoops(5)
    .OnUpdate(() => Debug.Log("update"))
    .OnStepComplete(() => Debug.Log("step complete"))
    .OnComplete(() => Debug.Log("complete"));

Note Enabling one or more callbacks can reduce performance during playback. In most cases, the impact on performance is minimal, but it's recommended to avoid using callbacks when creating a large number of tweens.

OnPlay

Called when the tween starts playing. Unlike OnStart, it ignores delays set with SetDelay and is also called if Play is invoked after a pause.

OnStart

Called when the tween begins its operation. If a delay is set with SetDelay, it is called after the delay has passed.

OnUpdate

Called every frame during the tween's playback.

OnStepComplete

Called at the end of each loop when SetLoops is configured.

OnComplete

Called when the tween is completed.

OnKill

Called when the tween is killed.

Avoiding Allocations

Similar to Tween.To() and Tween.FromTo(), you can avoid lambda expression allocations by passing the target instance as the first argument.

// A class with a field named "foo"
ExampleClass target;

float endValue = 10f;
float duration = 2f;

// Avoiding lambda expression allocation by passing "target" to "OnUpdate"
Tween.To(target, obj => obj.foo, (obj, x) => obj.foo = x, endValue, duration)
    .OnUpdate(target, obj => Debug.Log(obj.foo));

DelayedCall / Empty

You can create a tween that performs a specified action after a certain delay using Tween.DelayedCall().

// Display a log after 3 seconds
Tween.DelayedCall(3f, () => Debug.Log("delayed call"));

Additionally, you can create an empty tween using Tween.Empty().

// A tween that completes after 3 seconds
Tween.Empty(3f);

// DelayedCall() internally calls the following code
Tween.Empty(3f)
    .OnStepComplete(() => Debug.Log("delayed call"))
    .OnComplete(() => Debug.Log("delayed call"));

Sequence

A Sequence is a feature used to group multiple tweens together. By using Sequences, you can easily create complex animations by combining multiple tweens.

Creating a Sequence

You can obtain a new Sequence from Sequence.Create():

// Create a new Sequence
Sequence sequence = Sequence.Create();

Adding Tweens

Next, you add the tweens you want to include in the Sequence. There are various methods available to add tweens to a Sequence. By using these methods, you can combine tweens and build complex animations.

Sequences can be nested regardless of their hierarchy. Options and callbacks such as SetDelay and SetLoops will also work for the Sequence after it has been added.

Append

The Append() method adds tweens to the end of the Sequence. The added tweens will play in sequence when you play the Sequence.

// Append a tween to the end
sequence.Append(transform.TweenPosition(new Vector3(1f, 0f, 0f), 2f))
    .Append(transform.TweenPosition(new Vector3(1f, 3f, 0f), 2f));

You can use AppendInterval() and AppendCallback() to add delays or callbacks:

// Append a delay to the end
sequence.AppendInterval(1f);

// Append a callback to the end
sequence.AppendCallback(() => Debug.Log("Hello!"));

Prepend

If you want to add tweens to the beginning, you can use Prepend(). In this case, the tweens already added will move back by the duration of the new prepend tween.

// Prepend a tween to the beginning
sequence.Prepend(transform.TweenPosition(new Vector3(1f, 0f, 0f), 2f));

You can also use PrependInterval() and PrependCallback():

// Prepend a delay to the beginning
sequence.PrependInterval(1f);

// Prepend a callback to the beginning
sequence.PrependCallback(() => Debug.Log("Hello!"));

Join

To concatenate a tween with the ones added before it, you can use Join(). Tweens added with Join() will play concurrently with the previously added tween.

sequence.Append(transform.TweenPosition(new Vector3(1f, 0f, 0f), 2f));

// Join with the previous tween
sequence.Join(transform.TweenPosition(new Vector3(1f, 3f, 0f), 2f));

Insert

If you want to insert a tween at an arbitrary point, you can use Insert(). The tween added with Insert() will operate independently of other tweens and will start playing once it reaches the specified position.

// Insert a tween at 1 second from the start
sequence.Insert(1f, transform.TweenPosition(new Vector3(1f, 0f, 0f), 2f));

You can also insert callbacks using InsertCallback():

// Insert a callback at 1 second from the start
sequence.InsertCallback(1f, () => Debug.Log("Hello!"));

Implicit Conversion to Tween

A Sequence can be implicitly converted to a Tween, allowing you to assign it directly:

Sequence sequence = Sequence.Create();

// Can be assigned directly to a Tween variable
Tween tween = sequence;

Usage Considerations

Here are some important points to keep in mind when using Sequences:

  • You cannot add tweens to a Sequence while it is playing.
  • You cannot add tweens with infinite loops to a Sequence.
  • Once a tween is added to a Sequence, it becomes locked, and you cannot access it individually. Be cautious, as you cannot manipulate the individual tweens within a Sequence.
  • You cannot include the same tween in multiple Sequences.

Waiting for Tweens Using Coroutines

You can easily wait for tweens by using coroutines. To wait for a tween, you can use the WaitFor... methods. This allows you to wait until specific events like Complete or Pause occur.

IEnumerator ExampleCoroutine()
{
    // Wait for the completion of the tween
    yield return Tween.Empty(3f).WaitForComplete();

    // Wait until the end of one loop
    yield return transform.TweenPosition(Vector3.one, 1f)
        .SetLoops(3)
        .WaitForStepComplete();
}

Logging

If you want to perform logging of Tween callbacks and values for debugging purposes, you can easily achieve this using dedicated extension methods. Note that these logs will only be displayed if MagicTweenSettings' LoggingMode is set to Full.

using MagicTween;
using MagicTween.Diagnostics; // Enable debug extension methods

// Log specific callback
transform.TweenPosition(Vector3.up, 5f)
    .LogOnUpdate();

// Log all callbacks together
transform.TweenEulerAngles(new Vector3(0f, 0f, 90f), 5f)
    .LogCallbacks();

// You can also assign a name for identification
transform.TweenLocalScale(Vector3.one * 2f, 5f)
    .LogCallbacks("Scale");

float foo;
// You can also log values (current value per frame)
Tween.To(() => foo, x => foo = x, 5f, 10f)
    .LogValue();

Project Settings

You can customize Tween's initial settings and logging preferences.

Creating MagicTweenSettings

Create a MagicTweenSettings asset to store your configuration by navigating to Assets > Create > Magic Tween > Magic Tween Settings.

Note The created MagicTweenSettings will be automatically added to the project's Preload Assets. If the settings are not being loaded, ensure that MagicTweenSettings is included in the Preload Assets.

Logging Mode

Set whether logging is enabled or not.

LoggingMode Behavior
LoggingMode.Full Display all logs, including Log-related extension methods, in the Console.
LoggingMode.WarningsAndErrors Display only warnings and errors in the Console.
LoggingMode.ErrorsOnly Display only errors in the Console.

Capture Exceptions

When set to "On," exceptions that occur internally in Tweens will be logged as warnings. When set to "Off," exceptions will be logged as regular exceptions.

Default Tween Parameters

You can modify the default settings for Tweens.

Changing Settings from Script

You can access these settings from the MagicTweenSettings class in your script.

// Change Logging Mode from script
MagicTweenSettings.loggingMode = LoggingMode.ErrorsOnly;

Accelerating Transform Tweens with Jobs

Starting from v0.2, an option to accelerate Transform tweens using IJobParallelForTransform has been added. This option is disabled by default and can be enabled by adding MAGICTWEEN_ENABLE_TRANSFORM_JOBS to the Project Settings > Scripting Define Symbols section.

Once added, the acceleration by IJobParallelForTransform will be applied by simply manipulating Transforms using the usual extension methods.

The performance comparison is shown in the graph. When tweening 50,000 Transforms, there is an acceleration of nearly 1.7x.

TextMesh Pro

Magic Tween supports TextMesh Pro (TMP) and allows you to tween text characters individually using the TweenChar extension methods. Here's how you can use it:

TMP_Text tmp;

// GetCharCount retrieves the number of tweenable characters
for (int i = 0; i < tmp.GetCharCount(); i++)
{
    tmp.TweenCharScale(i, Vector3.zero).SetInvert().SetDelay(i * 0.07f);
}

You can stop the tweens associated with TMP_Text and reset the text's decoration to its initial state using ResetCharTweens():

// Stop the character tweens and reset the decoration to the initial state
tmp.ResetCharTweens();

Character tweens are powered by the TMPTweenAnimator class internally, which you can access using GetTMPTweenAnimator():

// Get the internal TMPTweenAnimator
TMPTweenAnimator tmpAnimator = tmp.GetTMPTweenAnimator();

// Extension methods for TMP_Text use methods from TMPTweenAnimator internally
tmpAnimator.TweenCharOffset(0, Vector3.up);

// You can directly set parameters for each character using SetChar**
tmpAnimator.SetCharScale(1, Vector3.one * 2f);
tmpAnimator.SetCharColor(1, Color.red);

// ResetCharTweens() equivalent
tmpAnimator.Reset();

// GetCharCount() equivalent
tmpAnimator.GetCharCount();

UniRx

By integrating UniRx, you can convert Tween callbacks and Tween operations into Observables.

Converting Callbacks to Observables

You can use methods like OnUpdateAsObservable() to convert Tween callbacks into Observables. For example:

float foo;

Tween.To(() => foo, x => foo = x, 10f, 10f)
    .OnUpdateAsObservable()
    .Subscribe(_ =>
    {
        Debug.Log("update!");
    });

Converting Tweens to Observables

You can use ToObservable() to transform a Tween into an Observable that emits values each frame. For example:

Tween.FromTo(0f, 10f, 10f, null)
    .ToObservable()
    .Where(x => x >= 5f)
    .Subscribe(x =>
    {
        Debug.Log("current value: " + x);
    });

UniTask

By integrating UniTask, you can use async/await to handle Tween waiting operations.

var tween = transform.TweenPosition(Vector3.up, 2f);

// You can directly await the Tween (waits until the Tween is killed)
await tween;

You can use AwaitForKill() and pass a CancellationToken to handle cancellation.

// Create a CancellationTokenSource
var cts = new CancellationTokenSource();

// Wait until the Tween is killed, passing the CancellationToken
await transform.TweenPosition(Vector3.up, 2f)
    .AwaitForKill(cancellationToken: cts.Token);

You can also wait for other events like completion using AwaitForComplete() or AwaitForPause().

// Wait until the Tween completes
await transform.TweenPosition(Vector3.up, 2f).AwaitForComplete();

Furthermore, you can specify the CancelBehaviour to determine the behavior on cancellation.

var cts = new CancellationTokenSource();

// On cancellation, call Complete and throw OperationCanceledException
await transform.TweenPosition(Vector3.up, 2f)
    .AwaitForComplete(CancelBehaviour.CompleteAndCancelAwait, cts.Token);
CancelBehaviour Behavior on Cancellation
CancelBehaviour.Kill Calls the Kill method.
CancelBehaviour.Complete Calls the Complete method.
CancelBehaviour.CompleteAndKill Calls both Complete and Kill methods.
CancelBehaviour.CancelAwait Throws an OperationCanceledException without calling Complete or Kill.
CancelBehaviour.KillAndCancelAwait Default behavior. Calls Kill and throws an OperationCanceledException.
CancelBehaviour.CompleteAndCancelAwait Calls Complete and throws an OperationCanceledException.
CancelBehaviour.CompleteAndKillAndCancelAwait Calls both Complete and Kill methods and throws an OperationCanceledException.

Creating Custom Tween Plugins

Magic Tween supports most primitive types and Unity.Mathematics types for tweens, and in most cases, you won't need to create extensions. However, there may be situations where you want to extend for finer control.

Magic Tween provides two interfaces, ICustomTweenPlugin and ITweenOptions, for extending types:

TweenPlugin

A TweenPlugin is a feature for extending specific types into tweens. Implementing this allows you to pass custom types to tweens.

Here's an example of implementing a TweenPlugin for double tweens:

// You need to add the TweenPluginAttribute
// This lets the Source Generator recognize the type and generate the necessary code
[TweenPlugin]
// Define a struct implementing ICustomTweenPlugin with type arguments for the tweened value and associated TweenOptions (NoOptions if not needed)
public readonly struct DoubleTweenPlugin : ICustomTweenPlugin<double, NoOptions>
{
    // Write calculation logic inside the Evaluate function
    public double Evaluate(in double startValue, in double endValue, in NoOptions options, in TweenEvaluationContext context)
    {
        // If SetRelative(true) is set, resolve the end value as a relative value
        var resolvedEndValue = context.IsRelative ? startValue + endValue : endValue;

        // If SetInvert(true) is set, swap start and end values
        // Calculate the current value based on context.Progress (0 to 1)
        if (context.IsInverted) return math.lerp(resolvedEndValue, startValue, context.Progress);
        else return math.lerp(startValue, resolvedEndValue, context.Progress);
    }
}

TweenPlugins do not retain state. If you want to hold additional settings, create custom TweenOptions.

TweenOptions

To add custom settings to your tween, define a struct implementing ITweenOptions. Here's an example of TweenOptions for integer tweens:

// Define a struct implementing ITweenOptions
public struct IntegerTweenOptions : ITweenOptions
{
    public RoundingMode roundingMode;
}

public enum RoundingMode : byte
{
    ToEven,
    AwayFromZero,
    ToZero,
    ToPositiveInfinity,
    ToNegativeInfinity
}

You can set your custom TweenOptions when defining the TweenPlugin.

Using Custom TweenPlugins

To use your custom TweenPlugin in a tween, you can use Tween.To() or Tween.FromTo():

double currentValue = 0.0;

// Create a tween with custom TweenOptions and TweenPlugins
Tween.FromTo<double, NoOptions, DoubleTweenPlugin>(x => currentValue = x, startValue, endValue, duration);

If you've specified custom TweenOptions, you can modify them using SetOptions(). You can also retrieve the currently set TweenOptions values using GetOptions().

public struct CustomOptions : ITweenOptions
{
    ...
}

// Modify custom options for the tween using SetOptions
tween.SetOptions(new CustomOptions() { ... });

// Retrieve options values using GetOptions
var options = tween.GetOptions();

Built-in TweenPlugins/TweenOptions

Magic Tween includes several built-in TweenPlugins and TweenOptions.

Here's a list of available TweenPlugins and their corresponding TweenOptions (there are additional specialized TweenPlugins/TweenOptions for specific tweens, but they are not meant for external use):

Type TweenPlugin Corresponding TweenOptions
float FloatTweenPlugin NoOptions
float2 Float2TweenPlugin NoOptions
float3 Float3TweenPlugin NoOptions
float4 Float4TweenPlugin NoOptions
double DoubleTweenPlugin NoOptions
double2 Double2TweenPlugin NoOptions
double3 Double3TweenPlugin NoOptions
double4 Double4TweenPlugin NoOptions
int IntTweenPlugin IntegerTweenOptions
int2 Int2TweenPlugin IntegerTweenOptions
int3 Int3TweenPlugin IntegerTweenOptions
int4 Int4TweenPlugin IntegerTweenOptions
long LongTweenPlugin IntegerTweenOptions
quaternion QuaternionTweenPlugin NoOptions

Implementation for ECS

Magic Tween provides APIs for implementing tweens for ECS, allowing you to create high-performance tweens compared to conventional methods.

Creating a Translator

When tweening values of specific components, you need to create a Translator component to apply the current tween value to the target component and a system to execute it.

As an example, let's create a Translator for tweening the following component:

public struct ExampleComponent : IComponentData
{
    public float value;
}

First, define a structure implementing ITweenTranslator:

public struct ExampleTranslator : ITweenTranslator<float, ExampleComponent>
{
    // Apply the value to the component
    public void Apply(ref ExampleComponent component, in float value)
    {
        component.value = value;
    }

    // Return the current value of the component
    public float GetValue(ref ExampleComponent component)
    {
        return component.value;
    }
}

Next, create a system class that inherits from TweenTranslationSystemBase. Specify type arguments for the Translator and the TweenPlugin to be used. You can refer to the "Built-in TweenPlugins/TweenOptions" table for available TweenPlugins. It's also possible to specify custom TweenPlugins.

The system class itself doesn't need additional implementation as the core logic is provided in the base class:

public partial class ExampleTweenTranslationSystem : TweenTranslationSystemBase<float, NoOptions, FloatTweenPlugins, ExampleComponent, ExampleTranslator> { }

With this setup, you're ready to tween values.

Tweening Component Values

To tween values using the created Translator, use Tween.Entity.To() or Tween.Entity.FromTo(). Provide the component type and the Translator type as type arguments.

var entity = EntityManager.CreateEntity();
EntityManager.AddComponent<ExampleComponent>(entity);

// Tween the value of ExampleComponent's 'value' to 5 over 10 seconds
Tween.Entity.To<ExampleComponent, ExampleTranslator>(entity, 5f, 10f);

Just like regular tweens, you can chain methods to add settings:

Tween.Entity.FromTo<ExampleComponent, ExampleTranslator>(entity, 0f, 5f, 10f)
    .SetEase(Ease.OutSine)
    .SetLoops(3, LoopType.Restart)
    .SetDelay(1f);

You can also add these tweens to a sequence:

var entity1 = EntityManager.CreateEntity();
var entity2 = EntityManager.CreateEntity();
EntityManager.AddComponent<ExampleComponent>(entity1);
EntityManager.AddComponent<ExampleComponent>(entity2);

var tween1 = Tween.Entity.To<ExampleComponent, ExampleTranslator>(entity1, 5f, 10f);
var tween2 = Tween.Entity.To<ExampleComponent, ExampleTranslator>(entity2, 5f, 10f);

var sequence = Sequence.Create()
    .Append(tween1)
    .Append(tween2);

Warning Avoid applying multiple concurrent Tweens with the same Translator to the same Entity. This may lead to overlapping value modifications and unexpected behavior.

Built-in Translators

Under MagicTween.Translators, you can find built-in Translators for ECS components.

Currently, there's a Translator that works with LocalTransform.

If you have the Entities Graphics package installed, you'll find Translators for tweening Material properties.

Limitations

Creating and manipulating Tweens/Sequences is only supported on the main thread. You cannot create new Tweens or perform operations like Kill or Complete from a Job. To alleviate this limitation, there's ongoing development to introduce functionality for creating/operating Tweens using a dedicated CommandBuffer.

Other Features

EaseUtility

The easing functions used internally by Tween can be accessed via EaseUtility.

float value1 = EaseUtility.Evaluate(0.5f, Ease.OutQuad);
float value2 = EaseUtility.InOutQuad(0.5f);

Optimization

Tween Caching

Usually, the cost of creating Tweens or Sequences is not a significant concern. However, in scenarios where you repeatedly use the same animations, creating them from scratch each time may not be very efficient. Caching Tweens and reusing them can be an effective approach in such cases.

// Create a Tween and change settings to manually control play and kill
Tween tween = transform.TweenPosition(Vector3.up, 2f)
    .SetAutoPlay(false)
    .SetAutoKill(false);

// Play or Restart the Tween using Play() or Restart()
tween.Play();
tween.Restart();

// Manually call Kill() when you're done using the Tween
tween.Kill();

// Alternatively, you can use SetLink to tie the Tween's lifetime to a GameObject
// tween.SetLink(transform);

When reusing tweens, always make sure to set SetAutoKill(false). If this is set to true, the Tween will be automatically destroyed when it completes playing. Also, if you want to manually manage the play timing, you can set SetAutoPlay(false) accordingly.

When SetAutoKill(false) is set, be sure to call Kill() manually when you are done with the Tween. Alternatively, you can use SetLink() to associate the tween's lifetime with a GameObject, so it gets destroyed when the GameObject is destroyed.

Experimental Features

The MagicTween.Experimental namespace contains features that are currently under development. These features are available for use but come with no guarantees, and there may be breaking changes without notice.

Known Issues

Performance Drop in the Editor

ECS performs numerous checks to enhance safety, which leads to decreased performance within the editor. This performance degradation is particularly noticeable when creating Tweens and, in some cases, can result in processing times several times longer than usual.

It's important to note that these safety checks are disabled in the build, so performance measurements should always be conducted in the build environment.

Performance Drop in WebGL

While it's possible to use ECS in WebGL, limitations in WebGL's specifications, such as the absence of multi-threading and SIMD, disable optimizations like the Job System and Burst. ECS achieves its high performance through the Job System and Burst, making performance degradation unavoidable in WebGL (hence, there are few advantages to using ECS on WebGL currently).

Magic Tween optimizes the calculation part of Tweens using the Job System and Burst. Therefore, on WebGL, performance suffers for the reasons mentioned above. While these effects are typically not noticeable, please be mindful of this when creating a large number of Tweens.

Support

Forum: https://forum.unity.com/threads/magic-tween-extremely-fast-tween-library-implemented-in-ecs.1490080/

License

MIT License