スプライト(オブジェクト) |
![]() |
背景の上に複数のオブジェクトを描く例です。このプログラム例では、オブジェクト(りんご)を動かしていませんが、オブジェクトは専用のメモリ領域を持っていて、背景とは独立に描画されるため(要するにハードウエア処理)、非常に高速に動かすことができます。動かす方法は、各自で工夫してください(ヒント:動く物体は、座標と速度で表される)。オブジェクトの画像を作成するノウハウもここで説明します。 |
/****************************************************************/ /* Sprite */ /****************************************************************/ #include "gba.h" #include "applobj2.c" #include "sky.c" void sp_attribute(int, int, int, int); void sp_disp(int, int); void sp_move(int, int, int); void sp_rotate(int, hword); void init_sprite(void); void init_screen(void); |
sky.c は、キー入力と複数画面合成のページで説明したものと同じです。 | hword* objcolor = (hword*) OBJ_PALETTE; // Object palette address hword* bcolor = (hword*) BG_PALETTE; // Background palette address hword* objdata = (hword*) OBJ_DATA0; // Object character data address // Screen structures screen plane = { (hword*) VRAM_MAP(24), (hword*) VRAM_TILE(0), 0, 0 }; |
オブジェクトパレット、背景パレット、VRAMのオブジェクトデータ領域のポインタを設定します。背景画面は1枚だけなので、screen 構造体は、1個だけ宣言します。 | int main() { int chnum; |
スプライトの機能は、VRAM領域に記録されたキャラクタデータを、先頭アドレス 0x07000000のObject Attribute Memory (OAM) に格納される座標にマッピングすることで使用できます。chnum は、各キャラクタデータに割り振られたキャラクタ番号です。キャラクタ番号は、キャラクタのサイズに応じて割り当てられるので、キャラクタ番号の割り当てについては、ハードウエアの概要のページで確認してください。ここでは、キャラクタは、1種類だけですので、キャラクタ番号も0番のみを使用します。 | // Initialize init_screen(); init_sprite(); |
init_screen 関数は、背景データの設定と、背景コントロールレジスタ、LCDコントロールレジスタの設定を行います。init_sprite 関数は、オブジェクトパレット、キャラクタデータ、オブジェクトの属性設定、オブジェクトスクリーンに関するLCDコントロールレジスタの設定を行います。 | // OAM chnum = 0; sp_attribute(0, OBJ_16x16, PRIORITY_1, chnum); chnum = 0; sp_attribute(1, OBJ_16x16, PRIORITY_1, chnum); chnum = 0; sp_attribute(2, OBJ_16x16, PRIORITY_1, chnum); chnum = 0; sp_attribute(3, OBJ_16x16, PRIORITY_1, chnum); chnum = 0; sp_attribute(4, OBJ_16x16, PRIORITY_1, chnum); chnum = 0; sp_attribute(5, OBJ_16x16, PRIORITY_1, chnum); |
キャラクタ番号を chnum で指定。sp_attribute 関数は、画面上に呼び出したオブジェクトを見分けるオブジェクト番号、オブジェクトのサイズ(16x16pix)、優先度(最高)、キャラクタ番号(0)を指定しています。このプログラムでは、キャラクタ変数の変更などは行わないので、わざわざキャラクタ番号を変数に代入する必要はありませんが、オブジェクトの制御が複雑な場合には、オブジェクト番号やキャラクタ番号を変数にしておくと便利です。 | // Object location sp_move(0, 20, 20); sp_move(1, 100, 80); sp_move(2, 220, 100); sp_move(3, 130, 140); sp_move(4, 180, 60); sp_move(5, 40, 100); |
sp_move 関数は、各オブジェクトの座標を指定しています。オブジェクトの座標は、pix単位です。 | // Object display sp_disp(0, 1); sp_disp(1, 1); sp_disp(2, 1); sp_disp(3, 1); sp_disp(4, 1); sp_disp(5, 1); |
sp_disp 関数は、各オブジェクトの表示/非表示を切り替えます。初期設定が完了してから表示したほうが無難です。 | // Loop while (1); } |
この例では、背景とオブジェクトの表示が終わったら、他の処理はないので、そのまま無限ループに入ります。 | // Setup sprite attribute void sp_attribute(int spnum, int shape, int priority, int chnum) { int i, j; sprite* sp; sp = (sprite*) OAM; sp += spnum; sp->attr0 = OBJ_COLOR_256 + OBJ_SW_TURNOFF; sp->attr1 = 0; sp->attr2 = ((priority & 3) << 10); sp->attr2 |= (chnum & OBJ_CHAR_MASK); sp->pacd = 0; |
sprite 構造体のポインタ変数 sp は、各オブジェクト番号に対応する、OAM上のオブジェクト属性のポインタとして使用されている。attr0(属性0)には、オブジェクト色数256とオブジェクト表示ゼロが指定されている。オブジェクトの y 座標は、sp_move 関数で別途指定されるので、ここでは指定しない。attr1は、一旦全ビットゼロに初期化されています。attr2は、キャラクタ番号を指定していますが、不適当なキャラクタ番号が指定された場合でも他の値を書き換えないよう、安全のためキャラクタ番号指定ビットマスクと AND を取った上で、元の attr2 と OR をとっています。これは、安全なプログラムを作るための常套手段です。pacd は、回転・拡大パラメータですが、ここでは使用しません。 (参考)構造体のポインタ変数名からメンバにアクセスする場合には、構造体ポインタ変数->メンバ名のように指定することができます。 | i = shape / 4; sp->attr0 |= (i << 14); j = shape % 4; sp->attr1 |= (j << 14); } |
shape の値は、gba.h の中で定義された、0〜11の値を使用しています。ハードウエアの概要で説明したように、オブジェクト属性1のサイズとオブジェクト属性0の形状を別々に指定する必要があるので、上記の演算を行います。shape / 4 が形状、shape % 4 がサイズを表していることを確認してください。 | // Display ON(disp=1)/OFF(disp=0) for sprite void sp_disp(int spnum, int disp) { sprite* sp; sp = (sprite*) OAM; sp += spnum; if (disp) sp->attr0 &= ~OBJ_SW_MASK; else sp->attr0 |= OBJ_SW_TURNOFF; } |
disp = 1 のとき、 attr0(属性0)の表示ON/OFFビットを OBJ_SW_MASKのビット反転を使用して 0(表示ON)に設定しています。disp != 1 のとき、attr0(属性0)の表示ON/OFFビットを 1(表示OFF)に設定しています。 | // Move sprite void sp_move(int spnum, int x, int y) { sprite* sp; sp = (sprite*) OAM; sp += spnum; sp->attr1 &= ~OBJ_X_MASK; sp->attr1 |= (x & OBJ_X_MASK); sp->attr0 &= ~OBJ_Y_MASK; sp->attr0 |= (y & OBJ_Y_MASK); } |
一旦、attr1, attr0 のオブジェクト座標データビットを 0 に初期化して、指定された(x, y)座標を書き込んでいます。安全のため、x, y 座標のビットマスクを利用して座標以外のビットを保護しています。これをしておかないと、x, y の値が適正範囲を超えた場合に、他のビットが書き換えられてしまいます。これは、安全なプログラムを作る常套手段です。 | // Initialize sprite module void init_sprite(void) { int i; // Initialize object palettes for (i = 0; i < 256; i++) objcolor[ i ] = applobj2_pal[ i ]; |
applobj2_pal 配列から、アドレス 0x0500 0200 のオブジェクトパレットにパレットデータを書き込んでいます。 | // Initialize object data for (i = 0; i < 2 * 2 * 32; i++) // 256 color 64 Byte (8x8) objdata[ i ] = applobj2_data[ i * 2 ] | (applobj2_data[ i * 2 + 1 ] << 8); |
applobj2_data 配列から、アドレス 0x0601 0000 のオブジェクトデータ領域にキャラクタデータを書き込んでいます。但し、objdata は、16 bit 変数ポインタであり、applobj2_data は、8 bit の配列変数なので、一旦、applobj2_data 配列要素から、16 bit 変数に直してから、代入を行っています。 | // Initialize OAM for (i = 0; i < 128; i++) sp_attribute(i, OBJ_16x16, 0, 0); |
OAMの全領域(オブジェクト番号 0〜127)を、取りあえず 16x16pix のサイズで初期化しています。 | // Turn on OBJ screen! gba_reg(LCD_CTL) |= LCD_OBJ | LCD_OBJ_MAP1D; } |
LCDコントロールレジスタに、オブジェクトを使用可能にするようORで追加指定。 | // Screen Initialization void init_screen(void) { int i; // Initialize palettes for (i = 0; i < 256; i++) bcolor[ i ] = sky_pal[ i ]; // Setup tiles for background picture for (i = 0; i < (32 * 20 * 32); i++) plane.tile[ i ] = sky_data[ 2*i ] + (sky_data[ 2*i + 1 ] << 8); // Setup the map for background picture for (i = 0; i < 32 * 20; i++) plane.map[ i ] = sky_map[ i ]; |
キー入力と複数画面合成のページと同様、タイルモード(MODE0)における背景画像の設定。 | //Setup background mode and LCD control register gba_reg(BG0_CTL) = LCD_SIZE00 | LCD_COLOR256 | LCD_BGTILE(0)| PRIORITY_2 | LCD_BGMAP(24); gba_reg(LCD_CTL) = LCD_BG0 | LCD_MODE0; } |
背景は BG0 の画面を使用。デフォルトでは、BG0 は最優先の画面になりますが、PRIORITY_2 により、優先度を下げてオブジェクトが前面に来るようにしています。gba.h の内容を確認しよう。背景の仮想画面サイズは、32 x32 タイル、色数は256色、VRAMタイル領域0番を使用、VRAMマップ領域24番を使用しています。データ領域が互いに重なっていないことを確認しよう。さらに、LCDコントロールレジスタでMODE0, BG0 の使用を指定しています。 |
const unsigned char applobj2_data[2*2*64] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, : : }; const unsigned short applobj2_pal[256] = { 0x0000,0x7BDF,0x77BF,0x739F,0x6F7E,0x6F7F,0x673F,0x5ADE, 0x35B2,0x529D,0x35B3,0x4E7D,0x35B4,0x4A5C,0x3193,0x4E7E, : : }; |
背景は、キー入力と複数画面合成のページで使用したものをそのまま使っています。オブジェクトは、背景の上に描かれるので(背景より優先度を下げると下に描くこともできます)、不要な地の色は、透明色にしておく必要があります。透明色は、パレット番号0 (配列添え字0)を黒に指定することで実現できます。オブジェクト用の画像を、これまで使用してきた、bmp2rgbでGBAのRGB形式に変換すると、パレット番号0が、黒になるとは限らず透明色が作れませんので一工夫必要です。ここでは、オブジェクト用の画像の地の色を、下の図のようにお絵かきソフトで白に塗りました。この例では、16x16pix のオブジェクト画像を使用していますので、画像を拡大して白を塗れば簡単に出来ます。それから、bmp2rgb でGBAデータに変換すると、上のパレット配列が得られます・・・うそです。applobj2_pal[0] = 0x07FF(白)となります。そこで、bmp2rgb の変換結果のファイルを開いて、0x07FF を 0x0000 に書き換えます。これで、りんごの周りの白い部分が透明色となり、背景が透過して見えるようになります。しかし、こんないい加減な 16x16pix のりんご画像を、GBAの背景の上に置くと、ちゃんと美味しそうなりんごに見えるから不思議ですね。![]() |
(c) Kanazawa Univ., 2004