Currently seems like UI Toolkit runtime UI animation only have simple UI animation feature. Is there any ETA and plan to implement better and feature rich runtime UI animation and tooling that able to create complex animation? I would like to see tween animation tooling integrates into UI Toolkit runtime UI to enable high performance complex animation.
What do you mean by “tween animation tooling”?
In code, you can use LeanTween and similar libs (or plain coroutines) to interpolate from one value to another over time.
Example:
LeanTween.value(gameObject, 0, 1, 0.3f) // Animate value from 0 to 1 in 0.3 seconds
.setOnUpdate((float interpolatedValue) => // This is called every frame
{
visualElement.style.opacity = interpolatedValue;
// Set other values as you please, scale, rotate, ...
})
.setOnComplete(() => visualElement.RemoveFromHierarchy()) // This is called when the animation is complete
.setEaseInSine(); // Use a nice (non-linear) curve for the interpolation
Of course, Unity could provide more advanced, visual animation tooling like Animator and friends that can record, save and replay arbitrary animations based on keyframes and state machines. I imagine that such a tooling is a must-have for non-coders.
Is this planned? Or maybe could existing Unity tooling be re-used to animate VisualElements?
I expect something UI designer friendly visual editor like DOTween Pro but better than DOTween Pro so non-coder can create complex UI animation without involving programmer. You can say it’s supercharged Animator that using tween way to do UI animation.
Hi optimise, that’s a great question thanks for asking.
You’re correct, we decided to prioritize simple UI animations since it generally makes up most of animations found in UI, but we need to address the more complex scenarios.
We’re considering a few solutions, like providing CSS like animation features, supported by visual authoring workflows. Integration with the Timeline tool is another one, which would enable to orchestration of UI animations with other Scene objects.
If you had to describe the best way for non-programmers to do UI animation, what would it look like?
Something like this but even better that it’s a complete tooling without needing to involve any programmer. I expect even more integrated features that it will support Addressables by default out of the box and it will auto load and unload ui panels when out of camera screen.
Ah yes, I’m in complete agreement. This is also something we’d like to provide in the future but we have so much to do before. Great feedback thank you!
Just a note for future me and others. If you use transitions in your USS, you can add classes to animate after X ms using this function:
UiHelpers.cs
public static class UiHelpers
{
public static void DelayAddToClassList(VisualElement ui, string classToAdd = "animate", int delay = 100)
{
ui.schedule.Execute(() => ui.AddToClassList(classToAdd)).StartingIn(delay);
}
}
Usage in C#:
UiHelpers.DelayAddToClassList(ui);
And some transition declaration i USS:
.button {
transition-property: translate opacity;
transition-timing-function: ease-out linear;
transition-duration: 0.3s 0.3s;
transition-delay: 0.2s 0.2s;
translate: -10 0;
opacity: 0;
}
.button.animate {
translate: 0 0;
opacity: 1;
}
One more opinion: Since you try to replicate CSS already with providing CSS-like or equal properties via USS, users are expecting an identical feature set provided by USS (at least at some point in time). Wrt. animations this would mean the ability to define animations in USS (also with keyframes) as explained, e.g., in the Mozilla docs
Imagine setting the position in this approach
uiElement.style.translate = new StyleTranslate(new Translate(val, 0, 0));
Here we have to create 2 new structs every update. not the optimized way of tweeting.
I recently released a package that adds support to Timeline for animating UI Toolkit, you might find it useful: GitHub - mihakrajnc/UITTimeline: Support for animating UI Toolkit elements with Timeline.
Still in early stages, currently supports transform, opacity, visibility, display, and adding / removing classes, but I’ll be adding more styles and optimisations in the coming weeks.
I’ve made super small extension method to help with tweening arbitrary values on VisualElementScheduler.
Unless I’m missing something, LeanTween works on GameObjects. And most of animation libs work on that.
So I wanted to have simple value tweening without needing to expose everything to game object layer. Additionally, I wanted to change values besides stuff related to USS.
So e.g. here I’m tweening a value to change ProgressBar.value attribute.
var easedValue = new EasedValue(
starting: progressBar.value,
target: progressBar.value + 60f,
duration: 3f,
EasingFunctions.InOutCubic);
schedule.Animate((value) => progressBar.value = value.Current, easedValue);
Also, if you want to tween multiple values it supports that too. You can give array of EasedValues with different starting points, targets and durations.
Here’s the extension code:
public static class VisualElementSchedulerExtensions
{
public static void Animate(this IVisualElementScheduler scheduler, Action<EasedValue[]> action, EasedValue[] values)
{
float elapsed = 0f;
if (values == null || values.Length == 0)
{
Debug.LogWarning("No EasedValues provided for animation.");
return;
}
float highestDuration = values.Max(v => v.Duration);
void scheduledAction()
{
elapsed += Time.deltaTime;
foreach (var value in values)
{
value.Update(elapsed);
}
if (values.Any(v => v.IsChanged))
{
action.Invoke(values);
}
if ((elapsed / highestDuration) < 1f)
{
scheduler.Execute(scheduledAction);
}
}
scheduler.Execute(scheduledAction);
}
public static void Animate(this IVisualElementScheduler scheduler, Action<EasedValue> action, EasedValue value)
{
scheduler.Animate((values) => action.Invoke(values[0]), new[] { value });
}
}
Here’s the EasedValue code:
public class EasedValue
{
public EasedValue(float starting, float target, float duration, Func<float, float> easeFunc = null)
{
Starting = starting;
Target = target;
Current = starting;
Duration = duration;
if (Duration <= 0)
{
Debug.LogWarning("Duration must be greater than zero. Setting to 0.1f.");
Duration = 0.1f; // Default to a small duration if not set
}
EaseFunc = easeFunc ?? (t => t); // Default to linear if not set
}
public float Starting { get; }
public float Target { get; }
public float Current { get; private set; }
public float Duration { get; }
public bool IsChanged { get; private set; }
private Func<float, float> EaseFunc { get; }
public void Update(float deltaTime)
{
if (Current == Target)
{
IsChanged = false;
return;
}
var t = Mathf.Clamp01(deltaTime / Duration);
var newValue = t == 1 ? Target : Mathf.Lerp(Starting, Target, EaseFunc(t));
IsChanged = !Mathf.Approximately(Current, newValue);
Current = newValue;
}
}
Working so far for me. Might need to optimize or add additional functionality(e.g. delays, cancelations etc.), but I’m using this as a starting point.