Tween
Pause-aware value animation with 31 easing curves, driven by the Time service's clocks.
Animate any value from A to B over time — and respect pause menus for free.
| Interface | ITweenService |
| Off switch | NullTweenService |
| Assembly | CommonGameSystem.Core |
| Startup | Registered automatically at boot — no setup needed |
What it does
A tween is a smooth interpolation between a start value and an end value over a fixed duration. The Tween service computes that value each tick; an easing curve shapes the acceleration (start slow and speed up, overshoot and settle, bounce, and so on). You provide the callback that applies each value to your object — the framework only calculates it. There is no reflection, no automatic property writing, and no external dependency.
Every tween is bound to one of the Time service's clocks — Gameplay, UI, or Background — so your animations respect pause menus and slow motion without extra code. A door-opening tween on the Gameplay clock freezes when the player pauses; a menu fade on the UI clock keeps running.
Quick start
Resolve the service once in Awake or Start and cache it:
using CommonGameSystem.Core;
using UnityEngine;
[DefaultExecutionOrder(100)] // Run after the framework has started.
public class MyMovingThing : MonoBehaviour
{
private ITweenService _tween;
private void Awake()
{
_tween = ServiceLocator.Resolve<ITweenService>();
}
private void StartMove()
{
_tween.To(
from: transform.position,
to: new Vector3(5f, 0f, 0f),
durationSeconds: 0.5f,
onUpdate: pos => transform.position = pos,
ease: EaseType.OutQuad
);
}
}
API reference
The typed To / From overload surface
The service supports exactly six value types, each with its own set of typed overloads:
float · Vector2 · Vector3 · Vector4 · Color · Quaternion
Each type has four overloads — To or From, with the default clock or an explicit Clock parameter — for 24 methods in total. All share the same shape (shown here for float; substitute any of the six types):
IDisposable To(float from, float to, float durationSeconds,
Action<float> onUpdate,
EaseType? ease = null,
Func<float, float> customEase = null,
Action onComplete = null);
IDisposable To(float from, float to, float durationSeconds, Clock clock,
Action<float> onUpdate,
EaseType? ease = null,
Func<float, float> customEase = null,
Action onComplete = null);
IDisposable From(float from, float current, float durationSeconds,
Action<float> onUpdate,
EaseType? ease = null,
Func<float, float> customEase = null,
Action onComplete = null);
IDisposable From(float from, float current, float durationSeconds, Clock clock,
Action<float> onUpdate,
EaseType? ease = null,
Func<float, float> customEase = null,
Action onComplete = null);
From(a, current, ...) is the same operation as To(a, current, ...) — the name simply reads better when you animate from a value back to the current one (a punch-in scale effect, for example).
There is deliberately no generic To<T> method: a fixed, typed overload set is guaranteed to work under Unity's ahead-of-time (IL2CPP) compilation, where open generic methods over value types can fail at runtime. Quaternion tweens rotate along the shortest arc using Quaternion.Slerp.
Both To and From return an IDisposable token — dispose it to cancel the tween early.
Parameters
from,to(orcurrent) — the start and end values.durationSeconds— animation length in seconds. A duration of 0 completes immediately atto.clock— optional.Clock.Gameplay(the default) pauses with the game;Clock.UIkeeps running during pause menus;Clock.Backgroundalways runs.onUpdate— called every tick with the interpolated value. You apply it to your object.ease— an optionalEaseType. Omit it for the default easing, or pick one of the 31 curves below.customEase— an optionalFunc<float, float>that maps progress (0 to 1) to eased progress. If provided, it overridesease.onComplete— fired once when the tween reaches 100%. It does not fire on cancel.
The 31 easing curves
EaseType covers Linear plus ten curve families, each in three directions — In (accelerate into the motion), Out (decelerate out of it), and InOut (both):
| Family | Members | Character |
|---|---|---|
| Linear | Linear | Constant speed, no easing |
| Quad | InQuad · OutQuad · InOutQuad | Gentle acceleration (squared) |
| Cubic | InCubic · OutCubic · InOutCubic | Moderate acceleration (cubed) |
| Quart | InQuart · OutQuart · InOutQuart | Strong acceleration |
| Quint | InQuint · OutQuint · InOutQuint | Very strong acceleration |
| Sine | InSine · OutSine · InOutSine | Soft, wave-based easing |
| Expo | InExpo · OutExpo · InOutExpo | Dramatic exponential ramp |
| Circ | InCirc · OutCirc · InOutCirc | Circular arc, abrupt at one end |
| Back | InBack · OutBack · InOutBack | Overshoots the target, then settles |
| Elastic | InElastic · OutElastic · InOutElastic | Springs past the target and oscillates |
| Bounce | InBounce · OutBounce · InOutBounce | Bounces like a ball at the boundary |
Rule of thumb: OutQuad or OutCubic for most UI motion; OutBack for playful pop-in; OutBounce and the elastics for cartoony emphasis.
Diagnostics
int ActiveCount { get; }— the number of live tweens. Diagnostic only; main thread only.
Examples
Fade a panel in over 0.3 seconds and log when done:
CanvasGroup panel = GetComponent<CanvasGroup>();
_tween.To(
from: 0f,
to: 1f,
durationSeconds: 0.3f,
clock: Clock.UI, // UI clock: keep running while the pause menu is open.
onUpdate: alpha => panel.alpha = alpha,
ease: EaseType.OutCubic,
onComplete: () => Debug.Log("Fade complete!")
);
Cancel a tween by disposing its token:
float score = 0f;
IDisposable token = _tween.To(0f, 100f, 1f, v => score = v);
// ... later, if you want to stop early:
token.Dispose(); // The tween cancels; onComplete does NOT fire.
Turning it off
ServiceLocator.Replace<ITweenService>(new NullTweenService());
This mutes all tweens. To and From return a shared empty token, no callbacks fire, and no ticker object is created. Useful for disabling animation in editor tooling or automated tests. The no-op version still validates input: a null onUpdate, an invalid Clock or EaseType, or a NaN, infinite, or negative durationSeconds still throws — so bugs stay visible even with animation off.
Common pitfalls
- Pick the right clock. Choosing the wrong clock is not an error, but the animation will feel wrong — for example, a gameplay animation that keeps moving during pause because it was bound to
Clock.UI. See the Time service for how each clock behaves under pause and slow motion. - Quaternion easing clamps overshoot. Overshoot curves (
InBack,OutBack,InElastic,OutElastic) show no visible rotation overshoot, becauseQuaternion.Slerpclamps its factor to the 0–1 range. The rotation holds at the end value during the overshoot phase. - Cancel is not the same as complete. Disposing a tween token cancels it silently —
onCompletedoes not fire. Reaching the end of the duration does fireonComplete. - Delegate allocation is your cost. The service pools its internal tween slots, but you allocate the
onUpdatelambda (and anycustomEaseclosure) on each call. For tight loops that must not allocate, store the delegate in a field once and pass that field on every call instead of writing a new lambda. - Do not keep tokens past completion. A finished tween's slot is reused for later tweens. A stale token is detected and ignored, so disposing it late does nothing harmful — but holding it is a sign of a lifetime bug. Fire-and-forget tweens do not need to store the token at all.
Related pages
- Time — the clocks tweens are bound to
- Tween Sequencing — ordered and parallel tween timelines
- Scheduler — delays and repeating timers on the same clocks