Determining Text Box Width and Height
In my game Dave-Man all the characters speak in little text bubbles. Some short, some long, some just one word. The bubbles looked awkward with extra space so needed the exact height and width so words don't get cut off.
I created a function that is passed text and the maximum line width (in characters) and it returns the number of lines and longest line's width (in characters). A little while back I tweeted an image of it:
It seemed well received so I thought I'd make a little "Tutorial" post walking through the steps of the function. The game engine I'm using is Construct 2 but this is a simple function doing some math and returning values so should work in any engine or language.
I wasn't sure it was worth tackling at first and just simply just took the number of characters divided by the max characters in a line. Round that up and it is the number of lines. But that doesn't work due to early line wrapping, which is especially problematic with larger words. You also can't always use the max width as if all the lines wrap early the width required is smaller.
Thinking the solution through in my head seemed a bit tricky to get perfect so I procrastinated but eventually important words getting cut off got me to take action. I found it really isn't that complicated, as others that have tackled similar would probably agree, so hopefully after I break it down you will agree and implement something similar if needed.
So without further ado, the function:
First off the function is passed a string of text and a value of the max characters you would like in a line.
If you are following along with the image you can see the local variables I created. I prefer to use more than are usually necessary just for readability of the math throughout the function. For example, function.param(0) in Construct is the first parameter passed to the function. So there is really no need to set that to a new variable as I could just use function.param(0) throughout. I believe the readability though is beneficial and can help cut down on errors in revision.
The function then loops through every word in the text, which I use the number of spaces to do. I will grab the length of the current word using the tokenat and the loopindex. Len() in Construct simply returns the length of a string.
The variable currentCharCount is the variable I used for the current number of characters in the current line. So if the currentCharCount plus the length of the current word, plus 1 for the space, is longer than the max width of a line this mean the word will be pushed to the next line. Therefore the variable "lines" is incremented and the currentCharCount is set to the current word's length as it will be the only thing in the new line. But before the currentCharCount is reset we need to check if that was the new longest line using a ternary operator—if currentCharCount is longer than longestLine variable then longestLine is set to currentCharCount, if not longestLine is set to itself.
If the line does not wrap then currentCharCount is incremented by the current word's length plus one for the space.
Since I only set the longestLine variable when a word wraps it never gets set if the text is only one line. Therefore I use another ternary operator to check if longestLine is zero (the initial value) and if so set it to the text's length.
Then I return a string with a comma separating the two values. In the code calling this function I use the tokenat to parse the string, using the comma as the token.
Note in the initial variables:
Line is set to 1 as any text will be at least 1 line.
currentCharCount is set to -1 as I always add the word's length plus 1 but the first word doesn't require a space. For example if the only text is "Dave" the text's length is 4 and if "Hi Dave" it is 7. If currentCharCount is set to 0 initially the strings would return 5 and 8 respectively, but this would also mess up the line wrap predictions for the first line.
Let me recap the steps in bullets as the steps may be a bit different in different languages:
- Loop through every word in the text
- Check if the word will cause wrap
- If so then check if this is the longest line thus far, increment number of lines, and set the count to the current word's length
- If not then add the word's length plus 1 for the space to the current count.
- If only one line set longest line to current character count...or the length of the original passed text
- Return the number lines (height) and longest line (width)