One element of the old-style RPGs we all know and love, and that persist in video games to this day, are the dialog boxes where the text appears letter by letter. Why these exist exactly is not clear. If you think about it they don’t make that much sense. But they are part of video game history and culture and by Jove I was going to make sure Piczle Cross Adventure had such a dialog system!
Text in Unreal Engine 4 can be an unpredictable and unwieldy beast. Luckily, as everything is on pause when the dialogues occur, I can use some outlandish and potentially inefficient methods to recreate the animated dialog boxes we all love so much.
Behind this relatively simple looking interface lies a bit of a mess of a script. Each letter is individually drawn and animated by the script. I could have just created a string in a textbox and after a delay add a new letter to such a string and redraw it. This would have meant UE4 could do a proper word-wrap and it’d be a fairly simple script. However, I wanted each individual letter to animate into position and, as UE4 has no real rich text options, add colours and animated words, a little like Banjo Kazooie’s fun dialogs.
Each letter is its own widget, or rather a single widget with some exposed variables which I drive from the main dialog widget. The dialog widget itself is basically three empty horizontal boxes into which I add these letter widgets one by one. This is how they each animate individually and can even have different colours and animated effects once they’ve appeared.
First things first. Getting the text strings is an easy matter of looking up an ID in a datatable I’ve made which contains all the dialogs. If I need a dialog to appear, I just tell it the ID of the dialog I want and spawn a dialog actor, which contains the dialog widget. The text gets copied out of the datatable into a working variable string.
To figure out what letter I want to add to the horizontal box I check the left-most character in the string. This is the moment I can look for special effects. If the next character is a square bracket [ I will skip adding a letter and instead grab the first 3 characters in the string. These three will contain a square bracket open [, a letter or number, and a close bracket ].
I’ve decided that each number or letter represents a certain effect. E.g.  means that the text following it will be red, instead of black, or a [A] means the text will animate in a wave pattern once added. These effects can be cumulative. By adding [A] to a string I can make the text that follows it be both red and animate in a wave pattern. Then I cap it off with a  which resets everything back to static, black text.
On top of these special effects I have also included the possibility of adding graphical representations of controller buttons. Rather than having to write “the A button” I simply replace it with a graphic of the A button. For this I created an arbitrary code. A [q] in the string means to show the A button graphic, [w] the B button,, etc. all the way up to directional pad, menu buttons, etc. This was I can easily port the game to other consoles and only need to change the graphics for the buttons, and not change any text. It also means I don’t have to localise console button terminology.
Once everything has been set up I finally add a letter to the horizontal box. I create a letter widget and send it all required information; which letter, what colour the letter needs to be shown in, if it is animated, or if the player has pressed “skip” (or in the option menu has selected fast text).
The skip is important because as the letter widget is created the letter is animated into place. If the player has skipped this part, no animation is played and the letter is immediately shown.
If there is no skip, there will be a short interval before the process repeats itself for the next letter. This continues until there are no more letters in the string, before moving onto the next line. And the the next, until all three lines have been run through this system letter by letter. At this stage the “A” button animates showing the player can press it to continue.
This approach has its issues, of course. For a start it’s scripted a little clunky and I’m sure it could be done in a more efficient way. However, following the old adage “if it works it works” it does actually work. One major issue is that I cannot use UE4’s automatic word wrapping. As such each set of dialog texts has to be handcrafted to fit on each of the three lines provided. Luckily I’ve created, with a minimum of fuss and effort, a little tool in the engine where I can write dialog texts and preview them real-time to make sure everything fits.
From the tool I can quickly write texts, make sure the lines remain within bounds, add effects or buttons. As you can see the bottom line in the image below is far too long to fit the dialog box, so a rewrite is required. The button style drop down allows me to select different platforms and see the relevant button graphic in real time. It is a little short of a full-fledged text editor but it’s been very useful. In a debug menu inside the game I can also call up a list of all the dialogs to check them for length, as a second backup, but since using this tool to create the dialogs in the first place I’ve had zero overruns.
A system like this also creates some issues for localisation. Previously I relied on UE4’s localisation dashboard but for dialogs like these, as well as in-game books (on which I’ll write some other day) it is likely some languages will need more space. Instead I’ve focused on datatables per language. There is a simple language check before the relevant data table gets called at which point I can use any of a set number of datatables. Are you playing in English? Please refer to this datatable. German? Use that one. Etc.
The way dialogs follow each other is by a simple ID referral inside the datatable. Once a dialog set of three lines has been used the dialog widget checks if it refers to another ID. If not, the dialog ends. If there is an ID it’ll continue on to that dialog. This way, if any language requires more text than the English (this will 100% happen, guaranteed!) the localiser can just add as many new unique IDs as they need.
For Piczle Cross Adventure, moreso than Piczle Colors and even Piczle Lines DX, I’ve made sure systems like these are as automated as possible. Once you have a “rock solid” (nothing is ever truly rock solid though) automated system you can feed it whatever data you need and it’ll work as expected. Adding dialogs to Piczle Cross Adventure is now so easy the only real hurdle is my ability to write fun enough dialogs to put in there.
(Yes, Piczle Cross Adventure has loot crates. Well, a loot crate.
Ugh, videogames, amirite?)