Skip to content

Animation

The Tick UI animation context drives per-widget enter, leave, and event-based transitions. You open a context with PushAnimate("id"), chain a fluent definition (Init, Enter, Leave, and event hooks), and read the animated values back via GetColor, GetFloat, GetRect, or GetInt. Drawing calls render the animated values.

The model

An animation context owns the values that interpolate over time: colour, scale, rect, and int. Your draw code reads the current value each frame; the context blends from start to target based on elapsed time and a curve.

State on the context is keyed by the string ID you pass to PushAnimate, the same way widget state is keyed by widget ID. Two contexts with the same ID share state. Nesting two PushAnimate scopes with different IDs creates two independent contexts.

For the underlying stack contract, refer to Stacks.

The lifecycle

A typical context goes through four phases per frame:

  1. Open. PushAnimate("id") pushes the context onto the animation stack and returns the fluent builder.
  2. Define. Chain .Init(), .Enter(), .Leave(), and any event-driven states (.BoolWhileTrue).
  3. Apply. Call .Apply() to finalise. The returned context holds the animated values for the current frame.
  4. Read and pop. Read values via GetColor, GetFloat, and so on while drawing. Call ui.PopAnimate() to close the context.

Reading from the context before .Apply() returns the default values, not the animated ones. Forgetting PopAnimate leaves the animation stack imbalanced; the next push nests unintentionally.

States and transitions

Three built-in states define the context's lifecycle:

  • Init. The value at the moment the widget first appears. Default state when no other applies.
  • Enter. The forward transition into the active state.
  • Leave. The reverse transition out of the active state.

Four event hooks drive the active state:

  • .BoolWhileTrue(string id, bool condition). The named state is active for as long as condition is true.
  • .BoolWhileFalse(string id, bool condition). The named state is active while condition is false. Useful for "show this when the dialog is hidden" patterns.
  • .BoolBecomesTrue(string id, bool condition). Fires on the frame condition flips from false to true. Useful for one-shot animations.
  • .BoolBecomesFalse(string id, bool condition). Fires on the frame condition flips from true to false.

Each state defines its own animated values per property. .Color(), .Scale(), .Float(), .Rect(), and .Int() return per-property fluent operators. .Set(value) pins a value. .Blend(from, to, duration) interpolates over time.

For the per-state authoring procedure, refer to Add an animation. For full workflows with multiple animated widgets, refer to the Animation guide.

Read animated values

The context returned by .Apply() exposes typed getters:

  • ctx.GetColor() returns the context's primary colour. The context also exposes its primary float, int, and rect values via ctx.GetFloat(), ctx.GetInt(), and ctx.GetRect(), and its primary scale and rotation (in degrees) via ctx.GetScale() and ctx.GetRotate().
  • ctx.GetColor(string id), ctx.GetFloat(string id), ctx.GetRect(string id), ctx.GetInt(string id) look up a named animated property by string ID. Use these when a context drives multiple animated values that share an enter/leave shape.
  • ctx.TryGetColor(...), ctx.TryGetFloat(...), ctx.TryGetInt(...), ctx.TryGetRect(...) return false when the named property is missing instead of returning a default.

Each call returns the blended value for the current frame, sampled against the active state and the configured curve. Pass the result into the matching draw call:

var ctx = ui.PushAnimate("hover_anim")
    .Init().Color().Set(restColor).Done()
    .Enter().Color().Blend(restColor, hoverColor, 0.2f).Done()
    .Leave().Color().Blend(hoverColor, restColor, 0.2f).Done()
    .Apply();

ui.FillRounded(ctx.GetColor(), 6);
ui.PopAnimate();

Curves and timing

Every blend takes its duration as an explicit argument, so timing is set per call rather than from a shared default. .Blend(from, to, duration) eases with a linear curve; the overload .Blend(from, to, duration, Curves.Curve.EaseOutCubic) selects an easing curve from the Curves.Curve catalogue. The BlendUnscaled variants measure their duration in unscaled time, ignoring Time.timeScale.

The animation stack

m_animationStack carries open contexts so nested animations work. A nested PushAnimate inside an open context runs on top of it; PopAnimate returns to the parent. Mismatched push and pop fires the same kind of leak that other stacks do.

For the per-stack push and pop contract, refer to Stacks.

Source file name

The class is named AnimationContext, and the file that contains it is Source/Interface/Animation/AnimationContext.cs. The fluent event types (AnimationContextEnter, AnimationContextLeave, AnimationContextEvent, and the rest) live alongside it in the same Source/Interface/Animation/ directory.

Pitfalls

GetColor returns the default, not the animated value. : You called the getter before .Apply(). Apply first, then read.

The animation stack leaks at end of frame. : A PushAnimate was not paired with a PopAnimate. Audit the body that opened the context.

Hover state never enters. : The BoolWhileTrue condition is reading stale state. Confirm the condition (ui.IsHovered(), for example) is read inside the same rect the visual occupies.

Additional resources