GB101:Sprites

From NYC Resistor Wiki
GB101 Class Notes
Sections
  1. Home
  2. Installing Gameboy Development Tools
  3. Introduction to the Gameboy Advance
  4. Hello Background
  5. Working with Palettes
  6. Tiles and Background Maps
  7. Sprites
  8. Scrolling Backgrounds
  9. Collision Detection
  10. Fixed point math
  11. Affine Sprites
  12. Sound
  13. Where do I go from here?

Introduction[edit]

In addition to the background tiles, the GBA features hardware sprite handling. Sprites and backgrounds are similar in that they are built out of 8x8 bitmap tiles. Instead of a tile map, sprites have object attributes which are stored outside of video ram. These attributes describe where in memory to find the graphics, the palette, the position on screen, as well as other bits. The part of memory where these are stored is generally referred to as OAM, and each entry is referred to as an OAM entry.

Sprites[edit]

Converting images[edit]

Because we'll be loading a bitmap with several tiles in it we'll need to convert it into an array so that the GBA can read it sequentially. That is, each tile should be able to be read sequentially so we can memcpy the whole thing into VRAM.

We'll be using the utility grit that comes with devkitPro. To use it, use the following syntax:


grit image.bmp -gB**COLOR DEPTH** -Mh**TILES ACROSS** -Mw**TILES DOWN** -ftc -W3

grit image.bmp -bB4 -Mh2 -Mw2 -ftc -W3


A switch to TONC[edit]

At this point I will be introduction an alternate library to libgba called TONC. The TONC library used througout the TONC tutorials and samples. It is a much cleaner and more organized library, and the sprite handling is much more straightforward.

Implementation[edit]

#include <tonc.h>
#include <string.h>
#include "link.h"
#include "tiles.h"

// The buffer to store the OAM entries between vblanks
OBJ_ATTR obj_buffer[128];

// This is our main loop
void obj_test()
{
	// our position
	int x= 96, y= 32;
	// tile id, palette bank
	u32 tid= 64, pb= 0;		
	
	// Get a pointer to the first OAM entry
	OBJ_ATTR *link= &obj_buffer[0];
	obj_set_attr(link, 
		ATTR0_SQUARE,				// Square, regular sprite
		ATTR1_SIZE_16,				// 8x8px sprite	
		ATTR2_PALBANK(pb) | tid);	// palette 0, tile 64	

	// position sprite (redundant here; the _real_ position
	// is set further down
	obj_set_pos(link, x, y);

	while(1)
	{
		// Wait for VBlank
		vid_vsync();
		
		// Get the state of the input controls
		key_poll();

		// move left/right
		x += 2*key_tri_horz();

		// move up/down
		y += 2*key_tri_vert();

		// increment/decrement starting tile with R/L
		tid += bit_tribool(key_hit(-1), KI_R, KI_L);

		// change the sprite tid and/or flip bits to
		// face link the right way
		if (key_hit(KEY_LEFT)) {
			if (tid != 64) 
				tid = 64;
			BIT_SET(link->attr1,ATTR1_HFLIP);
		}
		else if (key_hit(KEY_RIGHT)) {
			if (tid != 64)
				tid = 64;
			BIT_CLEAR(link->attr1,ATTR1_HFLIP);
		}
		else if (key_hit(KEY_UP)) {
			if (tid != 128)
				tid = 128;
			BIT_CLEAR(link->attr1,ATTR1_HFLIP);
		}
		else if (key_hit(KEY_DOWN)) {
			if (tid != 48)
				tid = 48; 
			BIT_CLEAR(link->attr1,ATTR1_HFLIP);
		}
		
		// toggle mapping mode
		if(key_hit(KEY_START))
			REG_DISPCNT ^= DCNT_OBJ_1D;

		// Change the tid and position
		link->attr2 = ATTR2_BUILD(tid, pb, 0);
		obj_set_pos(link, x, y);

		// Copy the bufferred OAM entries to the actual OAM memory
		oam_copy(oam_mem, obj_buffer, 1);	// only need to update one
	}
}

int main()
{
	// Load the link sprite into tile memory vram page 4
	memcpy(&tile_mem[4][0], linkTiles, linkTilesLen);
	
	// Load the palette memory into palette memory
	memcpy(pal_obj_mem, linkPal, linkPalLen);
	
	// Load the backgrounds
	memcpy(&tile_mem[0][0], (u8*)TS_Zelda, 256*256);
	memcpy(pal_bg_mem, (u16*)PAL_Zelda, 256*2);
	memcpy(se_mem, (u16*)MAP_Zelda, 32*20*2);

	// Set up our OAM entry buffer
	oam_init(obj_buffer, 128);
	
	// Set mode 0, bg 0, sprites enabled, 1d sprites
	REG_DISPCNT = DCNT_OBJ | DCNT_OBJ_1D | DCNT_MODE(0) | DCNT_BG0;

	// Set bg 0
	REG_BG0CNT = BG_8BPP | BG_CBB(0) | BG_SBB(31);
	
	// Start the demo
	obj_test();

	return 0;
}