You can do some quite nice text effects with Unreal Engine’s UMG Rich Text Block. Coloured text, outlines, shadows, even embed images in the text. But what if you wanted to do things like this to your text:
When I was looking for information on how to do this kind of animated effect, I didn’t find very much. So now I’ve figured it out, here’s the information I wish I’d been able to find.
Setting Up Animated Text Styles
The first thing to do is set up your Rich Text styles as you usually would,
following the official Rich Text Block documentation.
Short version, it’s just a Datatable of row type Rich Text Style Row, and the
key for each row is the tag you use in your text to delimit text of that style,
The important part for our animated text is to use a Font Material; this allows us a great amount of control over the shading and transformation of text with this specific tag. The official documentation for Font Materials is pretty sparse and completely undersells the capabilities of this feature!
You assign the font material in the details of your style row, like this:
If you’re using outlines, you should set the same font material for that as well. Outlines are automatically expanded and their vertex colours set to reflect the outline colour so the same material will generally work for both; with some caveats.
But, how do you write these font materials?
Creating Font Materials To Animate Vertices
How precisely you modify the positions of glyph vertices in a rich text block is very badly documented; not at all, in fact. So here we go:
We’ll create a master material to serve as the basis for our animated text effects.
- Create a new material
- Select the output node
- In the Details tab, set:
- Material Domain: User Interface
- Blend Mode: Opaque, unless you want partially transparent text
Setting the blend mode to opaque might seem odd, because surely the text glyphs are alpha masked? Well, at least at the point that the Font Material is invoked, the glyph shapes do not require alpha rejection. I’m not sure of the exact pipeline but I suspect the glyph shape masking comes after this Font Material step.
This blog is mostly about moving text around in an animated fashion, we won’t spend any time colouring it in any differently. So for the “Final Color” output, we’ll just use the vertex colour, which will be the face colour or the outline colour, depending on what context it’s being run in.
The output we’re most interested in is “Screen Position”. If you assign nothing to it, the text will render in place as usual. But how do we calculate modified positions?
The answer is not what you would think. If you right-click the material canvas to create a new node, there is one called “ScreenPosition”, so you’d naturally assume that you should begin with that. But nope, that would be far too logical 😜
If fact, the correct coordinates to use are “Absolute World Position”, which you can find named “WorldPosition” in the create node list. Yes, really, this will essentially create the same no-op result as not assigning anything to “Screen Position”:
It took me far too long to figure that out, because it’s not documented anywhere as far as I can tell, and is completely unintuitive. Hey ho I guess… 😣
My First Text Animation
OK so now we know our starting point, it’s fairly easy to start experimenting with altering those values. Lets make the tagged text float up and down as a single unit for now, like this:
The way we achieve this is to feed time, plus some tweakable parameters, into a sine wave, and use that to offset the vertex positions of the text in the Y direction:
Of course we can then create material instances of this with different variables of Speed and Magnitude to tweak how it looks. The colour of the text face and outline are coming from the colours in the rich text style, which arrive via vertex colours. To repeat, this material is being executed separately for the font face and the font outline, but because we’re using consistent calculations they stay together while animating - this can be more difficult as your effects become more extravagant.
Varying animation across words / phrases
OK so that’s fine for animating the text as a single block, but what if we want to do something more interesting, like making each letter move at a slightly different offset, making something like a “wave” effect? For that, we need more variables than just time, we need something that varies across the different letters in the text.
We actually have two things we can draw on to add variation:
If we use the position on the screen, every letter (in fact, every vertex of every letter) has a unique combination of X and Y that we can use to add variation.
So we use “Absolute World Position” again but as a variant to our animation as well as a position base. It just needs to be scaled down or fractional parts used to make it useful. Here’s an example:
By changing the “Position Sine Y Scale”, you can change the wavelength of the wave that the text follows. With the value shown here we get this:
Notice how, because each vertex is being animated separately, the text itself actually distorts to follow the wave, rather than each letter just moving up and down, which I really like.
Using Texture Coordinates
The other thing you can do to vary things within the text is to use the UVs. However, there are a couple of things to understand. Firstly, the range of UVs depends on how you tag your text. Let’s take this text:
<Debug>Some text where we enclose the entire sentence in a single tag.</> <Debug>Some</> <Debug>text</> <Debug>where</> <Debug>every</> <Debug>word</> <Debug>is</> <Debug>separately</> <Debug>tagged</>
The UV pattern for this text is:
Notice how there’s a full range of UVs for each tagged area; on the first line, the UVs stretch across the entire sentence, but on the second line there’s a complete UV range per word. So it appears that the way UE renders text means that each tagged area will have its own UV space.
This can be useful if you want to animate effects that match at the start / end of tags, regardless of where they are in physical space. Re-implementing the offset material like this:
To drive Screen Position you must use UV1, not UV0. You can do this by selecting the texcoord node and changing Coordinate Index to 1.
This will not agree with anything you feed into Final Color, which seems to be UV0! For example, for this debug view, I passed UV0 into “Final Color”. Simple! BUT, if you use UV0 to alter Screen Position, it won’t do what is suggested by that.
I assume what’s happening is that UV1 contains these tag ranged texture coordinates in the vertex shader (which is what Screen Position uses), but the pixel shader puts them in UV0 (which is what Final Color is driven by). Very confusing until I figured that out!!
Bottom line: Any inputs to Screen Position must use UV1, not UV0!!
By adding noise components and other kinds of displacements, you can create all sorts of effects like the ones shown at the top of this article using just a small number of parameters. Doing so can give your dialogue and other text driven aspects of your game more character and customisable emphasis.
I hope this blog post helps you do that, I sure wish someone else had written something like this before I started! 😄