Using the built-in text UI, is there a way to animate only a selected number of letters?
What I want to do is something like the word "you" in this video and "I'd" in this one
Using the built-in text UI, is there a way to animate only a selected number of letters?
What I want to do is something like the word "you" in this video and "I'd" in this one
As per DMGregory's suggestion in the comments, I propose a solution using the <voffset>
tag for rich text. It's a little bit elaborate code-wise but doesn't require you to work with shaders at all.
We begin by creating a Text - TextMeshPro UI element in the Hierarchy. Change the text, font, colour and alignment as you wish. By now, we have a simple, static text:
<voffset>
tagThe <voffset>
tag changes the offset of the baseline. We can enclose the word 'world' between tags and add a value in pixels or ems. The enclosed text will be lifted
If we change the text content to Hello, <voffset=10px>world</voffset>!
the result is the following:
First of all, we must reference the TextMeshPro Component, so that we can access its text
property. We do that with a class property and a function call at script startup:
TextMeshProUGUI meshText;
meshText = GetComponent<TextMeshProUGUI>();
We can animate the vertical movement via a sine function over time. We want control over the function's frequency and amplitude, thus limiting "how fast" and "how high" the text will float.
We define the following class properties:
public float frequency = 1f; // frequency factor
public float amplitude = 10f; // pixel factor
Now we can compute an offset value over time with parameters:
float offset = Mathf.Sin(Time.time * frequency) * amplitude;
We will feed this value to the text in each frame to update the animation.
To update the text and only change the <voffset>
value, we can search for a substring of type <voffset=*px>
where *
is a number, but this would require using regular expressions, which is likely overkill.
A simpler approach exists. Substring replacement is easy if we know the substring to replace in advance. Then, we can do the following:
text
property.string sourceString;
sourceString = meshText.text;
string newString = sourceString.Replace(
"<voffset=0px>", "<voffset=" + offset.ToString() + "px>");
meshText.text = newString;
Let's put everything together and in place. Our class should look like this:
using UnityEngine;
using TMPro;
public class Baseline : MonoBehaviour {
public float frequency = 5f;
public float amplitude = 10f;
string sourceString;
TextMeshProUGUI meshText;
void Start() {
meshText = GetComponent<TextMeshProUGUI>();
sourceString = meshText.text;
}
void Update() {
float offset = Mathf.Sin(Time.time * frequency) * amplitude;
string newString = sourceString.Replace("<voffset=0px>", "<voffset=" + offset.ToString() + "px>");
meshText.text = newString;
}
}
Now we can assign this script to the GameObject with a Text Mesh Pro - Text (UI) Component, tweak the parameters and test it:
This problem occurs because <voffset>
adjusts the line height to accommodate the displaced text. We can fix that by selecting the Capline Alignment option from the Inspector:
And the result is what we expect:
Unfortunately, this simple solution isn't enough for multi-line text with animation:
In such a case, we simply need to force the overall text line-height to be constant for the whole text by adding the <line-height>
tag at the beginning of our string:
<line-height=100%>Hello, <voffset=0px>world</voffset>!
How are you?
Now everything does work!
This is a very simple approach to animating a single word in a text string. You can extend these functionalities to animate individual letters in a word, by enclosing each character between <voffset>
tags and adding a phase offset to each letter so that they float at the same speed and height but different timing.