#author("2023-05-15T09:50:22+09:00;2023-04-24T18:46:53+09:00","","")
* スプライト [#le1275f0]
スプライトとは8x8~64x64サイズの画像を最大128個まで表示します。シューティングゲームでいう自機や弾、RPGでいう主人公キャラなどに使います。今回登場するメモリ領域は以下のとおりです。なお、図のとおりキャラクタデータはタイルモードとビットマップモードで''位置が変わり、格納サイズも変わる''という点に注意してください。早速、パレットデータ、キャラクタデータ、属性(Object Attribute Memory)の3つをそれぞれ見ていきましょう。

| 名前                                               | アドレス     |
| パレットデータ(256色1パレット or 16色16パレット) | 0x5000200~  |
| キャラクタデータ(タイルモード時)                 | 0x6010000~  |
| キャラクタデータ(ビットマップモード時)           | 0x6014000~  |
| 属性(Object Attribute Memory)                      | 0x7000000~  |

#ref(1.png,nolink)

** パレットデータ [#fc633cc4]
データフォーマットはmode3で使った色データと同じです。データ構造はそのままに、16色をパレット16個か、256色をパレット1個で指定することも可能です。

- 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

** キャラクタデータ [#rdc6f6b1]
キャラクタデータは、パレット番号を指定した集まりです。単位は1キャラクタを8x8ドットとしています。16色の場合、4bitでちょうど16色指定できるのでピッタリのサイズですね。256色も0x0~0xffまでなのでデータを無駄にすることなく使うことができます。

|  色  | ドット単位の格納方法                          | サイズ |
|  16  | 横2ドット=1byte(下位4bitが右、上位4bitが左) | 32byte |
| 256  | 横1ドット=1byte                               | 64byte |

** gritの出力 [#fb6a18f7]
16色のパレットデータとキャラクタデータの出力例を以下に表します。

#ref(2.png,nolink)

 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

** キャラクタデータの補足 [#t400ad4d]
単位は1キャラクタを8x8ドットと先ほど説明していました。たとえば以下の256x128ドットのキャラクタデータがあったとしましょう。

#ref(3.png,nolink)

このとき、キャラクタ番号は0番は空白、0番!、1番”、2番#、というようになります。8x8単位のみを扱うのでしたらこのキャラクタ番号を指定し、表示するだけで話は終わります。ところが8x8単位以上のものを表示したい場合、格納方法に工夫がいります。格納が1次元か2次元か、ということです。例として文字列の下にある球体を32x32ドットで表示したい場合を考えてみます。~

この球体の番号は96です。1次元で32x32を取り出した場合のキャラクタ番号は96, 97, 98, 99, 100, 101, 102・・・111となります。結果は以下の通り。

#ref(4.png,nolink)

円がきれいに表示されませんでした。では番号を224に変えてみましょう。

#ref(5.png,nolink)

うまく行きました。この方法が1次元的取り出し方法です。データが切れ目なく連続しているため、キャラクタデータを入れ替えるときにとても便利です。個人的には後に教える2次元方法より良いと思っています。シューティングゲームなどでは頻繁にキャラクタデータの変更を行います。ボス戦なら画面いっぱいにキャラクタデータを使うので今のうちに1次元に慣れておくのがいいでしょう。次に2次元的取り出し方ですが、これは番号96にした場合の取り出し番号が96, 97, 98, 99, 128, 129, 130, 131, 160, 161, 162, 163となります。VBAのデバッグ画面や画像を見る分にはわかりやすいのでキャラクタデータはこれ1本という時にいいでしょう。取り出す時の形状には、以下のようなサイズがあります。

 // スプライトの指定サイズ
 
 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
 };

** 属性(Object Attribute Memory) [#a58b52c1]
スプライトは1つ1つに対して、属性という単位で取り扱います。スプライトは最大128個ですので属性も128個あると思ってください。属性1つ分にはattr0, attr1, attr2, dummyという単位で構成されています。いわゆるC言語でいう構造体です。様々なパラメータが存在しており、いきなり覚えようというのは難しいと思います。今は軽く読み流す程度で結構です。

 typedef struct {
 	u16 attr0;
 	u16 attr1;
 	u16 attr2;
 	u16 dummy;
 } OBJATTR;

#ref(6.png,nolink)

- 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(7.png,nolink)

** スプライト操作の汎用関数 [#bfa170c7]
こちらで用意した関数を通して簡単な(大雑把な)扱いを知ってください。

| 関数名        | 用途                 |
| SpriteInit    | 初期化               |
| SpriteMove    | 移動                 |
| SpriteSetSize | サイズの設定         |
| SpriteSetChr  | キャラクタ番号の設定 |

- SpriteInit
 void SpriteInit(void)
 {
 	u32 i;
 	for(i=0; i<128; i++)
 	{
 		SpriteMove(i, 240, 160);
 	}
 }

すべてのスプライトを画面外に移動させます。非表示にするといった機能はありますが、念のため必ず初期化時に実行します。

- SpriteMove
 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といった中途半端な表示にも対応しているわけです。

- SpriteSetSize
 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;
 }

スプライトの形状と色数を変更します。引数はgba.hの説明にあるものから選択して使用します。詳細はソースコードを読んでください。

- SpriteSetChr
 void SpriteSetChr(u32 num, u32 ch)
 {
 	OBJATTR* sp = (OBJATTR*)OAM + num;
 
 	sp->attr2 &= 0xfc00;
 	sp->attr2 |= ch;
 }

スプライトで使用するキャラクタ番号を指定します。このほかにもいろいろな関数を用意できますが、今回はこれだけを使います。

** スプライトの表示例 [#h37e14bc]

 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);

*** 動作画面とデバッグ画面 [#m9a1ff76]
#ref(8.png,nolink)
#ref(9.png,nolink)

** 履歴 [#v77dd331]
- 2023/04/24
- 2014/12/23

トップ   一覧 検索 最終更新   ヘルプ   最終更新のRSS