53 preset animations across 7 categories — fades, slides, bounces, pops, modal entrances, notification slides, page pushes — ready to play on any UI Toolkit element. Author them as reusable ScriptableObjects, or chain them in code with a fluent builder. Under the hood it's all native UI Toolkit CSS transitions, so playback stays on the engine's fast path.

One line, no assets, no setup:

csharp
// Fade in, then bounce-pop — chain code presets and per-property steps freely new TransitionSequencePlayer( new TransitionSequence().FadeIn(0.2f).Bounce().Build() ).Play(myButton);

Features

  • 53 preset assets, 7 categories — import with Essentials, reference in the Inspector or play from code
  • Fluent builder API — chainable .FadeIn(), .FadeOut(), .Bounce(), .SlideIn(), .SlideOut(), .Pulse() presets plus full per-property steps (opacity, scale, translate, rotate, colors, width/height)
  • Declarative & reusable — sequences as ScriptableObjects with sub-sequence composition, parallel groups, Repeat, and Once / Loop / PingPong / LoopCount play modes
  • UIView integration — animations trigger automatically on fade events; a multi-target selector turns one entry into a stagger pass
  • Production niceties — auto-cancel when a new animation hits the same element, ClearInlineStyles cleanup, OnComplete callbacks, and UI particles mid-sequence via PlayOverlay

Quick Start

Using TransitionAnimator (MonoBehaviour)

The simplest way to add animations is through the TransitionAnimator component:

  1. Add TransitionAnimator to a GameObject
  2. Reference a UIViewComponent or a PanelRenderer (leave the PanelRenderer empty to auto-search this GameObject and its parents)
  3. Add animation nodes (preset assets or inline step groups)
  4. Set play mode and trigger options
csharp
// Play animation manually transitionAnimator.Play(); // Or enable "Play On Enable" in the Inspector

Using Preset Assets

Luna includes 53 preset animation assets. They import with the Essentials sample under Assets/Samples/LunaUI/<version>/Essentials/ScriptableObjects/TransitionPresets/. Reference one in the Inspector and play it:

csharp
[SerializeField] private TransitionSequenceAsset _fadeIn; // Play the referenced preset var player = _fadeIn.CreatePlayer(); player.Play(myElement);

Using the Fluent API

Build animations programmatically with the TransitionSequence builder:

csharp
var sequence = new TransitionSequence() .Opacity(0f, 0.3f, EasingMode.EaseOut) // Fade out .Scale(0.8f, 0.3f, EasingMode.EaseOut) // Scale down .Build(); var player = new TransitionSequencePlayer(sequence); player.OnComplete += () => Debug.Log("Animation complete!"); player.Play(myElement);

Components

TransitionAnimator

MonoBehaviour that plays transition animations on VisualElements.

Inspector Properties

PropertyDescription
NodesOrdered list of animation nodes (inline step groups or sub-sequence asset references)
Play ModeOnce, Loop, PingPong, or LoopCount
Loop CountNumber of loops when Play Mode is LoopCount
UI View ComponentOptional UIViewComponent reference for target resolution
Panel RendererPanelRenderer reference (used when no UIViewComponent is set; if empty, searches this GameObject and its parents)
Element NameName of the target VisualElement (leave empty for root)
Fade TriggerWhich UIView fade event triggers this animation
Play On EnableAutomatically play when the component is enabled

⚠️ Async tree delivery. PanelRenderer delivers its visual tree asynchronously — there is no synchronous rootVisualElement. The animator registers a reload callback on the resolved PanelRenderer the first time Play() resolves its target (the callback fires immediately when the panel is already mounted) and keeps the cached root current across rebuilds. If the panel hasn't mounted yet, Play() logs a warning and exits — call it again once the panel is ready, e.g. from UIViewComponent.WhenUILoaded(...).

Public Methods

csharp
// Play the animation on the configured element public void Play() // Play on a specific element public void Play(VisualElement target) // Stop playback public void Stop() // Pause/Resume public void Pause() public void Resume() // Create a sequence or player from this animator's nodes public TransitionSequence CreateSequence() public TransitionSequencePlayer CreatePlayer()

Inspector Events

These are UnityEvents with no public C# accessor — wire listeners in the Inspector:

  • On Complete — fired when the animation completes
  • On Stopped — fired when the animation is stopped

For code subscriptions, use TransitionSequencePlayer.OnComplete / OnStopped via CreatePlayer().

TransitionSequenceAsset

ScriptableObject for defining reusable animation sequences. Create via: Create > CupkekGames > Luna UI > Transition Sequence

Supports:

  • Inline step groups (sequential or parallel)
  • Sub-sequence references (composition)
  • Repeat nodes (loop inner nodes N times)
  • Overlay nodes (fire PlayOverlay / PlayOverlayInstance at a specific timing)
  • Recursive sub-sequence inlining with cycle detection

TransitionSequencePlayer

Runtime player for executing transition sequences on a VisualElement.

csharp
// Create from a built sequence var player = new TransitionSequencePlayer(sequence); // Or from an asset var player = myAsset.CreatePlayer(); // Enable debug logging player.Debug = true; // Play on target player.Play(myElement); // Check state bool isPlaying = player.IsPlaying; bool isPaused = player.IsPaused; int currentNode = player.CurrentNodeIndex; int currentIteration = player.CurrentIteration;

Auto-Cancel

When Play() is called on an element that already has an active TransitionSequencePlayer, the previous player is automatically stopped before the new one starts. This prevents conflicting animations from overlapping on the same element:

csharp
var player1 = new TransitionSequencePlayer(bounceSequence); var player2 = new TransitionSequencePlayer(fadeSequence); player1.Play(myElement); // starts playing player2.Play(myElement); // automatically stops player1, then starts player2

Events

csharp
event Action OnComplete; // Sequence fully complete event Action<int> OnIterationComplete; // After each loop pass event Action OnStopped; // Playback stopped via Stop()

Restore mode

Every sequence has a RestoreMode:

  • RestoreOnEnd (default) — the player cleans up when the sequence ends for any reason (completion, Stop(), preemption by another player). Cleanup happens in one of two ways, chosen per property:

    • Null-restoretranslate/scale/rotate, opacity, and background-position have their inline values cleared so the element falls back to whatever USS computes. These properties are treated as animation scratch space: an inline value on them is presumed to belong to some animation, never to the component. Crucially this includes opacity — if a previous fade-out left inline opacity: 0 behind, an entry animation that ends does not faithfully restore that stale 0 (which would make the element vanish); it clears the inline value and USS wins.
    • Snapshot-restore — every other animated property is restored to the exact inline value the element had when Play() was called: all colors (background-color, color, border colors, tint) and all length properties (width/height, min/max, margins, paddings, offsets, border widths/radii, font-size, flex-basis). These can be legitimate component state (ProgressBar owns its fills' inline colors), so a flash on an inline-colored element must return to the exact pre-flash color, not to USS.

    Together these make transient effects interrupt-safe: stopping an opacity pulse loop mid-dim returns to the USS opacity, a size-pulse loop cannot leave a card stuck mid-grow, and a color flash lands back on the exact pre-flash color. The property table lives in one internal registry (InlineStyleRestore); an animated property with no registry entry logs a warning instead of silently leaking.

  • Persist — the final values stay. Use for exit animations that must remain hidden/moved (FadeOut and SlideOut set this automatically).

Instant setter steps (SetOpacity, SetScale, SetTranslate, SetRotate, SetBackgroundPosition) count as animating their property, so RestoreOnEnd cleans up after them too.

One active player per element. Playing a sequence on an element stops whichever player was animating it (its restore runs first). With several juice systems in play — hover juice, attractor arrivals, bar effects — two effects aimed at the same element preempt each other by design; aim them at different elements (a wrapper vs. the content) when they must run simultaneously.

Under the hood

  • Native CSS transitions. Steps write inline styles plus USS transitions; completion is driven by UI Toolkit's TransitionEndEvent through an internal one-shot awaiter that auto-unsubscribes and won't hang if the element detaches mid-animation — the sequence advances instead.
  • UsageHints.DynamicTransform. When a sequence animates transform properties (translate / scale / rotate), the player ORs the required UsageHints onto the target element before playing, keeping per-frame transform changes off the mesh-regeneration path.

TransitionSequence Builder

The TransitionSequence class provides a fluent API for building animations:

Style Animations

csharp
var sequence = new TransitionSequence() // Opacity .Opacity(1f, 0.3f) .Opacity(0f, 0.3f, EasingMode.EaseOut) // Scale (uniform or separate x/y) .Scale(1.2f, 0.2f) .Scale(new Vector2(1.5f, 1f), new TransitionTimingConfig(0.3f, EasingMode.EaseInOut)) // Translate (pixels or percent) .Translate(0f, -20f, 0.3f) .TranslatePercent(0f, -50f, 0.3f) // Rotate (degrees) .Rotate(360f, 0.5f) // Colors .BackgroundColor(Color.red, 0.2f) // Border colors are per-side: BorderTopColor / BorderRightColor / BorderBottomColor / BorderLeftColor .Color(Color.blue, StyleColorProperty.BorderTopColor, new TransitionTimingConfig(0.2f)) // Length properties .Width(new Length(200, LengthUnit.Pixel), new TransitionTimingConfig(0.3f)) .Height(new Length(50, LengthUnit.Percent), new TransitionTimingConfig(0.3f)) // Background position (scrolling textures, marching stripes) .BackgroundPosition(64f, 0f, 0.8f, EasingMode.Linear) .Build();

Background position (scrolling textures)

BackgroundPosition animates background-position-x/y — combined with a repeating background-image this gives marching stripes, energy flows, and conveyor effects. SetBackgroundPosition is its instant counterpart; a Loop sequence of jump to 0 → animate to one tile width makes a seamless sawtooth scroll:

csharp
var stripeScroll = new TransitionSequence() .SetBackgroundPosition(Vector2.zero) // instant jump .BackgroundPosition(64f, 0f, 0.8f, EasingMode.Linear) // one tile width .WithPlayMode(TransitionPlayMode.Loop) .Build();

Both are also available as serialized step types in TransitionSequenceAsset (BackgroundPosition / SetBackgroundPosition). The shipped ProgressBarStripeScroll preset is exactly this sequence.

Parallel Animations

Run multiple animations simultaneously:

csharp
var sequence = new TransitionSequence() .Together(sub => sub .Opacity(1f, 0.3f) .Scale(1f, 0.3f) .Translate(0f, 0f, 0.3f) ) .Build();

Delays and Callbacks

csharp
var sequence = new TransitionSequence() .Opacity(0f, 0.3f) .Delay(0.5f) // Wait 500ms .Callback(() => Debug.Log("Halfway done!")) .Opacity(1f, 0.3f) .Build();

Repeat

Repeat a group of steps a fixed number of times:

csharp
var sequence = new TransitionSequence() .Repeat(3, r => r .Scale(1.1f, 0.15f, EasingMode.EaseInOut) .Scale(1f, 0.15f, EasingMode.EaseInOut) ) .Build();

If count is 1, the nodes are inlined directly without a repeat wrapper.

PlayOverlay (UI Particles / VFX)

Fire a particle effect or VFX overlay on a UI element at a specific point in your animation sequence — no custom code or Callback needed. This integrates directly with the UIRender system.

Fluent API:

csharp
var sequence = new TransitionSequence() .Scale(1.2f, 0.2f, EasingMode.EaseOut) .PlayOverlay(sparksPrefab) // fire-and-forget .Scale(1f, 0.2f, EasingMode.EaseIn) .Build();

With settings:

csharp
var overlaySettings = new UIOverlaySettings.Builder() .WithAnchor(OverlayAnchor.Center) .WithOverflowVisible(true) .Build(); var sequence = new TransitionSequence() .PlayOverlay(sparksPrefab, renderSettings, overlaySettings) .Build();

Blocking mode — wait for particles to finish before continuing:

csharp
var sequence = new TransitionSequence() .PlayOverlay(sparksPrefab, waitForDrain: true) .FadeOut() // only starts after particles fully drain .Build();

PlayOverlayInstance — borrow an existing scene GameObject instead of instantiating a copy:

csharp
var sequence = new TransitionSequence() .PlayOverlayInstance(existingParticleSystem) .Build();

Target a child element instead of the sequence root:

csharp
var sequence = new TransitionSequence() .PlayOverlay(sparksPrefab, targetElementName: "icon-slot") .Build();

Inspector (ScriptableObject): In a TransitionSequenceAsset, add a node and set its type to Overlay. Configure:

  • Content — prefab (or scene instance if "Use Instance" is checked)
  • Render Settings — camera/resolution settings for the render-to-texture
  • Overlay Settings — anchor, width/height percentages, overflow visible
  • Target Element Name — optional child element to overlay onto (empty = sequence target)
  • Wait For Drain — when checked, blocks the sequence until the overlay finishes

Handle cleanup: Overlay handles are automatically tracked by TransitionSequencePlayer and released when the player is stopped, reset, or loops. You don't need to manage handle lifetimes manually.

OnComplete

Register a completion callback directly on the builder. It fires after all other nodes:

csharp
var sequence = new TransitionSequence() .FadeIn() .Bounce() .OnComplete(() => Debug.Log("All done!")) .Build();

Note: .OnComplete() on the builder is separate from TransitionSequencePlayer.OnComplete event. The builder version is appended as a final Callback node during Build(), while the player event fires externally after sequence completion.

ClearInlineStyles

After animating, you may want to revert inline style overrides back to USS-defined or default values. Use ClearInlineStyles with the StyleProperty enum for type-safe cleanup:

csharp
var sequence = new TransitionSequence() .Scale(1.2f, 0.2f) .Scale(1f, 0.2f) .ClearInlineStyles(StyleProperty.Scale) .Build(); // Clear multiple properties at once var sequence2 = new TransitionSequence() .Together(sub => sub .Scale(1.2f, 0.3f) .Opacity(0.5f, 0.3f) ) .Together(sub => sub .Scale(1f, 0.3f) .Opacity(1f, 0.3f) ) .ClearInlineStyles(StyleProperty.Scale, StyleProperty.Opacity) .Build();

StyleProperty Enum

ValueCSS Property
Translatetranslate
Scalescale
Opacityopacity
Rotaterotate
BackgroundColorbackground-color
Widthwidth
Heightheight
Colorcolor

A string-based overload is also available for properties not covered by the enum:

csharp
.ClearInlineStyles("border-width", "margin-left")

Play Modes

csharp
var sequence = new TransitionSequence() .Scale(1.1f, 0.2f) .Scale(1f, 0.2f) .WithPlayMode(TransitionPlayMode.Loop) // Loop forever .Build(); // Or loop a specific number of times var sequence = new TransitionSequence() .Rotate(360f, 1f) .WithPlayMode(TransitionPlayMode.LoopCount) .WithLoopCount(3) // Rotate 3 times .Build();

Multi-Property Animation

Animate multiple properties with shared timing:

csharp
var timing = new TransitionTimingConfig(0.3f, EasingMode.EaseOutBack); var sequence = new TransitionSequence() .Multi(timing, new OpacitySetter(1f), new ScaleSetter(new Vector2(1f, 1f)), new TranslateSetter(Translate.None()) ) .Build();

Code-Based Presets

The TransitionSequence builder includes built-in preset methods for the most common animations. These are chainable and handle setup/cleanup internally (e.g. clearing inline styles).

Bounce

Scales up past target, settles, dips below, then returns to 1. Each phase has its own duration for fine-tuned timing. Clears inline scale automatically.

csharp
var sequence = new TransitionSequence() .Bounce(peakScale: 1.2f, settleScale: 1.15f, dipScale: 0.95f, peakTime: 0.12f, settleTime: 0.1f, holdTime: 0.2f, dipTime: 0.08f, returnTime: 0.1f) .Build();
ParameterTypeDefaultDescription
peakScalefloat1.2Scale peak
settleScalefloat1.15Scale after peak, before dip. Set equal to peakScale to skip
dipScalefloat0.95Scale dip below 1
peakTimefloat0.12Duration of the initial scale-up in seconds
settleTimefloat0.1Duration of the settle step in seconds
holdTimefloat0Optional pause after settle before scale-down begins. 0 = no pause
dipTimefloat0.08Duration of the dip step in seconds
returnTimefloat0.1Duration of the return-to-normal step in seconds
easingEasingModeEaseOutEasing for all scale steps

FadeIn / FadeOut

csharp
// Instantly sets opacity to 0, then animates to 1 new TransitionSequence().FadeIn(duration: 0.3f).Build(); // Animates opacity to 0 new TransitionSequence().FadeOut(duration: 0.3f).Build();

SlideIn / SlideOut

Animates translation from/to an offset. SlideIn clears inline translate at the end.

csharp
// Slide in from the left by 50px new TransitionSequence() .SlideIn(distancePx: 50f, direction: SlideDirection.Left, duration: 0.3f) .Build(); // Slide out to the right by 80px new TransitionSequence() .SlideOut(distancePx: 80f, direction: SlideDirection.Right, duration: 0.3f) .Build();

SlideDirection values: Left, Right, Top, Bottom.

Pulse

Scales up then back to 1, repeated count times. Clears inline scale at the end.

csharp
new TransitionSequence() .Pulse(scale: 1.1f, duration: 0.15f, count: 2) .Build();

Chaining Presets

Presets are regular builder methods — chain them with any other step:

csharp
var sequence = new TransitionSequence() .FadeIn(duration: 0.3f) .SlideIn(distancePx: 30f, direction: SlideDirection.Bottom) .Bounce() .OnComplete(() => Debug.Log("Entrance complete!")) .Build();

Preset Animations Library

Luna includes 53 ready-to-use animation presets organized into categories:

Base (17 presets)

PresetDescription
FadeIn / FadeOutOpacity transitions
ScaleIn / ScaleOutScale from/to zero
SlideInUp/Down/Left/RightSlide from off-screen
SlideOutUp/Down/Left/RightSlide to off-screen
FlipIn / FlipOut3D flip effect via scaleX
RotateIn / RotateOutRotation with opacity
BounceInBouncy scale entrance

Attention (8 presets)

PresetDescription
Shake / ShakeVerticalHorizontal/vertical shaking
WobbleRotation wobble
JelloElastic jiggle effect
RubberRubber band stretch
FlashRapid opacity flashes
HeartbeatPulsing scale
ColorPulseBackground color pulse

Feedback (7 presets)

PresetDescription
PopQuick scale bump
PopBounceIn / PopBounceOutBouncy pop entrance/exit
PressButton press effect
Lift / LiftResetHover lift effect
SquishSquash and stretch

Loading (4 presets)

PresetDescription
SpinContinuous rotation
BounceVertical bounce
BlinkOpacity blink
BreatheGentle scale pulse

Modals (9 presets)

PresetDescription
ZoomIn / ZoomOutScale with opacity
RiseIn / RiseOutRise from bottom
DropInDrop from top with bounce
SwingIn / SwingOutRotation swing
FoldY / UnfoldYVertical fold effect

Notifications (4 presets)

PresetDescription
SlideInTop / SlideInBottomNotification slide in
SlideOutTop / SlideOutBottomNotification slide out

Transitions (4 presets)

PresetDescription
PushInLeft / PushInRightPage push entrance
PushOutLeft / PushOutRightPage push exit

UIView Integration

UIViews can automatically play transition animations on fade events. See UIView Transitions for details.

csharp
// Add transition to UIView. An ElementSelector with no filters targets // the UIView's parent element — set Name / ClassName to scope to a // child or to multiple matches. myUIView.AddTransition(new UIViewTransitionEntry { Animation = popBounceInAsset, Trigger = FadeEventTrigger.FadeInStart, Target = new ElementSelector { Name = "modal-content" } });

TransitionTimingConfig

Encapsulates timing configuration for transitions:

csharp
var timing = new TransitionTimingConfig( duration: 0.3f, // Duration in seconds easing: EasingMode.EaseOut, // Easing function delay: 0.1f // Delay before start );

Available Easing Modes

All Unity UI Toolkit easing modes are supported:

  • Linear
  • Ease, EaseIn, EaseOut, EaseInOut
  • EaseInSine, EaseOutSine, EaseInOutSine
  • EaseInCubic, EaseOutCubic, EaseInOutCubic
  • EaseInCirc, EaseOutCirc, EaseInOutCirc
  • EaseInElastic, EaseOutElastic, EaseInOutElastic
  • EaseInBack, EaseOutBack, EaseInOutBack
  • EaseInBounce, EaseOutBounce, EaseInOutBounce

Example: Button Hover Effect

PanelRenderer delivers the visual tree asynchronously, so element queries belong inside its reload callback — never in Start. The callback fires immediately on registration when the panel is already mounted, and again after every rebuild (rebuilds produce a fresh tree, so re-query and re-register there):

csharp
public class ButtonHoverEffect : MonoBehaviour { [SerializeField] private PanelRenderer _panelRenderer; [SerializeField] private TransitionSequenceAsset _hoverIn; [SerializeField] private TransitionSequenceAsset _hoverOut; private TransitionSequencePlayer _playerIn; private TransitionSequencePlayer _playerOut; private VisualElement _button; void Awake() { if (_panelRenderer == null) _panelRenderer = GetComponent<PanelRenderer>(); _playerIn = _hoverIn.CreatePlayer(); _playerOut = _hoverOut.CreatePlayer(); _panelRenderer.RegisterUIReloadCallback(OnUIReload); } void OnDestroy() { if (_panelRenderer != null) { _panelRenderer.UnregisterUIReloadCallback(OnUIReload); } } private void OnUIReload(PanelRenderer renderer, VisualElement root, int version) { _button = root.Q<Button>("my-button"); if (_button == null) return; _button.RegisterCallback<PointerEnterEvent>(e => { _playerOut.Stop(); _playerIn.Play(_button); }); _button.RegisterCallback<PointerLeaveEvent>(e => { _playerIn.Stop(); _playerOut.Play(_button); }); } }

On a view backed by a UIViewComponent you can skip the callback plumbing: override OnUILoaded(VisualElement root) (or use WhenUILoaded(() => ...)) and wire the button there.

Example: Loading Spinner

csharp
// Create a continuous spinning animation var spinSequence = new TransitionSequence() .Rotate(360f, 1f, EasingMode.Linear) .WithPlayMode(TransitionPlayMode.Loop) .Build(); var spinner = new TransitionSequencePlayer(spinSequence); spinner.Play(loadingIcon); // Stop when loading completes spinner.Stop();

Demo Scenes

The Showcase sample includes one demo scene:

  • TransitionPresetGallery: Visual gallery of all 53 preset ScriptableObject animations

Import the Showcase sample to explore it. For wiring sequences to screen fade events, see UIView Transitions.

See also

  • Stagger Entry — cascade a view's children in one after another with a single multi-target transition entry.
  • TransitionToggleRepeat — loop a USS class toggle for breathing panels and pulsing buttons.
  • UIView Transitions — wire sequences to screen fade events.
  • ProgressBar transition effects — assign sequence assets to a bar's fill/drain/complete/pulse slots for punch, shake, flash, and low-HP juice.
  • UI Render — the system behind PlayOverlay's particles-on-UI.

All 53 presets ship in the box — get Luna on the Asset Store.

Settings

Theme

Light

Contrast

Material

Dark

Dim

Material Dark

System

Sidebar(Light & Contrast only)

Light
Dark

Font Family

DM Sans

Wix

Inclusive Sans

AR One Sans

Direction

LTR
RTL