スプライトとは8x8~64x64サイズの画像を最大128個まで表示します。シューティングゲームでいう自機や弾、RPGでいう主人公キャラなどに使います。今回登場するメモリ領域は以下のとおりです。なお、図のとおりキャラクタデータはタイルモードとビットマップモードでは位置が変わるので注意してください。早速、パレットデータ、キャラクタデータ、属性(Object Attribute Memory)の3つをそれぞれ見ていきましょう。
名前 | アドレス |
パレットデータ(256色1パレット or 16色16パレット) | 0x5000200~ |
キャラクタデータ(タイルモード時) | 0x6010000~ |
キャラクタデータ(ビットマップモード時) | 0x6014000~ |
属性(Object Attribute Memory) | 0x7000000~ |
データフォーマットはmode3で使った色データと同じです。
GBATEKから引用してみました。
Color Palette RAM 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
OBJ Palette RAMが今回のターゲットです。
データ構造はそのままに、16色をパレット16個か、256色をパレット1個で指定することも可能です。
キャラクタデータは、パレット番号を指定した集まりです。
単位は1キャラクタを8x8ドットとしています。
色 | ドット単位の格納方法 | サイズ |
16 | 横2ドット=1byte(下位4bitが左、上位4bitが右) | 32byte |
256 | 横1ドット=1byte | 64byte |
16色の場合、4bitでちょうど16色指定できるのでピッタリのサイズですね。
256色も0x0~0xffまでなのでデータを無駄にすることなく使うことができます。
たとえば以下のキャラクタデータがあったとしましょう。
#ref(): File not found: "clip_2.png" at page "tutorial.7"
このとき、キャラクタ番号は
空白 - 0 ! - 1 " - 2 # - 3 (以下略・・・)
となります。8x8単位のみを扱うのでしたらこのキャラクタ番号を指定し、
表示するだけで話は終わります。ところが8x8単位以上のものを表示したい場合、
格納方法に工夫がいります。
#ref(): File not found: "clip_3.png" at page "tutorial.7"
こちらは先ほどの画像を2倍表示して8x8単位に点線を加えたものです。
取り出し方法には1次元、2次元があります。仮に32x32で、1次元の取り出しを
キャラクタ番号96から行うと、次のような結果になります。
#ref(): File not found: "clip_4.png" at page "tutorial.7"
96, 97, 98・・・と取り出した為、円がきれいに表示されませんでした。
では224に変えてみましょう。
#ref(): File not found: "clip_5.png" at page "tutorial.7"
うまく行きました。
個人的には1次元の取り出し方を推奨します。なぜならデータが切れ目なく
連続しているため、キャラクタデータを入れ替えるときに便利だからです。
シューティングゲームなどでは頻繁にキャラクタデータの変更を行います。
ボス戦なら画面いっぱいにキャラクタデータを使うので今のうちに
1次元の方法に慣れておくのがいいでしょう。
入れ替えがない場合は2次元が使えます。
画像データを8x8単位に加工する必要がないのでうまく使い分けでください。
取り出す時の形状には、以下のようなサイズがあります。
// スプライトの指定サイズ 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つに対して、属性という単位で取り扱います。
構造体を表すと次のようになりますが、attr0~2までの中には
様々なパラメータが存在して、いきなり覚えようというのは難しいと思います。
今は軽く読み流す程度で結構です。
typedef struct { u16 attr0; u16 attr1; u16 attr2; u16 dummy; } OBJATTR;
#ref(): File not found: "clip_6.gif" at page "tutorial.7"
OBJ Attribute 0 (R/W) 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)
OBJ Attribute 1 (R/W) 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
OBJ Attribute 2 (R/W) 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)
#ref(): File not found: "clip_7.gif" at page "tutorial.7"
こちらで用意した関数を通して簡単な(大雑把な)扱いを知ってください。
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となっています。
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 | (160); sp->attr1 |= size | (240); }
スプライトの形状と色数を変更します。
引数はgba.hの説明にあるものから選択して使用します。
詳細はソースコードを読んでください。
void SpriteSetChr(u32 num, u32 ch) { OBJATTR* sp = (OBJATTR*)OAM + num; sp->attr2 &= 0xfc00; sp->attr2 |= ch; }
スプライトで使用するキャラクタ番号を指定します。
このほかにもいろいろな関数を用意できますが、今回はこれだけを使います。
関数名 | 用途 |
SpriteInit | 初期化 |
SpriteMove | 移動 |
SpriteSetSize | サイズの設定 |
SpriteSetChr | キャラクタ番号の設定 |
#include "lib/gba.h" #include "res.h" //--------------------------------------------------------------------------- void WaitForVsync(void) { while(*(vu16*)0x4000006 >= 160) {}; while(*(vu16*)0x4000006 < 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); } //--------------------------------------------------------------------------- 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 | (160); sp->attr1 |= size | (240); } //--------------------------------------------------------------------------- void SpriteSetChr(u32 num, u32 ch) { OBJATTR* sp = (OBJATTR*)OAM + num; sp->attr2 &= 0xfc00; sp->attr2 |= ch; } //--------------------------------------------------------------------------- void SpriteInit(void) { u32 i; for(i=0; i<128; i++) { SpriteMove(i, 240, 160); } } //--------------------------------------------------------------------------- int main(void) { // モード設定 SetMode(MODE_0 | OBJ_ENABLE | OBJ_1D_MAP); u16* oam = OBJ_BASE_ADR; // キャラクタデータ u16* pal = OBJ_COLORS; // パレットデータ u32 i; for(i=0; i<sprTilesLen/2; i++) { oam[i] = sprTiles[i]; } for(i=0; i<16; i++) { pal[i] = sprPal[i]; } SpriteInit(); // !(ビックリマーク)の表示 SpriteSetSize(0, OBJ_SIZE(Sprite_8x8), OBJ_SQUARE, OBJ_16_COLOR); SpriteSetChr (0, 1); SpriteMove (0, 20, 20); u32 x = 40; u32 y = 40; // 円の表示 SpriteSetSize(1, OBJ_SIZE(Sprite_32x32), OBJ_SQUARE, OBJ_16_COLOR); SpriteSetChr (1, 224); SpriteMove (1, x, y); for(;;) { WaitForVsync(); if( !(REG_KEYINPUT & KEY_UP) ) y--; if( !(REG_KEYINPUT & KEY_DOWN) ) y++; if( !(REG_KEYINPUT & KEY_LEFT) ) x--; if( !(REG_KEYINPUT & KEY_RIGHT)) x++; SpriteMove (1, x, y); } }
SetMode(MODE_0 | OBJ_ENABLE | OBJ_1D_MAP);
スプライトを使用するにはOBJ_ENABLEを入れます。
OBJ_2D_MAPは、OBJ_1D_MAPとの2択で、取り出し方が1次元か2次元か指定します。
// !(ビックリマーク)の表示 SpriteSetSize(0, OBJ_SIZE(Sprite_8x8), OBJ_SQUARE, OBJ_16_COLOR); SpriteSetChr (0, 1); SpriteMove (0, 20, 20);
引数の1番目はスプライト番号を表しています。ビットマップモードの場合、
メモリマップが変わる為、先頭番号が512番目からになることは忘れないでください。
#ref(): File not found: "clip_8.png" at page "tutorial.7"
#ref(): File not found: "clip_9.png" at page "tutorial.7"
キー入力できます。適当に動かしてみてください。