Tutorial.7 スプライト(1)

スプライト

スプライトとは8x8〜64x64サイズの画像を最大128個まで表示し、
シューティングゲームでいう自機や弾、RPGでいう主人公キャラなどに使います。

今回登場する配置領域、及びデータ構造は以下のとおりです。

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

キャラクタデータはタイルモードとビットマップモードでは位置が変わるので注意してください。
早速、パレットデータ、キャラクタデータ、属性(Object Attribute Memory)の3つをそれぞれ見ていきましょう。

パレットデータ

データフォーマットは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ドット=1byte64byte

16色の場合、4bitでちょうど16色指定できるのでピッタリのサイズですね。
256色も0x0〜0xffまでなのでデータを無駄にすることなく使うことができます。

キャラクタデータの格納

たとえば以下のキャラクタデータがあったとしましょう。

  • 256x128
    clip_2.png

このとき、キャラクタ番号は

空白 - 0
!    - 1
"    - 2
#    - 3

(以下略・・・)

となります。8x8単位のみを扱うのでしたらこのキャラクタ番号を指定し、
表示するだけで話は終わります。ところが8x8単位以上のものを表示したい場合、
格納方法に工夫がいります。

clip_3.png

こちらは先ほどの画像を2倍表示して8x8単位に点線を加えたものです。
取り出し方法には1次元、2次元があります。仮に32x32で、1次元の取り出しを
キャラクタ番号96から行うと、次のような結果になります。

clip_4.png

96, 97, 98・・・と取り出した為、円がきれいに表示されませんでした。
では224に変えてみましょう。

clip_5.png

うまく行きました。
個人的には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
};

属性(Object Attribute Memory)

スプライト1つ1つに対して、属性という単位で取り扱います。
構造体を表すと次のようになりますが、attr0〜2までの中には
様々なパラメータが存在して、いきなり覚えようというのは難しいと思います。

今は軽く読み流す程度で結構です。

typedef struct {
	u16 attr0;
	u16 attr1;
	u16 attr2;
	u16 dummy;
} OBJATTR;
clip_6.gif
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)
clip_7.gif

スプライト操作の汎用関数

こちらで用意した関数を通して簡単な(大雑把な)扱いを知ってください。

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_1D_MAPは、OBJ_2D_MAPとの2択で、取り出し方が1次元か2次元か指定します。

// !(ビックリマーク)の表示
SpriteSetSize(0, OBJ_SIZE(Sprite_8x8), OBJ_SQUARE, OBJ_16_COLOR);
SpriteSetChr (0, 1);
SpriteMove   (0, 20, 20);

引数の1番目はスプライト番号を表しています。ビットマップモードの場合、
メモリマップが変わる為、先頭番号が512番目からになることは忘れないでください。

動作画面とデバッグ画面

clip_8.png
clip_9.png

キー入力できます。適当に動かしてみてください。

履歴

  • 2014/12/23

添付ファイル: fileclip_9.png 231件 [詳細] fileclip_8.png 219件 [詳細] fileclip_7.gif 231件 [詳細] fileclip_6.gif 222件 [詳細] fileclip_5.png 225件 [詳細] fileclip_4.png 234件 [詳細] fileclip_3.png 238件 [詳細] fileclip_2.png 220件 [詳細] fileclip_1.gif 229件 [詳細]

Last-modified: 2014-12-25 (木) 20:08:20 (2591d)