Animations

Calm, tuned motion out of the box

Every element has a show and a hide preset, chosen in the Elements tab. Entrances ease out, exits are shorter and ease in, and every show and hide fades, so nothing pops in or out.

The ten presets

Each card loops: show, hold, hide.

What makes them feel good

Base pose

Each object's resting position, scale and alpha are recorded once. Every preset starts from it and returns to it, so repeated or interrupted animations never drift.

No skipped frames

A long frame, like the one where a scene loads, slows an animation slightly instead of jumping past half of it.

One animation per channel

A new animation stops older ones driving the same thing (position, scale, alpha). A button pop doesn't stop a slide; a new show stops a running hide.

No flash

Right after its scene loads, an element is put in its show start pose, in the same frame as Populate.

Overlapping transitions

In a screen switch, the old state hides while the new one shows: no empty frame in between.

Unscaled time

UI keeps animating while the game is paused (Time.timeScale = 0).

Override the animation for one call

UiTransition replaces the presets for a single ShowState or HideUI call, without changing the elements' configuration. Shared elements stay still either way.

UiTransition
// Show the Store with a zoom this time, whatever its elements are configured to do.
await UiManager.ShowState<StoreUiState>(context, transition: UiTransition.ShowWith(TweenPresets.ScaleKey));

// Close the top state by sliding it out to the right.
await UiManager.HideUI(UiTransition.HideWith(TweenPresets.SlideRightKey));

// Both sides at once: the new screen enters moving left, the old screen leaves moving left.
await UiManager.ShowStateByKey("ProfileUiState", context, false,
    new UiTransition(TweenPresets.SlideLeftKey, TweenPresets.SlideLeftKey));

Recipe: tabs that slide in the direction you tap

From the showcase's ShowcaseNavigator
// Moving to a tab on the right slides everything left (like swiping), and the other way round.
private static UiTransition GetTabTransition(ShowcaseTab from, ShowcaseTab to)
{
    if (from == to)
    {
        return UiTransition.Default;
    }

    string preset = (int)to > (int)from ? TweenPresets.SlideLeftKey : TweenPresets.SlideRightKey;
    return new UiTransition(preset, preset);
}

Button feedback

The pops and shake also work on any transform, without fading, as feedback:

Feedback
await TweenPresets.ClickPop(buyButton.transform);    // quick 1.08x pop
await TweenPresets.SettlePop(coinIcon.transform);    // gentle 1.04x pulse
await TweenPresets.ShakeLocal(buyButton.transform);  // "no": not enough coins

Your own animation

ShowAsync, HideAsync and PrepareForShow are virtual. Run extra motion next to the preset, or replace it completely. Tween.RunAsync gives you the same interruptible, frame-capped loop the presets use.

Stagger the cards in after the screen's own preset
public class StoreUiElement : UiElement
{
    [SerializeField] private RectTransform[] cards;

    public override void PrepareForShow()
    {
        base.PrepareForShow();
        for (int i = 0; i < cards.Length; i++)
        {
            cards[i].localScale = Vector3.zero;
        }
    }

    public override async Task ShowAsync(string presetKey = null)
    {
        Task screen = base.ShowAsync(presetKey);
        for (int i = 0; i < cards.Length; i++)
        {
            RectTransform card = cards[i];
            await Tween.RunAsync(card, 0.18f, TweenCurves.EaseOut, true, CancellationToken.None, null, eased =>
            {
                card.localScale = Vector3.one * eased;
            });
        }

        await screen;
    }
}
No-code choreography in the showcase

The showcase app adds Inspector components on top of these hooks: RevealOnShow, StaggeredReveal, FillBar, CountUpText, LoopMotion, PressFeedback and more. Designers add them to objects and the element plays them with its preset. They live in the sample project, so copy the ones you like. See Showcase.