スプライトとは8x8~64x64サイズの画像を最大128個まで表示します。シューティングゲームでいう自機や弾、RPGでいう主人公キャラなどに使います。今回登場するメモリ領域は以下のとおりです。なお、図のとおりキャラクタデータはタイルモードとビットマップモードで位置が変わり、格納サイズも変わるという点に注意してください。早速、パレットデータ、キャラクタデータ、属性(Object Attribute Memory)の3つをそれぞれ見ていきましょう。
名前 | 開始アドレス |
パレットデータ(256色1パレット or 16色16パレット) | 0x5000200 |
キャラクタデータ(タイルモード時) | 0x6010000 |
キャラクタデータ(ビットマップモード時) | 0x6014000 |
属性(Object Attribute Memory) | 0x7000000 |
フォーマットはmode3と同じです。構造はそのままに、16色をパレット16個か、256色をパレット1個で指定することも可能です。
BG and OBJ palettes are using separate memory regions: 05000000-050001FF - BG Palette RAM (512 bytes, 256 colors) 05000200-050003FF - OBJ Palette RAM (512 bytes, 256 colors) Color Definitions Each color occupies two bytes (same as for 32768 color BG modes): Bit Expl. 0-4 Red Intensity (0-31) 5-9 Green Intensity (0-31) 10-14 Blue Intensity (0-31) 15 Not used
キャラクタデータは、パレット番号を指定した集まりです。単位は1キャラクタを8x8ドットとしています。16色の場合、4bitでちょうど16色指定できるので0x0〜0xfピッタリのサイズです。256色も0x0~0xffまでなのでデータを無駄にすることなく使うことができます。
色 | ドット単位の格納方法 | サイズ |
16 | 横2ドット=1byte(下位4bitが左、上位4bitが右) | 32byte |
256 | 横1ドット=1byte | 64byte |
16色のパレットデータとキャラクタデータの出力例を以下に表します。この画像は8x8ドットで上部をシマシマ模様にしています。結果がそのままバイナリに反映されていることを確認してください。
grit.exe spr2.png -gt -gu8 -gB4 -fts
spr2Tiles: .byte 0x10,0x32,0x54,0x76,0x98,0xBA,0xDC,0xFE,0x10,0x32,0x54,0x76,0x98,0xBA,0xDC,0xFE .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 spr2Pal: .hword 0x0000,0x0010,0x0200,0x0210,0x4000,0x4010,0x4200,0x6318 .hword 0x4210,0x001F,0x03E0,0x03FF,0x7C00,0x7C1F,0x7FE0,0x7FFF
単位は1キャラクタを8x8ドットと先ほど説明していました。たとえば以下の256x128ドットのキャラクタデータがあったとしましょう。
このとき、キャラクタ番号は0番「空白」、1番「!」、2番「”」、3番「#」、となります。8x8単位のみを扱うのでしたらこのキャラクタ番号を指定し、表示するだけで話は終わります。ところが8x8単位以上のものを表示したい場合、格納方法に工夫がいります。格納が1次元か2次元か、ということです。例として文字列の下にある球体を32x32ドットで表示したい場合を考えてみます。この球体の番号は96です。1次元で32x32を取り出した場合のキャラクタ番号は96, 97, 98, 99, 100, 101, 102・・・111となります。結果は以下の通り。
円がきれいに表示されませんでした。では番号を224に変えてみましょう。
うまく行きました。この方法が1次元的取り出し方法です。データが切れ目なく連続しているため、キャラクタデータを入れ替えるときにとても便利です。個人的には後に教える2次元方法より良いと思っています。シューティングゲームなどでは頻繁にキャラクタデータの変更を行います。ボス戦なら画面いっぱいにキャラクタデータを使うので今のうちに1次元に慣れておくのがいいでしょう。次に2次元的取り出し方ですが、これは番号96にした場合の取り出し番号が96, 97, 98, 99, 128, 129, 130, 131, 160, 161, 162, 163となります。取り出す時の形状には、32x32以外にも以下のようなサイズがあります。
// スプライトの指定サイズ enum SPRITE_SIZECODE { Sprite_8x8, Sprite_16x16, Sprite_32x32, Sprite_64x64, Sprite_16x8, Sprite_32x8, Sprite_32x16, Sprite_64x32, Sprite_8x16, Sprite_8x32, Sprite_16x32, Sprite_32x64 };
スプライトと属性は1:1の関係です。スプライトは最大128個ですので属性も128個あると思ってください。属性1つ分にはattr0, attr1, attr2, dummyという単位で構成されています。いわゆるC言語でいう構造体です。様々なパラメータが存在しており、いきなり覚えようというのは難しいと思います。今は軽く読み流す程度で結構です。
typedef struct { u16 attr0; u16 attr1; u16 attr2; u16 dummy; } OBJATTR;
Bit Expl. 0-7 Y-Coordinate (0-255) 8 Rotation/Scaling Flag (0=Off, 1=On) When Rotation/Scaling used (Attribute 0, bit 8 set): 9 Double-Size Flag (0=Normal, 1=Double) When Rotation/Scaling not used (Attribute 0, bit 8 cleared): 9 OBJ Disable (0=Normal, 1=Not displayed) 10-11 OBJ Mode (0=Normal, 1=Semi-Transparent, 2=OBJ Window, 3=Prohibited) 12 OBJ Mosaic (0=Off, 1=On) 13 Colors/Palettes (0=16/16, 1=256/1) 14-15 OBJ Shape (0=Square,1=Horizontal,2=Vertical,3=Prohibited)
Bit Expl. 0-8 X-Coordinate (0-511) When Rotation/Scaling used (Attribute 0, bit 8 set): 9-13 Rotation/Scaling Parameter Selection (0-31) (Selects one of the 32 Rotation/Scaling Parameters that can be defined in OAM, for details read next chapter.) When Rotation/Scaling not used (Attribute 0, bit 8 cleared): 9-11 Not used 12 Horizontal Flip (0=Normal, 1=Mirrored) 13 Vertical Flip (0=Normal, 1=Mirrored) 14-15 OBJ Size (0..3, depends on OBJ Shape, see Attr 0) Size Square Horizontal Vertical 0 8x8 16x8 8x16 1 16x16 32x8 8x32 2 32x32 32x16 16x32 3 64x64 64x32 32x64
Bit Expl. 0-9 Character Name (0-1023=Tile Number) 10-11 Priority relative to BG (0-3; 0=Highest) 12-15 Palette Number (0-15) (Not used in 256 color/1 palette mode)
こちらで用意した関数を通して簡単な(大雑把な)扱いを知ってください。
関数名 | 用途 |
SpriteInit | 初期化 |
SpriteMove | 移動 |
SpriteSetSize | サイズの設定 |
SpriteSetChr | キャラクタ番号の設定 |
void SpriteInit(void) { u32 i; for(i=0; i<128; i++) { SpriteMove(i, 240, 160); } }
すべてのスプライトを画面外に移動させます。非表示にするといった機能はありますが、念のため必ず初期化時に実行します。
void SpriteMove(u32 num, s32 x, s32 y) { OBJATTR* sp = (OBJATTR*)OAM + num; sp->attr1 &= 0xfe00; sp->attr0 &= 0xff00; sp->attr1 |= (x & 0x01ff); sp->attr0 |= (y & 0x00ff); }
スプライトの移動処理です。範囲はGBAの液晶画面サイズ240x160と同じではなく512x256となっています。-3,-3といった中途半端な表示にも対応しているわけです。
void SpriteSetSize(u32 num, u32 size, u32 form, u32 col) { OBJATTR* sp = (OBJATTR*)OAM + num; sp->attr0 &= 0x1fff; sp->attr1 &= 0x3fff; sp->attr0 |= col | form; sp->attr1 |= size; }
スプライトの形状と色数を変更します。引数size, formはgba.hの説明にあるものから選択して使用します。詳細はソースコードを読んでください。
void SpriteSetChr(u32 num, u32 ch) { OBJATTR* sp = (OBJATTR*)OAM + num; sp->attr2 &= 0xfc00; sp->attr2 |= ch; }
スプライトで使用するキャラクタ番号を指定します。このほかにもいろいろな関数を用意できますが、今回はこれだけを使います。
int main(void) { // モード設定 SetMode(MODE_0 | OBJ_ENABLE | OBJ_2D_MAP); u16* oam = OBJ_BASE_ADR; // キャラクタデータ u16* pal = OBJ_COLORS; // パレットデータ u32 i; for(i=0; i<spr1TilesLen/2; i++) { oam[i] = spr1Tiles[i]; } for(i=0; i<16; i++) { pal[i] = spr1Pal[i]; } SpriteInit(); s16 x = 0; s16 y = 0; u16 s = 0; SpriteSetSize(0, OBJ_SIZE(s), OBJ_SQUARE, OBJ_16_COLOR); SpriteSetChr (0, 1); SpriteMove (0, x, y); for(;;) { WaitForVsync(); SpriteMove(0, x, y); u16 key = ~(REG_KEYINPUT); if(key & KEY_UP) y--; if(key & KEY_DOWN) y++; if(key & KEY_RIGHT) x++; if(key & KEY_LEFT) x--; // フレーム単位で形状を変更 SpriteSetSize(0, OBJ_SIZE(s>>4), OBJ_SQUARE, OBJ_16_COLOR); s++; if((s>>4) > 3) { s=0; } } }
スプライトを使用するにはSetModeにOBJ_ENABLEを入れます。OBJ_1D_MAPかOBJ_2D_MAPかの2択で、取り出し方が1次元か2次元か指定します。
SetMode(MODE_0 | OBJ_ENABLE | OBJ_1D_MAP);