Home

Game Boy Color: What's in a Frame?

Rendering a frame of graphics on the Game Boy Color isn't quite as simple as deciding which pixels should be which colours. So how do the pictures get on the screen? This is a breakdown of how tiles, tile maps, palettes, backgrounds, objects, and raster effects all come together to create the visuals of Invasion of the Geminorons [itch.io].

Contents

Three elements: background, objects, window

Graphics on the Game Boy Color are essentially made from three layers: the background layer, the objects layer, and the window layer. Each layer comes with its own features, advantages, and drawbacks, and one of the keys to designing Game Boy Color games is getting all three to work together harmoniously.

Put simply,

Figure 1 shows an 18-second sequence from Invasion of the Geminorons, with an accompanying view of what's happening on each layer.

Background
Objects
Window
Final frame
Frame: 
0

Figure 1: The background, objects, and window combine to make the final image on screen.

Drag the slider to step frame-by-frame through the animation.

Background

Straight away, we can see that most of what appears on screen comes from the background layer. This is true for most games. The background is simply an image made from a grid of eight pixel by eight pixel tiles. By tiles we mean small reusable images that we place side-by-side to create a larger image.

The Game Boy Color (1998) is backwards-compatible with the original four-shades-of-green Game Boy (1989), and the consoles store tile graphics in an identical format. That means each tile image stores four "levels" of colour. We can think about drawing the background in two stages. First, we make a monochrome image, exactly like the original Game Boy. Then we use the extra features of the Game Boy Color to map the internal four colour levels to colour palettes of our own choosing.

The process is illustrated in Figure 2. First we define a tile set and a tile map. The tile set is our collection of reusable eight-pixel-square images with four levels of colour. The tile map is list of which tiles to place in which positions on a 32 by 32 tile grid. When the tiles are lined up according to the map, this results in a four-colour, 256 by 256 pixel image. (We're just imagining this image, it never appears on screen.)

To add colour to our image, we then define colour palettes and an attribute map. The background uses eight palettes of four colours each, which we define using 15-bit RGB values. The attribute map associates each position on the tile map with one of our eight palettes. When we arrange the tiles according to the tile map, and then colour each tile according to the attribute map, the result is a 256 by 256 pixel image with up to 32 colours. (This is what appears on the Game Boy Color screen.)

0
1
2
3
4
5
6
7
8
9
+
=
tile set
tile map
monochrome image
+
0
1
2
3
+
=
monochrome image
colour palettes
attribute map
colour image

Figure 2: A colour background image is created by defining a tile set, tile map, colour palettes, and an attribute map. (Toy example) Top: A monochrome Game Boy draws the background using a tile set and tile map. Bottom: The Game Boy Color adds an additional attribute map, which assigns colour palettes and other attributes to the tiles on the tile map. Note: In reality, the tile set has up 512 tiles, there are eight four-colour palettes, and the tile and attribute maps are each 32 by 32 tiles in size.

Edit the tile map, attribute map, or palettes to change the final colour image.

At 256 pixels square, the background is significantly larger than the size of the Game Boy Color screen (160 pixels by 144 pixels). We can scroll around the background by writing X and Y offsets to dedicated hardware registers (Figure 3). Note that, compared to updating the tile set or tile map, scrolling the background is quick and efficient. This makes many graphical tricks involving the scroll registers feasible, one of which we will see later.

Scroll X:
0
Scroll Y:
0
Full background image
On-screen view

Figure 3: The background layer scrolls and wraps in the X and Y directions. (Toy example) Left: The background tile map is 32 tiles by 32 tiles square (256 pixels by 256 pixels). Right: A 160 pixel by 144 pixel view of the background is shown on screen. This view can be scrolled by writing to the background scroll registers.

Drag the sliders to scroll the background on the screen.

Objects

Objects, also known as sprites , are little chunks of graphics that we can place anywhere on the screen, unconstrained by the grid layout of the background. Object tiles are eight pixels wide and 16 pixels tall, and have their own set of eight palettes. Instead of four-colour palettes like the background, we get three colours plus transparency. The transparent colour is critical because it means the objects can appear with any shape on screen, not just little rectangles.

To draw objects, we start by defining a tile set and colour palettes. Then we specify the location and appearance of each object by writing to Object Attribute Memory (OAM). OAM stores the X and Y coordinates, tile index, and attributes of each object. The hardware takes care of compositing each object image into the background and displaying the result on screen.

Attributes include the colour palette to use, whether to mirror the object vertically or horizontally, and whether the background has priority over the object. When the background has priority, parts of the background are rendered on top of the object. Background pixels with colour zero—the first colour in each palette, corresponding to the lightest shade on monochrome Game Boys—will be drawn behind the object. Pixels with any of the other three colours are drawn in front of the object. Figure 4 shows a small example of how OAM works for four objects.

Object 0
0
0
Object 1
0
0
Object 2
0
0
Object 3
0
0
OAM definitions
+
0
2
4
6
8
+
0
1
2
3
=
Monochrome tile set (shown 3x scale)
Object palettes
On-screen view (with background)

Figure 4: Objects are placed on screen by defining a tile set and palettes, and writing parameters to Object Attribute Memory. (Toy example) Note: the background priority flag is shown to affect the order of overlapping objects. This is inaccurate; on real hardware, only the relationship with the background is changed.

Change the OAM data or palettes to see how the objects change in the final image.

There are three main uses for objects in Invasion of the Geminorons. The first is to display things that need to appear on top of the background, such as the cursor, or particle effects (Figure 1, frames 423–458). In other words, things that are conceptually different from anything drawn on the background.

The second is to display things that don't align with the grid structure of the background. Gems are normally rendered on the background, but when they're falling they need to slide between positions in the background grid. For this reason, falling gems are temporarily drawn using objects.

The third use is to add extra colours to the background, to get around the restrictions and limitations of four-colour palettes. For the yellow margin to be drawn completely on the background, that colour would need to be present in all eight background palettes (using 8 / 32 total colour slots). To avoid this, the margin is drawn partially on the background, and the gaps are filled using objects (Figure 1 , frame 72). Status and shield indicators are also drawn with objects so they can have a different "foreground" colour to the rest of the UI.

There are 40 "slots" in OAM, and consequently we can show 40 objects on screen at once. A key limitation is that no more than 10 may appear on one screen line. This is why gems fall one column at a time. A whole screen of gems falling at once would exceed both the per-frame and per-line limits.

Window

The window is essentially a second background. It shares the tile set and palettes of the background, and all of the steps shown in Figure 2 apply equally to the window. The difference between the window and background is simply how they are placed on screen; the window can appear partially on screen, or not at all.

We specify an X and a Y coordinate, and the window occupies the rectangle stretching from that point to the bottom right corner of the screen. If the point lies off-screen to the right or bottom, the window is hidden. Everywhere that the window is visible, it completely covers the background, with no transparency possible (Figure 5). Window-object layering works exactly like background-object layering.

A common use for the window is to draw a status bar or menu that stays fixed on the screen while the background scrolls. In Invasion of the Geminorons, it's used for some menu overlays, but otherwise doesn't play a large role.

Window X:
80
Window Y:
80

Figure 5. The window is placed by setting the window X and Y coordinates. (Toy example) The window's X-coordinate is offset from the screen coordinate by seven pixels. Setting an X value of 167 or greater, or a Y value of 144 or greater hides the window completely.

Drag the sliders to adjust the window position.

Asterisks: mid-frame adjustments

When we talk about the Game Boy Color, we often say things like "the window stretches to the bottom right corner of the screen," when what we really mean is:

The window stretches to the bottom right corner of the screen*

(*Unless we change the window position while the frame is being drawn.)

A lot of statements have these implied asterisks.

The Game Boy Color draws lines to the screen one at a time, from top to bottom. Each line renders with the graphical parameters defined at that moment: colour palettes, window position, object positions, etc. Crucially, there is a short period of time between each line when we can update these parameters. If we change the parameters while the frame is being drawn, any lines drawn after the change will be drawn with the updated parameters, while any lines drawn before the change are unaffected.

This opens up a huge number of creative possibilities, and effects that exploit this feature are known as raster effects or scanline effects.

A simple example appears in the title screen of Invasion of the Geminorons, shown in Figure 6 . A logo image is drawn using a single four-colour palette. But by updating the definition of this palette throughout the frame, we create vertical colour gradients for the background and text.

Background
Objects
Window
Final frame
Current line:
-
Background palette 0:
Monochrome image

Figure 6: Updating palette definitions mid-frame produces extra colours on screen. Left: The final frame is shown rendering line-by-line. The background appears exactly as it is defined on the currently rendering line. Right: The background image before palettes are applied. Every tile on screen is mapped to background palette 0.

Drag the slider to draw lines to the screen.

The main game screen of Invasion of the Geminorons uses the same principle, but in addition to colour palettes, the positions of the background, window, and objects all change mid-frame.

On the background, one palette is dedicated to the two character portraits, and updated several times throughout the frame. Menus are rendered on an offscreen area of the background but can be drawn on screen when needed by updating the background scroll Y value (Figure 7).

1. This is a typical in-game background image for Invasion of the Geminorons, seen before any palettes are applied.

This is the full 256 pixel by 256 pixel background.

2. The highlighted area (160 by 144 pixels) is what's usually visible on screen.

3. This area contains menu graphics. It's usually outside of the visible area. However, by manipulating the background scroll registers mid-frame, we can cut to this area and back again.

In other words, we can choose screen lines where one of these menus is drawn instead of the normal game background.

4. The UI around the border of the game area is drawn using one four-colour palette, palette number 0.

Background palette 0:

5. When it's the CPU opponent's turn, the colour scheme is changed by redefining palette 0.

Background palette 0:

6. Each type of gem has it's own four-colour palette, for six palettes in total (numbers 1–6). Energy counters in the UI are also drawn with these palettes.

Background palette 1:
2:
3:
4:
5:
6:

7. The final four-colour palette, palette number 7, is used to draw both character portraits. This palette is updated several times during each frame, to squeeze in extra colours. The exact colours vary depending on which characters are active on screen.

Background palette 7:
and
and
and
and
and

Figure 7: Organisation of the background tile map and colour palettes in Invasion of the Geminorons.

Drag the slider to highlight areas of interest.

So that the window appears in the middle of the screen, instead of on the bottom edge (Figure 1 , frame 72), we update the X coordinate in the middle of the frame. After the last line of the menu is rendered on screen, we set the window X coordinate to 167 (past the right edge of the screen). This hides the window on the remaining lines drawn to the screen.

Two 40 pixel tall vertical lines are drawn with objects to fill in gaps in the yellow margin surrounding the game area (Figure 1 , frame 0). The lines are identical in every frame, and simplest solution would be to use place six objects at the start of the game and never change them. However, we can save four OAM slots by using two objects that move around mid-frame to cover all the necessary areas.

Figure 8 shows how these adjustments to the background, objects and window combine to make the final frame.

Background
Objects
Window
Final frame
Current line:
-
Background scroll (X,Y):
0, 0
Window position (X,Y):
167, 63
Object 9 position (X,Y):
0, 0
Object 10 position (X,Y):
0, 0
Background palette 7:

Figure 8: Rendering a frame line-by-line. The background, objects, and window appear as they are defined on the currently rendering line.

Drag the slider to draw lines to the screen.

Background layer, part II

Tile sets and tile maps

There are a few more details to add about the background layer. In Figure 2, the tile set is fixed, and the background is changed by modifying the maps and palettes. In reality, tile data is stored in Video RAM, and can be edited just like the tile map. If we want to change the background image, we always have a choice of changing the tile map, or the attribute map, or the palettes, or the tile set, or any combination of those. Figure 9 shows how changes to the background tile set interact with changes to the background tile map.

Drawing colour:
0
1
2
3
4
5
6
7
8
9
+
=
tile set
tile map
monochrome image

Figure 9: Changing either the tile set or the tile map changes the background image. (Toy example)

Edit the tile set or tile map to change the monochrome background image.

When should we update the tile set, and when should we update the tile map? There are three main things to consider. The first is locality. If we change the definition of a tile in the tile set, the new tile replaces the old tile everywhere it appears on screen. On the other hand, when we edit an area of the tile map, only that part of the background changes.

The second consideration is speed. Tile maps use one byte per tile index, but we need 16 bytes to define a tile image. For the cost of updating one tile definition in Video RAM, we can update a row of 16 tiles on the tile map.

The final consideration is space. We can fit 512 tiles in Video RAM, and sometimes the best way to manage that space is to write new tile images over ones we don't need.

Attribute maps

One byte can't store 512 different values, so to actuallyuse 512 tiles, we need to return to the attribute map. The Game Boy Color divides Video RAM into two banks, and a bit in the attribute map selects whether a tile should be drawn from bank 0 or bank 1. In other words, where the tile map say "204", that could mean tile 204 from bank 0, or tile 204 from bank 1, and the attribute map specifies which.

Swapping between tiles in different banks means we need to update both the tile map and the attribute map. Certain kinds of animation therefore benefit from having all the tiles stored in the same bank. Outside of that case, which bank a tile is stored in isn't very important.

Along with three bits to store the palette index, and one bit to store the bank, two more bits in the attribute map specify whether the tile should be flipped horizontally or vertically. One bit does nothing, and finally, one bit specifies the background priority: whether or not colours 1–3 of the background should be drawn in font of objects.

The effect of setting the background priority bit is almost identical to setting the background priority bit in OAM. The difference is that setting the bit in OAM specifies that one object should be drawn behind all background tiles, while setting the bit in the attribute map specifies that one background tile should be drawn in front of all objects.

Figure 10 shows how the complete attribute map influences the background image.

a)
Tile bank:
0b
 = 0x00
b)
Priority
Flip Y
Flip X
Bank
Palette
0
1
2
Bank 0
0
1
2
Bank 1
+
0
1
2
3
4
5
6
7
+
+
=
Tile set
Palettes
Tile Map
Attribute Map
Final background

Figure 10: Background priority, tile flipping, source tile bank and palette index are all specified in the attribute map. (Toy example) a) The five attributes are packed into one byte. The unlabeled bit does nothing. b) The full attribute map combines with palette and tile map definitions to create the background image.

Edit the attributes, palettes or tile map to change the final background image. Note: setting the priority bit has no visible effect because there are no objects shown.

Now that we've seen the full details of tile sets and attribute maps, Figure 11 shows how the background tile set is arranged across the two banks of Video RAM.

_0
_1
_2
_3
_4
_5
_6
_7
_8
_9
_A
_B
_C
_D
_E
_F
_0
_1
_2
_3
_4
_5
_6
_7
_8
_9
_A
_B
_C
_D
_E
_F
0_
1_
2_
3_
4_
5_
6_
7_
8_
9_
A_
B_
C_
D_
E_
F_
0_
1_
2_
3_
4_
5_
6_
7_
8_
9_
A_
B_
C_
D_
E_
F_
Bank 0
Bank 1

1. This is a snapshot of Video RAM from Invasion of the Geminorons , showing all tiles available for use in the background. Tiles are stored in two banks. Tiles 0x00–0x7F in each bank may only be used by the background. Tiles 0x80–0xFF are shared with the objects layer. Another 128 tiles in each bank (not shown) may only be used for objects.

2. In practice, the tiles highlighted in magenta—the animation frames for each gem at rest—are used on both the background and object layers.

The tiles on the lower right, highlighted in cyan, are only used as objects. Every other tile shown is used for the background only.

3. The highlighted tiles are used to draw the opponent's health bar. Every tile needed for the bar is stored in Video RAM, and the bar is animated by updating the tile map. It's important that each frame of the animation is stored in the same bank, so we don't need to update the attribute map at the same time.

25

4. Similarly, the gem-clearing animations and counter digits have all their frames stored at once in Video RAM. The tile map is updated to cycle through the animation.

5. In contrast to the health bars and gems, other parts of the background are updated by updating the tile set. Only two character portraits are kept in Video RAM. When a new character is loaded, the tile map remains the same, but the tiles for the previous character's portrait are replaced with a new picture.

An identical process happens for special-power symbols; to change the symbol, we write new tiles over the old ones.

6. A large number of tiles are dedicated to text. The text uses a variable-width font, so the letters don't line up neatly with tile boundaries. To save on ROM space, the ROM contains ASCII strings, and one-bit-per-pixel glyphs for each letter; the actual tiles are generated at run time.

Text highlighted in magenta is rendered at startup and remains unchanged. Text highlighted in cyan is associated with a particular character or power, and the tiles are replaced when the text needs to change.

7. Tiles for the players health bar are highlighted in magenta. The tiles are stored upside-down and backwards in Video RAM, then flipped using the attribute map.

The reason for this is that the tiles are generated at run-time. The player's health bar has the same orientation as the opponent's (cyan) because they're created from the same source data.

Meanwhile, power names are rendered with a yellow border along the top. The player's health bar needs a border along the bottom. We can use the same code to add both borders by working with an upside-down version of the health bar tiles.

Figure 11: Organisation of the background tile set in Invasion of the Geminorons.

Drag the slider to highlight areas of interest.

Figure 12 shows part of the transition between one oppponent and the next. After defeating an opponent, an upgrade menu appears. At the same time, the portrait, name, and special power graphics of the defeated opponent are replaced in Video RAM.

This part of the engine isn't designed to be fast; we have plenty of time while the player reads the menu text, and doing things slowly saves some ROM space. There are a few glitchy frames visible (frames 122–124) where the new portrait tiles appear with the old palette and character name. Note that it is possible to seamlessly update some tiles every frame and achieve smooth animations; Invasion of the Geminorons just made different trade-offs to fit in 32kB of ROM.

Background
Objects
Window
Final frame
Frame: 
0

Figure 12: The background tile set is updated during the transition between one opponent and the next. The portrait, text, and special power logos change as the old tiles are replaced in Video RAM by new versions.

Drag the slider to step frame-by-frame through the animation.

Objects layer, part II

Much like the background layer, we can animate objects by updating OAM to change the tile index, or by writing over the tile data in Video RAM with new frames of animation. In practice, the object animations in Invasion of the Geminorons are quite simple. Every frame of every animation fits in Video RAM at once, and updating the tile set in unnecessary. The tile data is loaded once at the start of the game and doesn't change (Figure 13).

_0
_2
_4
_6
_8
_A
_C
_E
_0
_2
_4
_6
_8
_A
_C
_E
0_
1_
2_
3_
4_
5_
6_
7_
8_
9_
A_
B_
C_
D_
E_
F_
0_
1_
2_
3_
4_
5_
6_
7_
8_
9_
A_
B_
C_
D_
E_
F_
Bank 0
Bank 1

1. This is a snapshot of Video RAM from Invasion of the Geminorons, showing all tiles available for use as objects.

Tiles 0x80–0xFE in each bank use the same pixel data as background tiles 0x80–0xFF (see Figure 11).

When used for objects, the Game Boy interprets two consecutive eight by eight pixel tiles as one eight by 16 pixel tile. Colour zero (shown here as white) is transparent for objects.

(background) =(object)

2. Only the highlighted tiles are actually used for objects.

3. The tileset doesn't change, and animations are performed by updating the screen coordinates, tile index, and/or X-flip and Y-flip bits in OAM.

Figure 13: Organisation of the objects tile set in Invasion of the Geminorons.

Something that's unique to the objects layer is that objects can overlap one another. There's a simple rule for deciding which of two overlapping objects appears in front of the other: objects appearing earlier in OAM cover objects appearing later. In other words, the object at index zero appears in front of all other objects, the object at index 20 appears in front of objects 21–39, and the object at index 39 appears behind all other objects.

In frames 270–281 of Figure 1 , we can see a blue gem slide out from underneath part of the background. The objects that make up the gem have the priority bit set in OAM, and so appear behind colours one to three of the background. A dark red square, the same colour as colour zero of the background, blocks any part of the blue gem that might otherwise show through the background. Figure 14 shows in detail how the objects are layered so that the gems appear to be fully behind the background.

The blue shield pips appear on top of the background because they don't have the background priority bit set in OAM. However, they must have a lower index in OAM than either the gems or the red square; otherwise they would be hidden behind the other objects.

a)
BKG
OBJ
b)
BKG
OBJ
c)
OBJ
OBJ
d)
BKG OBJ
OBJ

Figure 14: Layered objects allow gems to move completely behind the background. a) The blue gem (two objects) has its priority bits set, and appears partially behind the background. b) The dark red square (two objects) is also only partially covered by the background, but appears fully covered because of its colour. c) The red square appears in front of the blue gem because the red objects appear before the blue objects in OAM. d) The red objects from b) are layered behind the background. The blue gem is drawn behind the red square, so it appears to be fully concealed behind the background.

Summary

Game Boy Color graphics are drawn using three layers: the background layer, window layer, and objects layer. The background and window layers place eight by eight pixel tiles on a fixed grid. The objects layer places eight by 16 pixel tiles at arbitrary positions on screen.

We can have 40 objects on screen (soft limit) and no more than 10 per screen line (hard limit). For this reason, the majority of pixels on screen must come from the background or window layers.

One in-game entity or concept can be split up and drawn on multiple layers, or move between layers as needed.

For backwards compatibility with the original Game Boy, background tiles are restricted to four colours, while object tiles use three colours plus transparency.

We can animate the background by changing the tile set or tile map or attribute map or colour palettes or X and Y scroll offsets. The best choice is highly dependent on the situation.

Objects are animated by updating Object Attribute Memory, or changing the tile set or colour palettes.

Many soft limitations can be broken by using hardware interrupts to update graphical parameters mid-frame.

Further information