Skip to main content

General Animator

The animation methods introduced in this section are based on MilInstantAnimator, also referred to as the general animator.

This subsection will go over additional mechanisms of the general animator. Unless otherwise noted with an “*”, the methods mentioned below follow a fluent interface design, meaning the method itself returns a MilInstantAnimator.

For example, methods like Then, ThenOneByOne, and And mentioned in the previous sections are part of the fluent interface. This means you can freely chain method calls on a MilInstantAnimator instance, like so:

MAni.Make(
...
).MethodA(...)
.MethodB(...)
.MethodC(
...
).Play();

Lifecycle

By default, the general animator will automatically stop playback when the current scene is unloaded, preventing animation operations from continuing on destroyed objects.

If you want the animator to persist through scene changes—for example, when animating an object marked with DontDestroyOnLoad—you can call:

DontStopOnLoad()

Playback Control

Enable looping for the animator:

EnableLooping()

Pause the animator*:

Pause()

Stop the animator*:

Stop()

Stop the animator and revert changes it applied*:

Reset()

Time Source

By default, the general animator uses UnscaledTime to update animations, which means it is not affected by Time.timeScale.

If you want it to be affected by timeScale, use:

SetTimeSource(TimeSource.ScaledTime);

Reset Mode

When the animation starts from the beginning, the general animator will automatically call Reset(). The extent of reverting applied changes depends on the “Reset Mode”.

There are two reset modes:

  • ResetToOriginalState (default)

    This mode restores the properties modified by the animator to the state recorded at the start of playback.

  • ResetToInitialState

    This mode restores the properties modified by the animator to the state defined by the animations at timestamp 0s.

You can set the reset mode using:

UsingResetMode(AnimationResetMode.ResetToInitialState);
warning
  • Animations without a start value will always reset using ResetToOriginalState, regardless of the ResetToInitialState setting.

  • Animations with blending mode Additive will always reset using ResetToOriginalState, regardless of the ResetToInitialState setting.

  • Since the Play() method submits animation tasks to the animator and they begin in the next frame, a situation may occur where a Canvas appears before animations apply, briefly showing its raw state. To avoid this, you can:

    1. Use PlayImmidiate() to apply animations in the current frame.
    2. Use ResetToInitialState as the reset mode. While the animation still starts on the next frame, the reset takes effect immediately, transitioning the state to the 0s animation target to avoid a visual glitch.

Reusing Animators with No Start Value

As explained in the reset mode section, if you reuse an animator that contains animations without a start value, it will reset the object's properties to the state before the animation started—likely not the intended result.

In this case, you should explicitly pass revertToChanges as false when calling Play to prevent resetting changes made by such animations:

Play(null, false);

Delay the Previous Animation Group

Besides Then, ThenOneByOne, and And, there is one more method for describing animation flow: Delayed.

It delays the immediate preceding animation group by the specified number of seconds. For example:

MAni.Make(
1f / ballSpriteRender.MQuad(x => x.color, Color.clear, Color.white),
1f / music.MQuad(x => x.volume, 0f, 1f)
).Then(
2f / ballTransform.MQuad(x => x.position, Vector3.zero, Vector3.one * 3f),
1f + 0.5f / ballTransform.MQuad(x => x.localScale, Vector3.one, Vector3.one * 2f)
).Delayed(1f)
.ThenOneByOne(
1f / camera.MQuad(x => x.fieldOfView, 6f, 4.5f),
1f / camera.MQuad(x => x.localEulerAngles, Vector3.zero, new Vector3(0, 0, 20f))
).Play();

is equivalent to:

MAni.Make(
1f / ballSpriteRender.MQuad(x => x.color, Color.clear, Color.white),
1f / music.MQuad(x => x.volume, 0f, 1f)
).Then(
// Note the offset changes for this group
1f + 2f / ballTransform.MQuad(x => x.position, Vector3.zero, Vector3.one * 3f),
2f + 0.5f / ballTransform.MQuad(x => x.localScale, Vector3.one, Vector3.one * 2f)
).ThenOneByOne(
1f / camera.MQuad(x => x.fieldOfView, 6f, 4.5f),
1f / camera.MQuad(x => x.localEulerAngles, Vector3.zero, new Vector3(0, 0, 20f))
).Play();