How Menus Work ‐ Part 2 - pret/pokeemerald GitHub Wiki

In the previous part of this guide, we covered the basic concepts that come into play when displaying the kinds of GBA graphics that Game Freak uses in their menus. Now, it's time to review the systems that Game Freak has built on top of those.

Backgrounds

We know that there are four background layers, each with its own tilemap. We also know that background layers can be moved around the screen. What other attributes can a background layer have? Here's a few:

  • Different layer sizes, up to 512x512px for the background mode Game Freak uses
  • Priority values, to control which layers go overtop of which other layers
  • The ability to have 256-color background layers, though such layers' tilemaps would consume more space in VRAM
  • An optional offset, which is applied when the tilemap specifies a tile ID from the tileset (the offset is added)
  • The location of the tilemap itself within VRAM
  • Whatever "mosaic" is supposed to be

All of this information would have to be communicated to the GBA via I/O registers. Ditto for which layers are even visible right now, and for their positions on-screen. Using I/O registers is a bit of a clunky way to handle all of this, however. What's more: I/O registers are write-only, so there's no way to see what values are already in them. (For example, you can't "move this background five pixels to the left of its current location," because you can't read its current location.) This is why Game Freak built a collection of structs and functions that they use to manage the background layers and their tilesets.

You'll generally start by defining an array of BgTemplate structs, which you'll use to configure the background layers you plan on using. Here's the list for the options menu, with some formatting added:

static const struct BgTemplate sOptionMenuBgTemplates[] =
{
    {
        .bg = 1,
        .charBaseIndex = 1,
        .mapBaseIndex  = 30,
        .screenSize    = 0,
        .paletteMode   = 0,
        .priority      = 0,
        .baseTile      = 0
    },
    {
        .bg = 0,
        .charBaseIndex = 1,
        .mapBaseIndex  = 31,
        .screenSize    = 0,
        .paletteMode   = 0,
        .priority      = 1,
        .baseTile      = 0
    }
};

Note that they're not actually defined in order. You can define the layers in any order; it's the bg field which indicates what layer a given definition applies to. For whatever reason, when they wrote this menu, Game Freak defined layer 0 after they defined layer 1.

Here are all of the possible fields:

bg
The background layer that this template is for. Must be in the range [0, 3].
charBaseIndex

A value in the range [0, 3]; this specifies which of the four background tilesets this background layer should use.

Actually, that's not entirely true. A background tileset can define 512 tile graphics, but a tile in the tilemap can specify tile IDs in the range [0, 1023], so when charBaseIndex is 2 or lower, it specifies the two tilesets that this background layer can use. Neat!

(When charBaseIndex is 3, some emulators will let you use tileset 3 and an imaginary "tileset 4," but "tileset 4" is actually sprite graphics data, and misusing it for backgrounds will produce corrupted graphics on real hardware.)

mapBaseIndex
This controls where in VRAM this layer's tilemap is located, in increments of 2KB (so, a value of 2 is an offset of 4KB).
screenSize
An enum defining the background size. When the GBA is set to use text mode for backgrounds, as it is for most of Pokemon's gameplay, then 0 means 256x256px, 1 means 512x256px (tall), 2 means 256x512px (wide), and 3 means 512x512px.
paletteMode
A value of 0 indicates a 16-color background layer, which is usually what we want. A value of 1 indicates a 256-color background layer.
priority
A value in the range [0, 3]; this controls the order in which background layers display. Lower values display on top of higher values.
baseTile

This is a convenience value used by Game Freak's background layer functions. When you use those functions like LoadBgTiles to load tiles into a layer's tileset(s), those functions need to be told where in the tileset to place the new tiles... and whatever value you pass, the layer's baseTile will be added to it.

It is easiest to leave this at 0, for reasons we'll get into when we talk about UI windows.

Making sure that things don't overwrite each other

There are some important considerations to be aware of.

Tilesets and tilemaps exist in the same address space, the same region of memory. There's 96KB of VRAM used to store tilesets and tilemaps for the backgrounds, as well as the tiles that make up sprites. There's nothing which says, "The tilesets have to go over here and the tilemaps have to go over there." You decide where they go when you write your BgTemplate, and if you define things incorrectly, you can have one bulldoze the other by mistake. In fact, if you use your emulator's tile viewer to look at the tileset used in scenes like choosing your starter, you'll likely see some garbled graphics that look like strange vertical stripes: the emulator is showing all of VRAM as if it were a tileset, and those stripes are tilemap data being misinterpreted as tileset data.

Here, let me show you. Here's mGBA's tileset viewer during the "choose your starter" scene. I've clicked on the first weird stripey tile in the tileset, and mGBA is showing its memory address: 0x06003000.

Screenshot of mGBA's tileset viewer. The first "stripey tile" in the set has been clicked on, and mGBA shows its memory address as 0x06003000.

And here's mGBA's map viewer during that same scene, showing the tilemap for background layer 3. The "map base," the beginning of the tilemap, is listed as memory address 0x06003000, the same memory address as the first wacky tile graphic in the tileset. Those weird tile graphics are the tilemap.

Screenshot of mGBA's tilemap viewer. Background layer 3 is being examined, and mGBA shows its map base address as 0x06003000, the same address as the "stripey tile" in the other image.

So when you're defining your backgrounds, you need to make sure that their tilemaps don't overwrite each other; and when designing your tilesets, you need to make sure that the actual graphics you want to display don't overwrite the tilemaps. Remember when I said that Game Freak didn't manage to completely hide the limits of GBA graphics, and that you'd need to know the basics of how they work in order to properly build menus? All this is what I was talking about. You have a limited memory budget for graphics data and you need to know how to spend it.

So in mGBA's viewer, every tile graphic in the tileset (or, to be more direct, every 32 bytes of VRAM) is listed with an ID in the range [0, 2047]. The tiles at ID 2048 and up are used for sprites, and can't safely be used for background graphics. If we use tile IDs as our unit of measurement, then...

  • For a 256x256px background layer, the tilemap costs 64 tile IDs (2KB VRAM), and it must be located at a multiple of 64. The mapBaseIndex value on BgTemplate is that multiple.
    • The mapBaseIndex cannot be higher than 31. 64 times 32 is 2048.
  • VRAM is broken into four tilesets each containing 512 tile IDs (16KB VRAM), with no gaps between them. The charBaseIndex specifies the first of those tilesets that a background layer will use.
    • When charBaseIndex is 2 or lower, the layer has access to two tilesets.
    • When charBaseIndex is 3, the layer can only use that single tileset; past that is sprite data.
  • You probably aren't going to be using 2048 unique tile graphics, so there'll be some unused tile IDs that you can sacrifice to store your tilemaps.
    • It's okay for a tilemap to overwrite unused parts of a tileset. Every part of VRAM that isn't sprite memory is "part of a tileset;" everywhere you're allowed to place a tilemap at is "part of a tileset." Just pick a part that the tilesets aren't actually using.
    • It's not okay for a tilemap to overlap parts of the tileset that you're actually using for tile graphics.
    • It's not okay for tilemaps to overlap or overwrite each other.
  • If you increase the background layer size, then you also increase the tilemap size. A double-size layer (256x512px or 512x256px) is 4KB, consuming 128 tile IDs. A quadruple-size layer (512x512px) is 8KB, consuming 256 tile IDs.

If you use 256x256px background layers, then avoiding overlap between your tilemaps is as easy as giving each background layer a different mapBaseIndex. Safely overlapping the tilemaps and the tilesets requires knowing how many tile graphics you need in your tileset, knowing where they're going to load (because you control that, too), and using the mapBaseIndex to place the tilemap so that it's out of the way of whatever amount of memory your tilesets' graphics will consume.

Looking back at the options menu's BgTemplates, we see that they define two different layers with different mapBaseIndex values, 30 and 31, so the tilemaps won't overlap each other. Layer 1 is set to display overtop of layer 0 on-screen via their priority values, but otherwise, there's not much that differs between the two.

Functions that manipulate backgrounds

Here are some commonly used functions for manipulating backgrounds that have been set up through Game Freak's systems.

void ShowBg(u8 bg) and void HideBg(u8 bg)
These show or hide the specified BG layer.
void InitBgsFromTemplates(u8 bgMode, const struct BgTemplate *templates, u8 numTemplates)
Uses a list of BgTemplates to set up the GBA's background layers. You'll want to pass 0 (text mode) for bgMode. The templates argument should be the name of your list variable; no ampersand needed. If your list variable is named sMyCoolBackgroundList, then numTemplates should be ARRAY_COUNT(sMyCoolBackgroundList).
s32 ChangeBgX(u8 bg, s32 value, u8 op) and ChangeBgY
These functions allow you to move the background around. The op should be BG_COORD_SET, BG_COORD_ADD, or BG_COORD_SUB. I/O registers are write-only, but these functions remember the values they've written in, so as long as you use them, you can read, add, or subtract the background's coordinates. Of course, even some of Game Freak's code ignores these functions and writes to the I/O registers directly, so when your menu opens, you should begin by using BG_COORD_SET to force each of your background layers to position (0, 0). The options menu does that for all four layers even though it only uses two of them.
u16 LoadBgTiles(u8 bg, const void *src, u16 size, u16 destOffset)
This function loads tile graphics from src, a pointer to ROM or RAM[1], into VRAM, given a background layer and a destination tile ID (destOffset) to load to. The size is the size of the tile data in bytes; remember that a single tile graphic is 0x20 (32) bytes, so this should be 0x20 times however many tiles you plan to load.
void FillBgTilemapBufferRect(u8 bg, u16 tileNum, u8 x, u8 y, u8 width, u8 height, u8 palette)

This function takes a background layer, a tile ID, and a color palette ID, and fills a rectangular area of the tilemap with that tile and palette. For typical tile-based graphics, such as the borders shown on dialog boxes, this function is what you'll use after loading the tiles into VRAM, to actually place those tiles on the screen.

Note that this function doesn't write directly into VRAM. Rather, Emerald's engine maintains copies of the tilemaps in RAM, and this function modifies those copies. When you're done editing the tilemap, you can push the whole thing into VRAM all at once by calling CopyBgTilemapBufferToVram(bg).

Windows

Not to be confused with the GBA's hardware-level screen windows, Game Freak has a "window" system that allows you to draw arbitrary graphics, most notably text, on the background layer, programmatically. In other programming environments, such as HTML5 and JavaScript, these "windows" would be called "canvases."

Think about text in the Pokemon GBA games versus text in the classic Game Boy and Game Boy Color games. The old games used a monospaced font where each symbol fit exactly inside of an 8x8px tile, so they could just load the character set into the VRAM tileset and then reference characters within the tilemap. However, the GBA games were the first to use a proportional font, where the width of each symbol varies, and they didn't burn an entire tile on each character: a lowercase I and a lowercase L would appear flush against each other, sharing on-screen tiles if necessary. This of course means that wherever text is being drawn, every tilemap tile needs its own, unique, tileset graphic: if you take a screenshot of some text from Pokemon Emerald and cut out a random 8x8px tile, odds are you're going to end up with a unique fragment of sliced letters that isn't repeated anywhere else on-screen. If you're displaying text in a 5x2-tile region, you'll need 5 times 2 = 10 tiles in the tileset to draw that text onto.

That is the problem that Game Freak's window system is meant to solve. A window defines a rectangular area in a background layer where Game Freak plans on drawing text or other arbitrary graphics, and the window system will reserve as many tiles in the VRAM tileset as that rectangular area needs. The word "window" may make you think of the borders that display around dialog boxes, but those are unrelated; they're not part of the window system, and instead get drawn separately. If you think of a literal window, we're talking about just the glass, not the frame.

Here are the window definitions from the options menu, formatted for legibility:

enum
{
    WIN_HEADER,
    WIN_OPTIONS
};

// -snip-

static const struct WindowTemplate sOptionMenuWinTemplates[] =
{
    [WIN_HEADER] = {
        .bg = 1,
        .tilemapLeft = 2,
        .tilemapTop  = 1,
        .width       = 26,
        .height      = 2,
        .paletteNum  = 1,
        .baseBlock   = 2
    },
    [WIN_OPTIONS] = {
        .bg = 0,
        .tilemapLeft = 2,
        .tilemapTop  = 5,
        .width       = 26,
        .height      = 14,
        .paletteNum  = 1,
        .baseBlock   = 0x36
    },
    DUMMY_WIN_TEMPLATE
};

Throughout the menu's code, windows are identified by the ID — their index in the list of templates — so the code uses an enum in order to give meaningful names to those IDs. The DUMMY_WIN_TEMPLATE constant marks the end of the list.

Here are the fields on a WindowTemplate:

bg
The background layer that this window will be drawn on.
tilemapLeft
The X-coordinate of the window's left edge within the background layer's tilemap — so, it's measured in tiles, not pixels.
tilemapTop
The Y-coordinate of the window's top edge within the background layer's tilemap.
width
The width of the window, in tiles.
height
The height of the window, in tiles.
paletteNum
Which of the 16 available background color palettes to use. Each individual tile in a background layer's tilemap can be set to use any of these 16 palettes; for simplicity's sake, Game Freak's code will force all of the window's tiles to use the palette we specify.
baseBlock
This is a very important setting: we have to choose where in the tileset we'll place this window's contents. This value, plus the background layer's baseTile, is the ID of the first tile in the tileset that will be overwritten and used for the window's contents. If you don't set baseBlock values correctly, then windows may overwrite each others' tiles, or they may overwrite a background layer's tilemap.

There's a limit on how many windows can exist at once. It's a pretty big limit — NUM_WINDOWS, or 32 in the vanilla game — but it's worth remembering that everywhere you want to print text requires a window.

Making sure that things don't overwrite each other, because we're not done with that yet

Remember when we went over background templates and how you need to decide where things are arranged in VRAM, so that nothing overwrites each other? I said that you need to decide where in the tileset you load your graphics to, so that the tilemap doesn't conflict with portions of the tileset that you're actively using. Well, windows count as "loading graphics:" you're using portions of the tileset for your window, so you need to be mindful of how many tiles your window will consume, and make sure that your tilemap isn't in the way.

  • A background layer's tilemap always takes up as much space as 64 tileset graphics. Multiply the layer's mapBaseIndex by 64 to get the first tile ID that the tilemap overlaps with.
  • A window will use width * height many tiles in the tileset. The specific tile IDs that the window uses are determined by its baseBlock and the background layer's baseTile (add them to find the first ID), and the window's width and height (multiply them to find the number of IDs used).
    • When we went over BgTemplates, I mentioned that it's easiest to leave the background layer's baseTile at zero. This is why. If you leave it at zero, then you just have to worry about the windows' baseBlock.
  • Menus will typically clear VRAM when they're opened, so the only tiles in the tileset that are used are the tiles you devote to your windows, and any extra graphics you decide to load. Everything goes where you put it, and you can place things wherever makes the most sense to you.

You'll want to keep your BgTemplate list near your WindowTemplate list, so you can see them both at once and edit them in tandem.

Or, you can just YOLO it, pick values that seem like they'll work well enough, and then double-check things in your emulator's tileset and tilemap viewers to make sure there's no overlap. If things display oddly and it looks like the tileset graphics are oddly close to one another, then you can make trial-and-error fixes to baseBlock or one of the other knobs and dials you need to turn.

One other thing to note is that backgrounds are filled with tile ID 0 by default, and by convention that's meant to be a fully transparent tile. This means that if you use a baseBlock value of 0, you'll actually overwrite that transparent tile and flood-fill the entire background layer, even if your window doesn't cover the whole layer. As such, it's best to have baseBlock start at 1, and we can see that for the options window, Game Freak actually started from 2.

The options menu as an example

The WIN_HEADER window, used to display the menu title, has a baseBlock of 2, a width of 26 tiles, and a height of 2 tiles; this means that it skips the first two tiles in the tileset, and consumes the next 52 tiles, ending just before tile ID 54. The next window is WIN_OPTIONS, which uses a baseBlock of 54 (written in hex as 0x36); we can see that it avoids overlapping with the portions of the tileset that are consumed by WIN_HEADER. WIN_OPTIONS has dimensions 26 by 14, and so uses 364 tiles, ending just before tile ID 418. If we think back to the BgTemplates for the options menu, those used mapBaseIndex values 30 and 31; multiply those by 64, and we see that they write to tile IDs beginning at 1920, well out of the way of what WIN_HEADER and WIN_OPTIONS are using.

The preprocessor can help

You don't have to do all the math yourself. You can define preprocessor constants for the window dimensions and similar, and then let the compiler do the math for you.

#define WIN_HEADER_WIDTH   26
#define WIN_HEADER_HEIGHT   2
#define WIN_OPTIONS_WIDTH  26
#define WIN_OPTIONS_HEIGHT 14

#define WIN_HEADER_TILE_IDS_USED  (WIN_HEADER_WIDTH  * WIN_HEADER_HEIGHT)
#define WIN_OPTIONS_TILE_IDS_USED (WIN_OPTIONS_WIDTH * WIN_OPTIONS_HEIGHT)

#define WIN_HEADER_BASE_BLOCK  2
#define WIN_OPTIONS_BASE_BLOCK (WIN_HEADER_BASE_BLOCK  + WIN_HEADER_TILE_IDS_USED)
#define END_OF_WINDOW_BLOCKS   (WIN_OPTIONS_BASE_BLOCK + WIN_OPTIONS_TILE_IDS_USED) // ID of first tileset graphic after window tileset graphics

#if END_OF_WINDOW_BLOCKS > 2047
   #error Too many tiles used for windows; total tile count exceeds VRAM budget.
#endif

// divide by 64 and round up
#define FIRST_SAFE_TILEMAP_BASE_INDEX  (END_OF_WINDOW_BLOCKS / 64) + ((END_OF_WINDOW_BLOCKS % 64) ? 1 : 0)

#if FIRST_SAFE_TILEMAP_BASE_INDEX > 31
   #error Too many tiles used; the tilesets themselves may fit in VRAM but there's no safe place to put the tilemap.
#endif

This particular approach would need some adjustments if you load additional tile graphics, e.g. for window frames. This is left as an exercise to the reader.

Functions that manipulate windows

Here are some commonly used functions for manipulating these windows.

bool16 InitWindows(const struct WindowTemplate *templates)
This function deletes all existing windows, and then takes a list of new window definitions (its argument) and initializes them. It relies on DUMMY_WIN_TEMPLATE being the last list element; forget to include that, and the function won't know where the list ends!
void PutWindowTilemap(u8 windowId)
The InitWindows function only sets up the window itself. To set up its tilemap data, you'll need to call this function. This stages the tilemap data within RAM; a later call to CopyWindowToVram (see below) will be needed to forward those changes from RAM to VRAM.
void FillWindowPixelBuffer(u8 windowId, u8 fillValue);
This function fills the window's entire area with a solid color. The color should be specified as PIXEL_FILL(n) where n should be replaced with a color index within the window's 16-color palette.
void BlitBitmapToWindow(u8 windowId, const u8 *pixels, u16 x, u16 y, u16 width, u16 height)
Copy the 4-bits-per-pixel image located at pixels to the specified window, displaying it at the given coordinates (in pixels) and cropping(?) it to the given size (in pixels).
void CopyWindowToVram(u8 windowId, u8 mode);

When you draw to a window using text printers (we'll get to those) or functions like FillWindowPixelRect, you're actually drawing to a copy of the window's tiles maintained in RAM. This allows you to fully draw a window's contents and then push them to VRAM via DMA when you're done, so that the user doesn't potentially see individual parts of the window while you're drawing them. The mode parameter should be one of the following:

COPYWIN_NONE
Does nothing.
COPYWIN_MAP
Only updates the window's tilemap (by calling CopyBgTilemapBufferToVram, described above, for you). The tile graphics themselves are not updated.
COPYWIN_GFX
Only updates the window's tile graphics, not its tilemap.
COPYWIN_FULL
Updates the window's tileset and tilemap.

When a window is first created, you'll need to do a "map" or "full" update, so that the background layer's tilemap is properly updated. After that, though, you're generally not going to be re-arranging a window's tiles; you'll just be painting over the tile IDs that it's already using, so a "graphics" update should be sufficient.

Text printers

Displaying text is one of the main use cases for windows, so how do we actually do it? The more accurate question would be, what do we do it with?

Think about the different ways text is displayed in Pokemon Emerald. Sometimes, it appears instantly, as on most of the UI. However, text can also be printed one character at a time, with the game waiting until all of the text is fully printed before performing some action (or letting the player perform some action, like advancing to the next line of character dialogue). Game Freak built a system to manage this: text printers.

Here's a typical text-printing call, taken from the options menu:

AddTextPrinterParameterized(WIN_HEADER, FONT_NORMAL, gText_Option, 8, 1, TEXT_SKIP_DRAW, NULL);

You can see that we specify a window ID, a font, the text to print, and the X and Y coordinates (in pixels, relative to the window's top-left corner) to print it at. The last two parameters, however, are particularly interesting: one is the speed at which we should print the text, and the next is a "callback" — a function pointer to call when the text is finished printing. We've specified TEXT_SKIP_DRAW as our speed, which means that the text actually prints instantly; and since we know the text will print instantly, and not at some point in the future, we don't need a callback, so we just pass NULL for that.

Bear in mind that this modifies the window's tileset in RAM. Your text won't show up on-screen unless and until you call CopyWindowToVram as discussed above.

...

...

Hey, wanna see something cursed, and learn how to control text colors at the same time? This is from the options menu:

static void DrawOptionMenuChoice(const u8 *text, u8 x, u8 y, u8 style)
{
    u8 dst[16];
    u16 i;

    for (i = 0; *text != EOS && i < ARRAY_COUNT(dst) - 1; i++)
        dst[i] = *(text++);

    if (style != 0)
    {
        dst[2] = TEXT_COLOR_RED;
        dst[5] = TEXT_COLOR_LIGHT_RED;
    }

    dst[i] = EOS;
    AddTextPrinterParameterized(WIN_OPTIONS, FONT_NORMAL, dst, x, y + 1, TEXT_SKIP_DRAW, NULL);
}

In the options menu, the name of an option is displayed on the left, and all of its possible values are displayed on the right, with the currently active value shown in red. The way Game Freak handles that is by calling DrawOptionMenuChoice for each value, with the style argument indicating whether we're drawing an active (1) or inactive (0) value. Game Freak's approach to this was to embed formatting codes into the start of every value name, to change the color; in DrawOptionMenuChoice, they copy the value name into a buffer so that they can do some byte surgery on those formatting codes to change the colors they use. In other words, dst is a non-const copy of strings like this:

const u8 gText_TextSpeedFast[] = _("{COLOR GREEN}{SHADOW LIGHT_GREEN}FAST");

There's a better way to control the text color from code. In menu.h, there's this function:

void AddTextPrinterParameterized3(u8 windowId, u8 fontId, u8 left, u8 top, const u8 *color, s8 speed, const u8 *str);

The color argument should be an array of three bytes defining the background, foreground, and shadow colors for the text. Each byte should be the index of a color in the window's 16-color palette. Remember: text is drawn right onto the tiles in the window's background tileset, so it uses the same palette as the window itself.

So if we wanted to, we could rewrite DrawOptionMenuChoice like this:

static void DrawOptionMenuChoice(const u8 *text, u8 x, u8 y, u8 style)
{
    u8 colors[3]; // background, foreground, shadow, as palette indices
    colors[0] = 1;
    colors[1] = 6;
    colors[2] = 7;
    if (style != 0)
    {
        colors[1] = TEXT_COLOR_RED;
        colors[2] = TEXT_COLOR_LIGHT_RED;
    }

    AddTextPrinterParameterized3(WIN_OPTIONS, FONT_NORMAL, x, y + 1, colors, TEXT_SKIP_DRAW, text);

    //
    // NOTE: This wouldn't actually work in the options menu, unless you also edit the value names 
    //       in `strings.c` to remove the formatting codes that Game Freak was doing surgery on in 
    //       their code. Those formatting codes would override the colors we're setting here.
    //
}

An important note on animated text

We've reviewed text that prints instantly, but what if you want text that prints one character at a time, like dialogue shown on the overworld? In that case, for the text speed argument in functions like AddTextPrinterParameterized, you'd pass the number of frames to wait between each character... but there's something important that we need to understand about text printers, when we do that.

When a text printer is displaying text over time rather than instantly, the printer itself needs to be stored over time, because the printer keeps track of the state of text printing — how much text we've printed, what colors we're printing with, where within the window the next character will be printed to, and so on. There's a limit on how many text printers can exist simultaneously, and that limit is NUM_WINDOWS — 32 in the vanilla game. Instant text printers, using TEXT_SKIP_DRAW, don't contribute to this limit. That's because they don't need to be stored over time: when the text-printing code sees that you're asking for instant text, it'll just create a text printer, run it in full, and then discard the printer immediately.

Functions related to text

Here are some of the useful functions for printing text. At minimum, you'll need to include text.h.

s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing)
Returns the width of a piece of text, in pixels. For letterSpacing, you can usually pass 0. If you want to do things like centering or right-aligning text within a column, you'll need to know its width and do some math.
u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16))
A basic function for printing text. For non-instant text, this returns the ID of the text printer being used.
u16 AddTextPrinterParameterized2(u8 windowId, u8 fontId, const u8 *str, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16), u8 fgColor, u8 bgColor, u8 shadowColor) from menu.h
A basic function for printing text, with color options passed individually. For non-instant text, this returns the ID of the text printer being used.
void AddTextPrinterParameterized3(u8 windowId, u8 fontId, u8 left, u8 top, const u8 *color, s8 speed, const u8 *str) from menu.h
A basic function for printing text, with color options passed as an array, and no callback argument.
void AddTextPrinterParameterized4(u8 windowId, u8 fontId, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, const u8 *color, s8 speed, const u8 *str) from menu.h
A basic function for printing text, with color options passed as an array, letter and line spacing options, and no callback argument.
void AddTextPrinterParameterized5(u8 windowId, u8 fontId, const u8 *str, u8 left, u8 top, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16), u8 letterSpacing, u8 lineSpacing) from menu.h
A basic function for printing text, with letter and line spacing options.
bool16 AddTextPrinter(struct TextPrinterTemplate *template, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16))
A text-printing function where instead of passing a bunch of arguments, you pass a pointer to a TextPrinterTemplate struct that you have manually configured. If one of the overloads above doesn't have the particular combination of options you want, then this'll get you sorted.
u8 GetPlayerTextSpeedDelay(void) from menu.h
Retrieves the player's text speed in a form you can pass to one of the text-printing functions.

Next steps

We've looked at menu graphics as the GBA hardware sees them, and we've looked at some of the supporting tech that Game Freak has built around that. In the next article, we can begin looking at a simple menu: what code would we actually run in order to manipulate this stuff? What do we do with all these concepts?


Footnotes

[1] This is sort of an advanced topic, but... if you're feeling clever, you may try generating tile graphics via program code, storing them in RAM and copying them to VRAM. If you do, you'll probably store the tile graphics as local variables on the stack, call LoadBgTiles,... and then see broken graphics. LoadBgTiles queues a DMA transfer, but Emerald's engine doesn't perform those transfers immediately. Rather, it runs them in batches during what's called the "v-blank interval," a pause between drawing one frame of animation and drawing the next. If you want to construct tile graphics using program code, you'll need to store them somewhere persistent, such as a heap-allocated buffer.

⚠️ **GitHub.com Fallback** ⚠️