Sprites

Okay, now it's time to add some sprites to our scene!

How do the Sprites Work?

Well, first of all, you have a big array of sprite attributes. The area of memory that holds this is called the OAM (Object Attribute Memory). The main engine's OAM is mapped to memory address 0x7000000->0x70003FF, it's 1024 bytes large. Each sprite entry takes up 8 bytes (actually 6, but we'll round up), so you can have 128 sprites defined in the OAM at a time. (wee you can draw 128 sprites on the screen!)

Now, for each sprite entry there are 4 Hwords of data. The first Hword in the sprite entry is Attribute0. The second, Attribute1. And the third, Attribute2. The last Hword isn't a sprite attribute, but it is used to define rotation/scaling parameters.

Each of the OBJ attributes define how the sprite will look when it is to be displayed. Let's pick apart the attributes.

Attribute 0

Here is the first attribute... It controls a bunch of things.

Vertical Position

This is where the top-left coordinate of the sprite will be when it is drawn on the screen (measured in pixels).

This number can be negative too, to show the sprite crossing the top boundary. For negative numbers, the value is specified in twos-complement format (just AND the value with 0xFF and you'll be fine).

Rot/Scale Enable

This flag enables rotation/scaling effects to be applied when rendering the sprite. We'll look into this more later.

Rot/Scale Double Size

When the Rot/Scale flag is 1, then this flag will double the boundary size of the sprite. This is useful to prevent the edges of the sprite from being clipped.

IMPORTANT: When the Rot/Scale flag is 0, then this is the disable flag. Setting this bit for non rot/scale sprites will disable the sprite from being rendered. Back in the early GBA days, it seems people didn't know about this bit much, so to hide the sprites, they would move the vertical position off of the screen (or worse, the horizontal position). That method will effectively hide the sprite, but it does not stop it from using the rendering cycles. Setting this bit to disable the sprite will save you some rendering cycles!

OBJ Mode

This value ranges from 0->3.

When this value is 0, the sprite will act normally. :P

When this value is 1, then this sprite will be selected for the alpha blending, first target, regardless of the OBJ setting in the BLDCNT register.

When this value is 2, then the shape of this sprite will be added to the OBJ window. The OBJ window is something that masks certain parts of some backgrounds/other sprites with pretty shapes (shapes made by sprites put together).

When this value is 3, then this is a bitmap sprite. These can display 16-bit direct color images.

Mosaic Enable

This bit enables mosaic effects to be applied to the sprite. See the backgrounds chapter for an example of what mosaic looks like.

16/256 Colors

If this bit is 1, then the sprite will use 256 color tile data. We will use 0, for 16-color sprites.

Shape

This bit changes the shape of the rectangular drawing region for the sprite. 0 = Square, 1 = Wide, 2 = Tall, 3 = Do not use.

See the table for the Size attribute below.

Attribute 1

This attribute has two different modes, depending on whether the Rot/Scale enable flag was set in attribute0.

Horizontal Position

This is the X position of the top left corner of the sprite rectangle from the left side of the screen (measured in pixels).

This number can be negative too, mask negative values with 0x1FF before writing it to the sprite attribute!

Horizontal Flip

Setting this bit will make the sprite image flipped horizontally. Like this: (flipped one is on the right)

Vertical Flip

Setting this bit will make the sprite image flipped vertically. You can also mix the two flipping bits together.

Size

This setting controls the size of the drawing rectangle and how many tiles will be used for rendering the sprite.

Each setting will draw (width/8) * (height/8) tiles.

Attribute1 (Rot/Scale Enabled)

In this mode, there are no horizontal/vertical flipping flags, and we have a new setting.

Rot/Scale Index

This value selects one of the rot/scale parameters for transforming the pixels (0..31). The parameter data is mixed into the OAM memory, it uses the 4th unused Hword of each sprite entry.

In the diagram, we see a picture of the OAM memory with each cell representing 2 bytes (1 Hword). AT0, AT1, and AT2 are the three sprite attributes for each sprite entry. The last Hword of each entry form the affine matrices, each matrix has four values: PA, PB, PC, and PD. These values may also be called dx, dmx, dy, and dmy. Personally, I like to call them XX, XY, YX, and YY. They form a 2x2 matrix that is used to transform the pixels of a sprite.

More info on this in a later chapter.

Attribute 2

Character Index

This is the number of the first tile that the sprite will draw.

Which tiles are used depends on the layout of the tile vram (layout is selected in the video mode).

1D Layout

This is the mode we are using, here is a closup of our OBJ VRAM at the beginning.

Now, in 1D mode, the tiles are chosen linearly, with the tiles placed in the sprite rectangle in the order left->right, top->bottom. Look what images the sprite will draw if we choose tiles 0->4:

I hope you understand what's going on there.. :)

2D Layout

In this mode, the tiles are arranged in a way so the VRAM block looks normal. This main disadvantage of this is that the data isn't linear, so you have to piece your tiles together like a puzzle in VRAM :P. This will be displayed if we try to draw our current sprite in 2D mode.

It'll look normal if we move the lower half of the sprite into the next row (and for larger sprites, move each row down to the next):

One advantage of 2D mode, is that you can use parts of 2 or more different images loaded in vram to make some sprites (you can't do this if both images are stored in linear format). Have a closer look at what happens when I shoot the center of the second boss in Super Wings.

And here is a snapshot of all the tile data, everything is clearly visible because of the 2D layout.

Now, although the 128x64 ship could be rendered with 2 64x64 sprites, I additionally pasted another 32x64 sprite on top of it. This is the sprite that flashes when you shoot the center.

In 1D mode, the ship would have to be arranged like something like this:

Can't really get a center piece from that. :P (would have to load/generate separate tile data).

Bitmap layout

For bitmap sprites, the layout is a bit screwy. ;)

I don't want to clutter up this tutorial with bitmap stuff, so maybe I'll explain it later in another tutorial.

Priority

Here is the priority setting, it's just like the one for BG. Sprites have a higher base priority than BGs. If a sprite has the same priority number as a BG, then the sprite will be rendered on top of the BG.

Palette

This is the index of the 16-color palette to use to render the 16-color tile data. In 256 color mode, this value isn't used.

For direct color bitmap sprites, this entry is used as a per-sprite alpha value. It is used together with the alpha blending effects controlled by BLDCNT.

Displaying Some Sprites

We'll want to modify the OAM entries easily, add a definition to make a reference of the OAM casted to a struct containing the attributes.

typedef struct t_spriteEntry
{
    u16 attr0;
    u16 attr1;
    u16 attr2;
    u16 affine_data;
} spriteEntry;

#define sprites ((spriteEntry*)OAM)

Okay. Go to your setupGraphics function again. Let's start by enabling sprites (and 1D layout)!! Change your video mode.

    videoSetMode( MODE_0_2D | DISPLAY_BG0_ACTIVE | DISPLAY_BG1_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D_LAYOUT );

Compile and run the game, you should see this:

Notice the little crap in the top-left corner. That is a sprite! Actually, it's 128 uninitialized sprites stacked on top of each other. ;)

To fix this, let's set all the sprite entries to disabled before setting the video mode.

    int n;
    for( n = 0; n < 128; n++ )
        sprites[n].attr0 = ATTR0_DISABLED;

ATTR0_DISABLED is an alias for the double size flag (used as disable bit when theres no rotation/scaling). Compile and run the game. The sprites in the corner should be gone now.

Let's add some of our own sprites! We'll add balls all over the screen! [that sounded a little wrong...]

    // code to test out the sprite engine
    int n;
    for( n = 0; n < 50; n++ )
    {
        // attribute0: set vertical position 0->screen_height-sprite_height, 
        // other default options will be okay (default == zeroed)
        sprites[n].attr0 = rand() % (192 - 16);

        // attribute1: set horizontal position 0->screen_width-sprite_width
        // also set 16x16 size mode
        sprites[n].attr1 = (rand() % (256 - 16)) + ATTR1_SIZE_16;

        // attribute0: select tile number and palette number
        sprites[n].attr2 = tiles_ball + (pal_ball << 12);
    }

Compile and run...etc. You should see the amazing image below:

Whee! :D

Previous: Loading sprite graphicsContentsNext: Fixed point arithmetic