Using DOTween to animate the 'Opacity' property in a VisualElement

For anyone who’s familiar with ‘DOTween’ and/or UIElements, I’m trying to tween the opacity of a VisualElement from 0 → 1, 1 → 0 and it doesn’t seem to be working. Is this because of my code, DOTween, or because this is not yet supported in UIElements? This is the function I made:

void MenuTransition(VisualElement menu, float t)
{
    float opacity = menu.style.opacity.value;
    if(menu.style.opacity.value == 0)
    {
        DOTween.To(() => opacity, x => opacity = x, 1f, t);
        menu.focusable = true;
    }
    else if(menu.style.opacity.value == 1)
    {
        menu.focusable = false;
        DOTween.To(() => opacity, x => opacity = x, 0f, t);
    }
}

UI Toolkit ships with a dedicated tweening api

(experimental but works fine at the time of writing this post)

189425-gif-01122021-13-09-16.gif

using UnityEngine;
using UnityEngine.UIElements;

public class RuntimeUiAnimator : MonoBehaviour
{
	[SerializeField] UIDocument _uiDocument = null;
	void OnEnable ()
	{
		var ROOT = _uiDocument.rootVisualElement;

		ROOT.Query("animated").ForEach( (next) => {
			var zeroToOne = next.experimental.animation
				.Start( 0f , 1f , 1000 , (ve,value) => ve.style.opacity = value )
				.Ease( UnityEngine.UIElements.Experimental.Easing.InOutQuad )
				.KeepAlive();
			var oneToZero = next.experimental.animation
				.Start( 1f , 0f , 1000 , (ve,value) => ve.style.opacity = value )
				.Ease( UnityEngine.UIElements.Experimental.Easing.InOutQuad )
				.KeepAlive();
			var idle = next.experimental.animation
				.Start( 1f , 1f , 1000 , (ve,value) => {} )
				.KeepAlive();
			
			zeroToOne.OnCompleted( ()=> idle.Start() );
			idle.OnCompleted( ()=> oneToZero.Start() );
			oneToZero.OnCompleted( ()=> zeroToOne.Start() );
			
			zeroToOne.Start();
		} );
	}
}

UIElements/UIToolkit supports all kinds of tweens. Just the ones that work.

float opacity = menu.style.opacity.value;
float opacity is a stack-level copy that becomes captured by value into these lambda expression objects (completely separate heap allocations). So reading and changing it’s value in a lambda is futile. In other words this:

DOTween.To(() => opacity, x => opacity = x, 1f, t);

Will be converted into ± this:

class lambda_getter_123456
{
    public float field1;
    public float Get =>  this.field1;
}
class lambda_setter_342738
{
    public float field1;
    public float Set =>  this.field1 = value;
}
 DOTween.To(
      new lambda_getter_123456{field1=opacity} ,
      new lambda_setter_342738{field1=opacity} ,
      1f , t
 );

_
What you may want to do instead is something like this:

var style = menu.style;
DOTween.To( () => style.opacity , x => style.opacity = x , 1f , t );

where style references a memory location that this ui system actually reads.