A Look at Word-wrapping your Messages

In this article we will be looking at word-wrap and what an amazing feature it is. We will discuss word-wrap in RPG Maker and how it can be used, as well as some complications that arise because RPG Maker does not natively come with word-wrap.

What is Word Wrap?

Wikipedia has a pretty explanation on word wrap.

Word-wrap (and line-wrap) are features that determine when a line of text has reached the end of a line that they are displayed on and automatically starts a new line. The purpose of this is to avoid forcing the user to scroll horizontally to view the rest of the line (as in internet browsers), or to avoid having text cut off.

The difference between word-wrap and line-wrap is where the line break occurs. For line wrap, it will just print characters out, and once it hits the end of the line, it’ll print the next character on a new line. This may be undesirable because you might be in the middle of a word and now it doesn’t make sense. Some solutions to this include inserting a hyphen to indicate that the word continues on the next line. Sometimes it might even end up on a new page.

Word-wrap, on the other hand, will determine that it can’t print the whole word on the same line, and will start the word on the next line. This may be more desirable depending on whether you care whether words are broken apart or not.

Why It Matters

To appreciate these features, think about what happens when you don’t have them. For example, RPG Maker doesn’t have any word-wrap or line-wrap and so you end up with things like this:

wordWrappingMessage1

Not very pretty. Or even correct: we’re missing two-and-a-half words, because the line was too long to fit on one line.

DIY

A solution? Manually insert line breaks!

RPG Maker attempts to help you by providing some visual guidelines. You can see that there are arrows in the show text message editor that indicate when the message will likely be cut off. The first arrow is used if you are showing a face graphic, while the second arrow is used if you don’t have a face graphic.

However, these guidelines assume a few things

  • You’re using the default font, and
  • You’re using the default window size, and
  • You’re using “plain text”

If you’re using larger or smaller fonts, those arrows aren’t very accurate (in fact it doesn’t seem very accurate for the default font either). If you want a larger or smaller window, those arrows won’t help either.

The more pressing issue is the assumption that you’re using “plain text”.

RPG Maker’s message window doesn’t just display text; it comes with a complete set of message codes that do a variety of things from controlling the speed of the text to showing the gold window to replacing certain codes with other values such as a specific actor’s name or a variable’s value.  These are called message codes, and it is a very large topic so I will not be covering that in detail here.

Basically, message codes that you write in the editor are not displayed the same way in game. Codes that control the message may or may not take up any space, while codes that convert codes into other text will likely take up more space. Of course, the editor doesn’t know this. I call them “Control Codes” and “Convert Codes”based on what their function is. You can see a list of default message codes when you hover your cursor over the text window:

wordWrappingMessage4

Here’s an example of me following the guidelines and displaying a few variables using convert codes in my message:

wordWrappingMessage2

While it looks fine in the editor, I can’t say the same about the results.

Starting With Line-Wrap

If all we had to deal with was regular text, manually breaking your lines might have been a feasible solution, but in general it would be better to have automated wrapping. Because all text is displayed one character at a time, we can start off with an implementation of line-wrap to convince ourselves that it works.

We can follow a simple rule: if there’s space on the line, draw it on the same line. If there’s no more space, continue on a new line.

The default window already provides a number of methods for handling line breaks. In particular, in Window_Base there is a method called process_new_line that basically starts a new line

def process_new_line(text, pos)
  pos[:x] = pos[:new_x]
  pos[:y] += pos[:height]
  pos[:height] = calc_line_height(text)
end

Let’s start with the “plain text” scenario. RPG Maker calls it a “normal character”:

def process_normal_character(c, pos)
  text_width = text_size(c).width
  draw_text(pos[:x], pos[:y], text_width * 2, pos[:height], c)
  pos[:x] += text_width
end

The logic is pretty simple: draw the character, increment the x-pos by the width of the character. Rather than simply drawing text, we want to check whether the text can be drawn on the same line first.

class Window_Base
  def process_normal_character(c, pos)  
    text_width = text_size(c).width    
    #---------------------------------------------
    if pos[:x] + text_width >= self.contents.width
      pos[:new_x] = new_line_x
      process_new_line(c, pos)
    end    
    #---------------------------------------------
    draw_text(pos[:x], pos[:y], text_width * 2, pos[:height], c)
    pos[:x] += text_width
  end
end

Does it work?

wordWrappingMessage3

It definitely took an arbitrarily long line of text and broke it into different parts. Some things to note

  • On the second line, the first character is a space. This is because the space couldn’t be drawn on the previous line.
  • At the end of the second line, you can see that “verify” was broken in the middle of the word

Which is expected, since our simple logic doesn’t care what kind of character we’re looking at. You can sort of fix the second problem but printing out a hyphen before processing the line break, but that’s only if hyphens work for you.

Line-Wrap to Word-Wrap

Personally, I don’t like using hyphens to work with broken-up words. Instead, I’d prefer to just have the entire word moved onto a new line. We can’t insert our code into the same method, because that is specifically for processing an individual character, and even if we wanted to, we don’t have a reference to the text we are printing out.

We move up to the process_character method.

class Window_Base
  def process_character(c, text, pos)
    case c
    when "\n"   # New line
      process_new_line(text, pos)
    when "\f"   # New page
      process_new_page(text, pos)
    when "\e"   # Control character
      process_escape_character(obtain_escape_code(text), text, pos)
    else        # Normal character
      process_normal_character(c, pos)
    end
  end
end

Here, we actually have a reference to the remaining text to be parsed. We can perform our word-wrap check by first grabbing all of the characters that are part of the current word and perform our checks on the spot, modifying the position as needed before we actually process the character. We’ll have to ignore word-wrap if we’re working with a message code otherwise strange things may happen (especially since we can add our own message codes).

What is a Word?

For some, this may seem to be an obvious question. However, it really isn’t. Especially if you start considering other languages. There are also cases where a word should not be broken up, for example someone’s name.

But for now let’s consider the very simple (but inaccurate) definition of a word, which is a continuous string of characters without any spaces.

class Window_Message
  alias :th_message_word_wrap_process_character :process_character
  def process_character(c, text, pos)
    if pos[:x] + next_word_width(c, text) >= self.contents.width
      pos[:new_x] = new_line_x
      process_new_line(c, pos)
    end    
    th_message_word_wrap_process_character(c, text, pos)
  end

  def next_word_width(c, text)
    word_width = text_size(c).width
    return word_width if text.empty?
    return word_width + text_size(text[0, text.index(/\s/)]).width
  end
end

Checking the output with our new wrapping script:

wordWrappingMessage5

Looks better: words aren’t broken apart. Still has those leading spaces though. You probably should be checking whether you’re working with a message code or not though.

Page Breaks

A new problem appears as a result of inserting new line breaks: what happens when your line is too long and doesn’t fit in the window? Fortunately, the message window already handles this for you: during the line break processing, it also determines whether you need a new page. If you do need a new page, it will pause and wait before continuing.

class Window_Message
  def process_new_line(text, pos)
    @line_show_fast = false
    super
    if need_new_page?(text, pos)
      input_pause
      new_page(text, pos)
    end
  end
end

So this solves another problem that we would have had to deal with ourselves.

Wrapping Message Codes

You may be wondering whether our script supports  message codes. Recall the two types of message codes

  • Convert Codes – the engine replaces this code with the appropriate value
  • Control Codes – this controls how the engine behaves when it sees a certain code

Convert codes are replaced with the appropriate value when the message is displayed, and these are always text values. This is real-time data (at the time of printing), which is an amazing feature that you simply cannot achieve in regular printed literature. Convert codes are replaced in a pre-processing step that occurs before we actually begin printing our text, so by the time word-wrapping becomes relevant, we don’t need to worry about these.

Control codes are somewhat more complicated, because they could do anything from simply altering certain behavior of the message drawer, or to draw images. You’d have to consider each one case-by-case.

Some Considerations

There are a large number of different issues that you can run into when implementing word wrapping. Here are a few.

Explicit Line Breaks

What happens to the existing line breaks in your message when you perform word-wrap. When you press enter in the text box, it automatically creates a line break and will be displayed by the window. One way to address this is to get rid of the original line breaks and let the engine figure out where they should be inserted, but this solution doesn’t work in all cases. Different word wrapping scripts provide their own implementations, such as removing all existing line breaks, but providing a custom line break character that you can use to tell the engine that you actually want a line break.

Explicit Whitespace

Sometimes you just want to put in a bunch of spaces for some reason. A decision would need to be made here on how spaces would be handled, especially when it comes to line-breaks which may result in leading whitespace.

Working with Non-Text Elements

The message window doesn’t only display text. It can display other images as well such as icons or sprites. The existing implementation of word-wrap assumed that you’re working with strictly text, which is not always true.

Because there is no standard way of drawing non-text elements, you would need to determine how the image is being drawn, and then use the image’s width in your calculations.

Performance Impact

Word-wrapping is not free. In fact, it is rather computationally expensive due to two things

  1. It calls Bitmap#text_size, which is slow
  2. It calls Bitmap#draw_text, which is slow

So if you’re trying to draw a paragraph with some 200 words, you will see a noticeable delay.

Unless you are using scripts that improve the performance of these two methods, I would suggest only using word-wrap for situations where you don’t need to draw too much text, such as the show text message events.

Word Wrap Scripts

Here are some implementations of word-wrapping for RPGMaker VXAce that provide various ways to handle all of the different issues that arise from automatic word wrapping.

Summary

This is a simple implementation of word wrapping for your message boxes. It only applies to the message window, and no other windows, because the message window is the only window that defines new_line_x. However, if you moved that method up into Window_Base, then you can implement word-wrapping for all windows.

Spread the Word

If you liked the post and feel that others could benefit from it, consider tweeting it or sharing it on whichever social media channels that you use. You can also follow @HimeWorks on Twitter or like my Facebook page to get the latest updates or suggest topics that you would like to read about.

You may also like...

33 Responses

  1. Arsist says:

    Definitely bookmarking this one. Because I don’t use the default font, and, well… sigh

Leave a Reply to Arsist Cancel reply

Your email address will not be published. Required fields are marked *