I found a way to animate individual glyphs in UI Toolkit. All we have to do is to write a callback function for the action/event “PostProcessTextVertices” of the label. This function receives a list of all glyphs that we can iterate over and change their individual positions, uvs and tints. Here is the full source code of a script that animates every Label with the class name “wave-effect”.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class LabelWaveEffect : MonoBehaviour
{
[SerializeField] private float _frequency = 1.0f;
[SerializeField] private float _amplitude = 5.0f;
private List<Label> _labels;
private void Awake()
{
if (TryGetComponent(out UIDocument document))
{
_labels = document.rootVisualElement.Query<Label>(className: "wave-effect").ToList();
}
}
private void Start()
{
if (_labels != null)
{
foreach (Label label in _labels)
{
if (label != null)
{
label.PostProcessTextVertices += OnPostProcessVertices;
}
}
}
}
private void OnDestroy()
{
if (_labels != null)
{
foreach (Label label in _labels)
{
if (label != null)
{
label.PostProcessTextVertices -= OnPostProcessVertices;
}
}
}
}
private void Update()
{
if (_labels != null)
{
foreach (Label label in _labels)
{
if (label != null)
{
label.MarkDirtyRepaint();
}
}
}
}
private void OnPostProcessVertices(TextElement.GlyphsEnumerable glyphs)
{
int glyphIndex = 0;
foreach(TextElement.Glyph glyph in glyphs)
{
var vertices = glyph.vertices;
for (int i = 0; i < vertices.Length; i++)
{
var vertex = vertices[i];
vertex.position += _amplitude * Mathf.Sin(glyphIndex + Time.timeSinceLevelLoad * _frequency) * new Vector3(0.0f, 1.0f, 0.0f);
vertices[i] = vertex;
}
glyphIndex++;
}
}
}