Site Logo


Sparen's Danmakufu ph3 Tutorials Lesson 17 - Introduction to Text Functions and Fonts

The video for this lesson is Pumpkin of the Afterlife by Lunarethic. Lots of different fonts and uses of text here.

Part 1: What will be Covered in this Lesson?

In this lesson, I will discuss Text Objects and Fonts, and how to use them in Danmakufu.

We will also have a discussion on when to use text objects rather than images of text, as well as how best to align and manage text displays for maximum player benefit.

Part 2: What are Fonts and How Do I Use them in Danmakufu?

Note: Arial is used as the backup for the following fonts. Depending on whether or not you have the font and what browser you are using, you may see all the fonts rendered properly or you may see a splattering of colored Arial (or in the worse case scenario, Times New Roman). At the end of the guide, links and information on the fonts used in this tutorial will be available.

So let's begin with this: what are fonts? You have undoubtedly heard about the ever-present Helvetica, the standard and boring Arial, and the remarkably stupid troll font Comic Sans. Please do not use Comic Sans in a serious script (or anything serious, for that matter). Other unacceptable fonts include Papyrus and Wingdings (Wingdings).

The default font used by Danmakufu (ph3, after [.1 pre2]) is MS Gothic. Other common fonts used in the real world include Verdana and Courier. In Computer Science, Andale Mono is commonly used. Windows users may see Lucida Console instead.

For most practical purposes, you will want to avoid Danmakufu's default of MS Gothic. It's not a bad font, but it has a number of quirks. However, Danmakufu grants you the flexibility to use nearly any font in existence, so that should not be a problem.

The InstallFont() function is able to install nearly any font that you might need. By default, Danmakufu will be able to use fonts on your Computer. However, when distributing scripts to others, it is important to include non-standard fonts with your script and use InstallFont on them, since you cannot tell people to install all of the fonts manually. Not everyone will have your font. This being said, InstallFont is neat because it returns true if the loading was a success. You use InstallFont as follows:

InstallFont(GetCurrentScriptDirectory() ~ "font/Revue.ttf");

You only need to install a font once per script run - once this is run, Revue can be used by any text object in your script. It is a good idea to put it in your System script (we will go over this in a later tutorial) or at the start of your package (if you use one). Put it somewhere where it will be run once at the very start of a script. Note that you give the path to the actual font file - not the name of the font.

For example: ObjText_SetFontType(objText, "Revue")

We will go over how to actually use the fonts in actual text in the next part.

Part 3: How Do I Create Text Objects in Danmakufu?

So, text in Danmakufu. First thing to note is that Text Objects are a subset of Render Objects. This means that all of the render functions (including ObjRender_SetPosition(), Obj_SetRenderPriority(), and ObjRender_SetAlpha()) apply to Text Objects. The one real exception is ObjRender_SetColor() since Text Objects have a different and more versatile method of using colors and borders.

Like with all of the standard objects, you need to create the object with ObjText_Create(). Then you use ObjText_SetText() to set the text to display in that object. Note that if you do not use ObjText_SetMaxWidth(), the text will potentially go off the screen and beyond - it does not automatically wrap by default.

If you want to set the font of the text to something other than MS Gothic, you must use ObjText_SetFontType(). Now, you do NOT reference the font by its filepath. You must refer to it by its name as a string. For example, if I have a TrueTypeFont (ttf) called Eurosti.ttf (for the Eurostile Typeface), I must use ObjText_SetFontType(objText, "Eurostile"), not ObjText_SetFontType(objText, "Eurosti"). This is perhaps the biggest pain in Danmakufu when using custom fonts. It's made so that you can type the name of a common font in and it will search your computer's font library, not for custom fonts. It may take a bit of time to figure out what the 'appropriate' name of your font is - it's not always the part of the name before the file extension.

Part 4: How Do I Set the Size and Color of Text Objects?

Earlier I discussed that there is no real use for ObjRender_SetColor() when using Text Objects because they have their own functions for that. We'll discuss that in this part.

Before that, though, there are two things I need to mention - boldface and changing the font size. You can use ObjText_SetFontBold() to make a text object display bold. You can use ObjText_SetFontSize() to change the font size. These things change the shape of the text object and may aid or impede clarity, depending on how they are used. At this point, it is important to note that a single text object can only have one size, one font, can be bold or not, and can only have one border, etc. This is the biggest weakness of Danmakufu's text functions (you also cannot use underline, overline, strike through, or italics in Danmakufu ph3 [.1 pre6a]). The settings you set for one text object apply for the entire object. We will discuss how to circumvent this at the end of this lesson.

Now, colors. The cool thing about Danmakufu is that it has built-in support for text color gradients - You can use two different colors for ObjText_SetFontColorTop() and ObjText_SetFontColorBottom() and there will be a gradient. If you want the same color, just paste the same color for both.

Then there are borders. ObjText_SetFontBorderType() will set the border that you want - BORDER_NONE, BORDER_FULL, or BORDER_SHADOW. You can change the width of this border with ObjText_SetFontBorderWidth(). BORDER_NONE means that there is no border - this is different from BORDER_FULL or BORDER_SHADOW with a width of 0 - there is still a border in these cases which may result in ugly text. Speaking of ugly text, BORDER_SHADOW always has a one-pixel border around the entire text object, not just on the bottom side. This makes it a relatively poor choice for most cases.

To set the color for a border, use ObjText_SetFontBorderColor().

Below is an example of a text object using borders.

    let objText = ObjText_Create();
    ObjText_SetText(objText, "--> Russell Square Regular <--");
    ObjText_SetFontSize(objText, 24);
    ObjText_SetFontType(objText, "Russell Square Regular");
    ObjText_SetFontBold(objText, true);
    ObjText_SetFontColorTop(objText, 255, 255, 255);
    ObjText_SetFontColorBottom(objText, 255, 255, 255);
    ObjText_SetFontBorderType(objText, BORDER_FULL);
    ObjText_SetFontBorderColor(objText, 224, 64, 192);
    ObjText_SetFontBorderWidth(objText, 1);

Assuming you have the font and your browser is capable, it should render similar to below:

--> Russell Square Regular <--

EXERCISE: Test out some text objects. See how borders work, and how color gradients can be used. What colors go well with others? Think about contrast in regards to the background and legibility.

Part 5: How Do I Align and Center Text Objects?

Aligning and centering text objects is where things become painful. ObjText_SetHorizontalAlignment() is the function used for this. There are three styles - left, center, and right. To align right and center, you must set ObjText_SetMaxWidth() as well. This is to ensure that Danmakufu knows where the borders are. If you want to left-align, you do not need to set a maximum width, although it is recommended that you do so anyways. Note that with ObjText_SetMaxWidth(), characters that do not fit will be shoved onto the next line. Additionally, glyphs in fonts may render with different widths and the like depending on your computer. Please be advised. You can use functions like ObjText_SetSidePitch() with negative values to squeeze text into a smaller space.

Let's begin by aligning to the left. All you need to do is ObjText_SetHorizontalAlignment(ALIGNMENT_LEFT), and all lines of the text object will be left-aligned. Since left-align is the default, you shouldn't even need to do this.

Now let's align to the right. Of course, you need to set the maximum width and other things, but what does the x position actually mean? Well, say you have a max width of 300 and want the right boundary to be at 192 pixels. In this case, you would use

    ObjText_SetHorizontalAlignment(objText, ALIGNMENT_RIGHT);
    ObjText_SetMaxWidth(objText, 300);
    ObjRender_SetX(objText, 192 - 300);

So why do we need 192 - 300? Well, the right bound that we want is 192, but the text object is 300 pixels wide, so we need to set the x position to the left corner of the bounding box. In other words, we will have a bounding box for the text object extending from 192 - 300 to 192, with width 300. In this bounding box, the text will all be aligned to the right. If we did not have this bounding box (if SetMaxWidth was never called), Danmakufu would not know where the bounding box was, and would not know where or how to render the text object.

Centering along a line uses a similar process. We will use ALIGNMENT_CENTER for this. In this case, the X position will be the line you want to center along - half the max width. For example:

    task LifeExtendGraphic{
        let objText = ObjText_Create();
        ObjText_SetText(objText, "EXTEND!");
        ObjText_SetFontSize(objText, 24);
        ObjText_SetFontType(objText, "MS Gothic");
        ObjText_SetFontBold(objText, false);
        ObjText_SetFontColorTop(objText, 255, 224, 255);
        ObjText_SetFontColorBottom(objText, 240, 208, 240);
        ObjText_SetFontBorderType(objText, BORDER_FULL);
        ObjText_SetFontBorderColor(objText, 192, 96, 192);
        ObjText_SetFontBorderWidth(objText, 2);
        ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);
        ObjText_SetMaxWidth(objText, 360);
        Obj_SetRenderPriority(objText, 0.6);
        ObjRender_SetX(objText, 192 - 180);
        //Minus half the max width. 192 = 384/2 (384 is GetStgFrameWidth w/ r.p.<80)
	ObjRender_SetY(objText, 75);

        loop(120){
		yield;
        }
        Obj_Delete(objText);
    }

There are plenty of other functions for manipulating text objects in the documentation. Check them out if you want to do some more interesting adjustments.

Part 6: How do I Use Danmakufu's Bracketed String Characters?

Danmakufu ph3 is somewhat stupid in that it gave up the standard \n for [r] in strings. But there are other similar things and escape characters in ph3.

According to the official documentation, there are two bracketed strings - [r] for new lines and [ruby] for ruby/furigana. For [r], you just place it in the string and it will automatically line break.

For ruby/furigana, the actual encoding requires the rb and rt parameters. For example, チルノは[ruby rb=\"天才\" rt=\"バカ\"]である will render as チルノは天才(バカ)である

There are also some other standard things, such as the &nbsp;, a non-breaking space, &quot; for a quotation mark " (since you cannot just put them in strings or the Danmakufu interpreter will assume you are trying to end the string, and &osb; and &csb; for the [ and ], respectively (since they are interpreted in syntactic analysis).

This being said, you can turn off Syntactic Analysis. Using ObjText_SetSyntacticAnalysis() will disable interpreting of things such as [r] in a text object.

Quiz: Creating and Customizing Text Objects

1) Reimu wants her text to be white with a red border of width two, and to use the font Gill Sans with a size of 24, bold. Write the necessary code to accomplish this task.

Hit 'Show' to show possible answers.

Suggested answer is below.

    let objText = ObjText_Create();
    ObjText_SetText(objText, "Reimu Hakurei");
    ObjText_SetFontSize(objText, 24);
    ObjText_SetFontType(objText, "Gill Sans");
    ObjText_SetFontBold(objText, true);
    ObjText_SetFontColorTop(objText, 255, 255, 255);
    ObjText_SetFontColorBottom(objText, 255, 255, 255);
    ObjText_SetFontBorderType(objText, BORDER_FULL);
    ObjText_SetFontBorderColor(objText, 255, 0, 0);
    ObjText_SetFontBorderWidth(objText, 2);

2) Seiran wants to triangulate the world, and has embarked on her grand journey. But first, she wants the word 'MOCHI' to be prominently displayed in the center of the Danmakufu playing field. Of course, it will be in wonderful Futura. Assuming that the render priority is between 0.2 and 0.8, write code to position the text.

Hit 'Show' to show possible answers.

Suggested answer is below.

    let objText = ObjText_Create();
    ObjText_SetText(objText, "MOCHI");
    ObjText_SetFontSize(objText, 24);
    ObjText_SetFontType(objText, "Futura");
    ObjText_SetFontBold(objText, true);
    ObjText_SetFontColorTop(objText, 255, 255, 255);
    ObjText_SetFontColorBottom(objText, 255, 255, 255);
    ObjText_SetFontBorderType(objText, BORDER_FULL);
    ObjText_SetFontBorderColor(objText, 255, 0, 0);
    ObjText_SetFontBorderWidth(objText, 2);
    ObjRender_SetY(objText, 128);
    ObjRender_SetX(objText, 194 - 160);
    ObjText_SetMaxWidth(objText, 320); //any number large enough to hold 5 characters of 24 pt font Futura is OK
    ObjText_SetHorizontalAlignment(objText, ALIGNMENT_CENTER);

Part 7: Text Objects or Images of Text?

Now that we've discussed Text Objects, it's about time we discussed their limitations. As we have discussed previously, a single text object is limited to a single color gradient, a single border style, and a single font and font size.

If you wanted a horizontal gradient of rainbow text or insane Alpha-blended text shadows, you're not going to be able to do that natively in Danmakufu. Also, languages like Thai tend to, more often than not, get stuck in weird font or Text Encoding issues and simply not render correctly (If you need foreign language support, Danmakufu ph3 seems to like UTF-16 Little Endian with Byte Order Mark (BOM)). In these cases (and in others as well), it is almost always preferable to use an image file containing text.

For example, look at the following (from Phantasmagoria of Imagine Breaker). Note: It may be blurry depending on your browser. Additionally, a gray background has been prepared to show the black alpha shadow.

Gore Shotsheet Image

The first thing you will notice is the pixel-perfect precision used in the image. Danmakufu does a horrible job with this level of precision in text objects - fonts will render differently on different computers with different widths between individual glyphs. Additionally, the color and border do not bleed here, and the border works as intended. In Danmakufu, large thick borders may look quite ugly, especially if you in fact WANT a gradient between the text color and border. These kinds of effects cannot be done using Danmakufu's text objects.

Then there's the shadow. In Danmakufu, BORDER_SHADOW, as we discussed previously, does not create only a shadow, and in general does not work that great. Using an image, you can customize shadows and effects, and are only limited by your ability to use graphical editing software.

For another example, let's examine Lunarethic's HUD difficulty text images in his RaNGE 15 contest entry.

Lunarethic Difficulty Image

Note: Permission for use granted.

Besides being able to control how the text is rendered (distance between characters, text border, etc), we can see here that additional effects using the same colors and border format were done in this entry. Ordinary Revue is obviously incapable of handling the extra graphical effects. And this is where the real line tends to be drawn - text objects are only capable of rendering what is in the font library. If you want to apply masks with other images and textures, you can do that through pixel shaders, but adding graphics into text and similar things can really only be done using images.

To close, I'll give a brief note about another way of rendering text - sprite lists. With the built-in Danmakufu defaults for the system number text (for score and the like), sprite lists are used. 2D Sprite lists are essentially lists of 2D sprites grouped together as one object. By doing this with an image file containing text glyphs, you are able to manually select how to render each individual character, which allows you to apply color to text and the like. However, this is an exceptionally expensive way to render text, since each character is essentially a 2D sprite. This is mainly used with custom fonts that aren't packaged as .ttf or .otf, etc, as well as with emulations of other systems, such as my Touhoumon Mystery Dungeon project. Regardless, they will be discussed in a much later tutorial when we go into primitives.

Quiz: Text Vs Image Vs Sprite List

1) Kanako is trying to make a stage opening image using the font DIN 1451. However, it's too standardized for her liking, and Suwako suggests pasting frogs everywhere on the text. Of course, they cannot change the font, so what is the best way to have both the frogs and text in harmony?

A. Text Object
B. Image containing text
C. Sprite List only
D. Text Object + Render Object(s)

Part 8: Where Can I Get these Fonts?

All fonts below will render in Wingdings (Wingdings) if you do not have them. If you don't have Wingdings, the Wingdings text left of the parenthesis (in gray) will render in Arial. Unless you don't have Arial. In which case you're on your own.

Helvetica - Wikipedia

Arial - Wikipedia

Comic Sans MS - Wikipedia

Papyrus - Wikipedia

MS Gothic (Note: MS Gothic is Danmakufu's default font)

Verdana - Wikipedia

Courier - Wikipedia

Andale Mono - Wikipedia

Lucida Console - Wikipedia

Revue (Note: Revue is used in Touhou (10+) for the HUD difficulty text)

Eurostile - Wikipedia

Russell Square Regular (Note: Russell Square is used in Touhou (10+) for HUD boss names)

Gill Sans - Wikipedia

Futura - Wikipedia

DIN 1451 - Wikipedia

Summary

  • Load fonts with InstallFont() before using them
  • Provide copies of fonts you use in your script
  • When aligning text right or center, max width is required
  • Danmakufu uses [r] instead of \n for new lines in strings
  • Use images containing text for stylized text and for things that cannot be done with Text Objects

Sources and External Resources

N/A