Nice text wrapping in UMG
Ever run into one of those things that seems like it should be simple, but isn’t? This week I’ve come across a couple of them, and after banging my head against the wall, I’ve arrived at solutions for both that “work for me”, and I hope they work for you too.
Typewriter solutions aren’t a hard thing to do with UMG in Unreal Engine. Here’s an example from our WIP game (The Black Cat).
Notice anything annoying? At the end of each line, UE5 decides to wrap half way through a word. This is just the built in behaviour doing what it should do. The problem is that we’ve added a typewriter script and the word-wrapping doesn’t come with clairvoyance built in. p.s. if you’re interested in how we did the typewriter script, let me know and I’ll do another post on that.
See how the typewriter starts a word on the next line if it’s not going to fit? Nice.
Initially I thought this would be a pretty easy thing to do, but it turns out there’s a few caveats that I tripped over on the path to working this one out (and as I’ve mentioned, unlike in web, I can’t just fork the library and change the behaviour).
The process we’ll follow is pretty simple:
Split our text into a string array.
Build our string up word-by-word, each time checking if our text box is wider than we want it to be
Force words that are too long onto the next line.
Here’s an example I prepared earlier:
The blue line represents our intended dialogue box size (200px)
By adding one word a time, we can check if the text block pushes beyond our intended size…
If it does, we can just replace the space with a line break, and bam, it fits again!
But let’s go through one step at a time…
First, we take the string we want to print out, and convert it to a String Array (splitting on the space). We then loop through the array, re-concatenating the words back together. Hang on, why the hell would we put them back exactly as they were? Just hold on a second, I’ll tell you!
NOTE: I should mention that as well as our “visible” text block, we also have a hidden text block. While we use the visible one so our player can see the typewriter, we use the second one for layout (it’s exactly the same dimensions, but is rendered with our full text in it - this forces our widget starts at the correct size, even though our visible text block is empty ).
Ok, so what we do here is one by one, add a word, and update the text block with the new sentence. Simple so far? Ok this is where the “magic” happens. When I say, “magic”, I actually just mean the bit that makes this not work the way I thought it would…
Turns out UMG doesn’t know how big anything is until it renders. Makes sense! Lucky for us we can force it to do a layout “prepass” in which it does exactly that (without the render). Once it’s done that, we can now get the “desired size” of our text block.
IMPORTANT: for this to work, make sure you have no set size on your text block (i.e. it’s not fixed size because it’s in a canvas or something) . You’ll also want to make sure there’s no Min Desired Width and that Auto Wrap Text and Wrap Text At are all unset.
Ok, we’re nearly there. Last thing to do; if our text box is wider than we’d like it be, what do we need to do? We need to throw in a line break. Below we do just that, we essentially find the last “space” and replace it with a line break.
Now we just rinse and repeat until we’ve done our entire string. Once it’s freshly padded out with line breaks, we can pass it off to our typewriter as usual, and it “just works”.
So, to recap:
UMG can’t tell you how big a widget it is until it has been rendered, but you can force it do a layout prepass anyway (obviously there are performance considerations! YMMV)
Split up the string and recombine it a bit at a time, always making sure your text box is not beyond whatever limits you set.
Enjoy!
The usual disclaimer: I’ve been a professional web developer for 25+ years, but I am new to Unreal Engine and game development in general. I’m no expert, but I’m always happy to help. If you have any questions, or if there’s anything else you’d like to see, just ask!