Under the Hood - rmfandyplayz/UnityUIAnimationTools GitHub Wiki
How the player works inside, for programmers and the curious. Nothing here is needed to make animations. Most pages also have their own Under the hood block for details specific to that feature.
When things happen
| When | What |
|---|---|
Awake |
Every step's Target Path is resolved to an object, once. Every step's resting value (what Baseline measures from) is captured, before anything animates. A player with an Animation Set takes its own copy of the set's animations. Problems with a step (a path that misses, no clip, a missing shader property) are warned about here, once. |
Play |
A DOTween Sequence is built from the steps, fresh each time. Other animations are ended according to Interrupt Others. |
| Every frame | DOTween updates the sequence. The player itself has no Update. |
| Disable / destroy | Running animations are killed (see Kill On Disable), and every onEnd callback is told why. |
CaptureBaseline() re-runs the resting-value capture. Resolving targets and copying the set only happen once.
Building the sequence
- Timing: each step's start time is worked out once (
LayOutSteps):AFTER PREVIOUSgoes after everything so far,WITH PREVIOUSstarts with the step above, and Delay is added to that position. A step's delay is applied in exactly one place. DOTween adds a tween's ownSetDelayon top of its position in a sequence, so using both would double every delay. - Configure first, insert second: a tween must be fully set up (
From,SetEase,SetRelative) before it's inserted into the sequence. DOTween locks a tween once it's inserted, and changes after that are silently ignored. - Unscaled time is
SetUpdate(true), which is what keeps UI moving atTime.timeScale == 0. - Lifetime: the sequence is linked to the GameObject (
SetLinkonly works on the outer sequence, not on tweens inside it) and killed inOnDestroy.
Ending animations
- Every "finish it" path goes through
Sequence.Complete(true): Interrupt Others'COMPLETE,Stop(name, complete: true)andStopAll(true). DOTween'sKill(complete: true)reaches the end values but skips callbacks still ahead in the sequence, so aSet Activestep never runs. - Every end reaches
onEndby two routes: the player's own call, and the sequence'sOnKill(which catches a kill from outside, likeDOTween.KillAll()). A fire-once flag, local to each play, makes the second a no-op. It lives on the play rather than on the animation so that anonEndwhich replays the same animation can't have the old play fire the new one's callback. - A punch or shake cut short is written back to its start. See Punch and Shake.
Values set from code
SetTo / SetFrom store their value beside the authored one, never over it, so the Inspector keeps showing what you typed and ClearOverrides has something to go back to. There's one slot per kind of value, so changing a step's Type never reads another kind's leftovers.
DOTween in edit mode
DOTween never initialises outside play mode, and that changes how killing works:
tween.Kill() |
DOTween.Kill(id) / KillAll() |
Pause() |
Complete() |
|
|---|---|---|---|---|
| Play mode | works, OnKill fires |
works, OnKill fires |
works | works |
| Edit mode | does nothing | works | works | works |
So a test that kills a tween in edit mode and checks the result is measuring the Editor, not the game. Check anything about tween lifetimes in play mode.
The preview handles this by tagging every sequence with an id and killing it with DOTween.Kill(id). DOTween's own DOTweenEditorPreview.Stop() doesn't kill anything either: it only stops updating, so a "stopped" preview would otherwise resume alongside the next one.
Also: DOTween.Kill(x) matches tweens by their target or id, so DOTween.Kill(sequence) matches nothing. It's not the same as sequence.Kill().
How the preview stays safe
- Values are captured and restored per property, never by round-tripping whole components (that also rewrites references, blanking things like an Image's sprite).
- Resting values are re-captured before every preview Play, from the true resting state, so
Baselineendpoints never drift. - Sounds are suppressed and On Complete events cleared for the whole preview.
- Only one player previews at a time, and the preview is one Undo step.
- It unwinds from
[InitializeOnLoad]hooks on entering play mode (before Unity backs up the scene) and before a script reload, not only from an Inspector closing.
Serialized data
- Enum values are stored as numbers, and those numbers are the contract. Every enum in the tool assigns its numbers explicitly. Reordering the lines is safe; changing a number repoints every step already saved with it. See Warnings and Common Issues.
- New fields must be harmless when missing. Unity gives a field missing from old data its default, so every feature added since the first version is invisible until used, and old animations play byte-for-byte as they did. This has been checked with before/after fingerprints of every authored animation in a real project.
- Unity's
+button zero-fills new list elements instead of running C# initialisers.OnValidatefills in any field still at zero (FillUnsetDefaults), and never touches one you've set.