Version: 6000.3+
The TextElement.PostProcessTextVertices API lets you modify the meshThe main graphics primitive of Unity. Meshes make up a large part of your 3D worlds. Unity supports triangulated or Quadrangulated polygon meshes. Nurbs, Nurms, Subdiv surfaces must be converted to polygons. More info
See in Glossary vertices of each glyph immediately before UI Toolkit renders them. You can use this callback to customize the position, tint, and UV coordinates of the text at a low level.
Each enumerated Glyph also exposes stable metadata that makes it possible to drive per-glyph effects from rich-text markup:
line — zero-based visual line number the glyph sits on.linkID — value of the enclosing <link=...> tag (the same string surfaced through PointerDownLinkTagEvent.linkID), or null when the glyph is not inside a <link>. All glyphs that belong to the same link span share the same value. <a href=...> hyperlinks are not reported through this field.kind — classifies the glyph as Character or Sprite.This example creates a custom Editor window that animates text by fading glyphs in and out when you press Spacebar. It uses the TextElement.PostProcessTextVertices API to modify the vertex data of the text glyphs.
You can find the completed files that this example creates in this GitHub repository.
This guide is for developers familiar with the Unity Editor, UI Toolkit, and C# scripting. Before you start, get familiar with the following:
Create a C# script for a custom Editor window. Use a scheduled job to track animation time. In the OnPostProcessTextVertices method, iterate through GlyphsEnumerable and adjust each vertex tint’s alpha channel until the fade completes.
Editor if you don’t have one.Editor folder, create a C# script named TextAnimation.cs with the following content:using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using TextElement = UnityEngine.UIElements.TextElement;
public class TextAnimation : EditorWindow
{
[MenuItem("Window/UI Toolkit/TextAnimation")]
public static void ShowExample()
{
TextAnimation wnd = GetWindow<TextAnimation>();
wnd.titleContent = new GUIContent("TextAnimation");
}
Label label;
float animationDuration = 1f; // in seconds
float elapsed = 0f;
float startRealtime;
IVisualElementScheduledItem animationJob;
bool isTextVisible = true;
public void CreateGUI()
{
VisualElement root = rootVisualElement;
var container = new VisualElement()
{
style =
{
flexGrow = 1,
top = 0,
bottom = 0,
right = 0,
left = 0
},
focusable = true // We can only receive key events on a window with a focusable element.
};
label = new Label("Hello ❤️ World!") { style = { flexGrow = 1, fontSize = 24, unityTextAlign = TextAnchor.MiddleCenter } };
container.Add(label);
root.Add(container);
rootVisualElement.RegisterCallback<KeyDownEvent>(evt => OnSpacebarPressed(evt), TrickleDown.TrickleDown);
label.PostProcessTextVertices += OnPostProcessTextVertices;
const int targetHz = 60;
animationJob = label.schedule.Execute(UpdateTime).Every(1000 / targetHz);
animationJob.Pause(); // Pause the job until the animation starts
}
private void UpdateTime()
{
elapsed = Mathf.Min(Time.realtimeSinceStartup - startRealtime, animationDuration);
if (elapsed >= animationDuration)
{
elapsed = animationDuration; // Cap at max duration
animationJob.Pause();
}
label.MarkDirtyRepaint();
}
public void OnSpacebarPressed(KeyDownEvent evt)
{
if (evt.keyCode != KeyCode.Space || animationJob.isActive)
return;
elapsed = 0f;
startRealtime = Time.realtimeSinceStartup;
animationJob.Resume();
isTextVisible = !isTextVisible;
}
void OnPostProcessTextVertices(TextElement.GlyphsEnumerable glyphs)
{
int glyphsToToggle = (int)(elapsed * glyphs.Count / animationDuration);
int toggled = 0;
foreach (TextElement.Glyph glyph in glyphs)
{
if (toggled++ >= glyphsToToggle)
break;
var verts = glyph.vertices;
for (int i = 0; i < verts.Length; i++)
{
var v = verts[i];
var tint = v.tint;
tint.a = isTextVisible ? (byte)255 : (byte)0;
v.tint = tint;
verts[i] = v;
}
}
}
}
Test the text animation sample in the custom Editor window.
By default vertices[i].tint only controls the face color of a glyph. To animate
the outline and text-shadow tints per glyph, call
Glyph.SetTints inside your
PostProcessTextVertices callback:
m_TextElement.PostProcessTextVertices += (glyphs) =>
{
int i = 0;
foreach (var glyph in glyphs)
{
var t = (i++ / (float)glyphs.Count); // 0..1 across the line
glyph.SetTints(
outline: new Color(1, 0, 0, t),
shadow: new Color(0, 0, 1, t));
}
};
SetTints takes nullable outline and shadow arguments — pass null for either
field to leave it at the element’s baseline (unityTextOutlineColor /
textShadow.color).
[!IMPORTANT] Calls to
SetTintsare not additive across calls on the same glyph. Each call rebuilds the glyph’s effective settings from the element baseline, so callingglyph.SetTints(outline: red)and thenglyph.SetTints(shadow: blue)in sequence reverts the outline to baseline. To set both outline and shadow on the same glyph, pass both arguments in one call:glyph.SetTints(outline: red, shadow: blue).