GB ゲーム開発覚え書き: バックグラウンドを設定する1
Game, C, GameBoy前回のシリーズではスプライトを動かしたので、今回はバックグラウンドレイヤーを学ぶ。
今回も実装を C に甘えつつ、ASM で書く時も参考にできるようなレイヤーから学んでいく。
バッググラウンドレイヤーについて
バックグラウンドレイヤーは、その名の通り背景にあたる部分。
1 マスには 8 ビット(1 バイト)の「タイルデータの参照」が含まれる。
バックグラウンドのタイルデータ
タイルデータは、一部スプライトと共有される部分がある。
まず、タイルデータ自体は $8000–$87FF
, $8800–$8FFF
, $9000–$97FF
の 3 ブロックある。
スプライトに使えるタイルデータは $8000–8800
の 0〜127 番、 $8800–$8FFF
の 128〜255 番の範囲。$9000
以降は使えない。
一方で、バックグラウンドレイヤーは、$9000
スタート。そして、符号付きなので、$8800–$8FFF
に -128〜-1,$9000–$97FF
に 0〜127 が入る。
(LCDC の bit 4 を 1 にすると、アドレス範囲がスプライトと同じになる)
タイルマップの場所
タイルマップは $9800-$9BFF
にある。
(LCDC の bit 3 または 6 が 1 だと 9C00-9FFF
になる)
タイルマップの各データ(1 マス)は 8 ビットで、タイルデータの番号が入る。
GBC のゲームのみ追加の 8 ビットがある。詳細は後述。
GBTD でタイルデータを作る
まず、スプライト同様タイルデータの作成から始める。
以前の記事で「GBTD でタイルデータを作る」 を書いたので、ここは省略。
空白のタイル 0 番と、ブロックのタイル 1 番を描いてエクスポートする。
ファイル名は "Backgrounds.c" でラベルは "Backgrounds" とする。
出力は以下のコード。
src/Backgrounds.c
const unsigned char Backgrounds[] =
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0(空白)
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFD,0x83,0xFD,0x83,0xFD,0x83, // 1(ブロック)
0xFD,0x83,0xFD,0x83,0x81,0xFF,0xFF,0xFF
};
前回書き忘れていたこととして、 1 タイルデータの情報は 16 バイトで構成される。
タイルマップは、この 16 バイトを 1 グループとした 1 バイトの情報を持てる。つまり、上記の配列をタイルマップに当てはめると Backgrounds[0〜15]
が空白の部分 0x00
として、Backgrounds[16〜31]
がブロックの部分 0x01
として使えることになる。
GBMB でタイルマップの作成
GBMB は GBTD で作成したタイルデータをインポートして、タイルマップを作成できるツール。
まず、起動したら "File" を選択し、"Map properties" を選ぶ。
"Map properties" ウィンドウが開く。
"Size" フィールドの "Width" に 20 を、"Height" に 18 を指定。
"Tileset" の "Filename" には、GBTD で作成した .gbr ファイルを指定する。
すると、先ほど読み込んだタイルデータが右のパレットに読み込まれる。
1 番タイルを選び、右ドラッグでマップを書く。
GBMB のエクスポート
GBTD 同様エクスポートが必要。
"File" から "Export to" を選ぶと、"Export Options" 画面が表示される。
"Standard" と "Location format" の 2 つのタブがある。
GBMB のエクスポートは GBTD と同じく、配列で出力される。ただし、今回は各ドットの情報ではなく、各マスのタイル番号「など」が格納される関係で、色々と知っておかないといけない。
エクスポートの設定: Standard
ここでの設定は、 GBTD のエクスポートとほぼ同じ。
"File" フィールドには、GBTD と同じくファイル名と、"Type" に "GBDK C file" を選択する。
"Settings" フィールドの "Label" も StageMap へ。"Bank" は 0 のまま。
"Split data" を使って、マップデータを分割できるけども、今回はいじらない。
エクスポートの設定: Location format
続いて、"Location format" タブに移動する。
ここでプロパティを 1 つ以上設定しないと、エクスポートできない。
"Location format" については現存する資料がほとんどなくて、何もわからなかった。
本体のコードと、WarioCraft というサンプル ROM のコメントを読んでも何もわからなかったので、できる限りのことをわかるつもり。
プロパティの設定
まず、左カラムの番号付きリストは、プロパティとビット数を指定するところになる。
プルダウンメニューから、"[Tile number]" を指定する。
隣の欄に表示される "7" はビット数。今回はこのまま。
プロパティの追加ができるけども、やらなくて OK。
ふいんきだけで触るのもいけないので、どんなプロパティがあるのかを知っておく。
プロパティ名 |
---|
Tile number |
Tile number: Low 8 |
Tile number: High 9 |
Vertical flip |
Horizontal flip |
GBC pallete |
SGB pallete |
GBC BG Attribute |
0 filter |
1 filter |
そもそも、「プロパティ」がなんなのかわからない。
そこで、GBMB が出力できるデータは、タイルの番号だけじゃないことを知る必要がある。
例えば GBC では BG Map Attributes という追加の 8 ビットを持つ。ここにはカラーパレットや、反転(GBC ではタイルの反転ができる)などの情報が含まれている。
それらをコードに含めるには、パレットの情報とかも持つ必要があり、それらの情報を出力に含めるための設定が「プロパティ」になる。
今回出力に含めたいのは「タイルの番号」なので、"Tile number" を指定すればいい。
では "Tile number: Low 8" と "High 9" はなんなのか。
それは「8 ビット以下か、9 ビット以上か」を示しているらしい。
先程の BG Map Attributes は 9 ビット以上に属するものなので、タイルのデータが 9 ビット以上でエクスポートされるといけない。
また、今回指定する "Tile number" は 7 ビットなので、例えば GBC のカラーパレット用に必要なプロパティ "GBC Palette" を追加すると、8 ビット目にデータが食い込んでしまう。
そもそもなんで "Tile number" が 7 ビットなのか、それはわからなかったので宿題にする。
あとは、"High 9" をバンクセレクタとして使用することもあるらしい。
GBC は 512 のタイルデータを持てるけど、実際は GB との互換性を保つために 256 + 256 のバンク切り替え方式になっている。
BG Map Attributes に、「どっちのバンクか」の情報が入っているので、"High 9" をバンクセレクタとして使用し、"Low 8" をタイル番号として使用することで、バンク 0,1 のタイル番号を指定できる。GBMB だと 256 番目以降のタイルは bit 9 が 1 になるらしい(実際にやってみないと何もわからない)。
最後の方のプロパティ "0 filter" はマップを "0x00" で埋め、"1 filter" は "0x01" で埋める。これは何に使うのかわかっていない…。
Map Layout, Plane の設定
次に、右カラムの設定をする。
ここでは "Plane Count" を "1 Plane(8 bits)" に変えるだけ。
ここもふいんきだけで触ってはいけないので、それぞれの役割を見てみる。
項目名 | 説明 |
---|---|
Map layout | Row は横方向、Cols は縦方向に 1 マスずつ配列に入れる |
Plane count | 1 マスの領域。8 ビットで 1 Plane とされる |
Plane order | Plane を 1 つの配列にまとめるか、分割して出力するか。 |
Tile offset | ここに設定した数、実際のタイル番号にプラスして出力される |
Map layout は Row のままで OK。横スクロールゲームなど、追加のマップデータを 1〜列ずつ書き換える時に、Cols 単位でタイルマップを分割しておくと便利なことがある。
Plane を分割できるのは BG Map Attributes のためだと思う。
BG Map Attributes は GB にはないので、互換性のため VRAM バンク 1 に入る。
「1 マスごとのタイル番号を格納した配列」と「1 マスごとの属性を格納した配列」の 2 つをそれぞれ用意しておいて、バックグラウンドの実装時にそれぞれ使用する、という事情があるのだと思う。
今回は GB のゲームなので、1 マスごとには「タイルの番号」という 8 ビットの情報があればいい。そのため "Plane count" は "1 Plane(8 bits)" にする。
また、Plane は 1 つなので "Plane order" の設定を変える必要はない。
"Tile offset" は 0 のままで OK。
エクスポートして確認
この状態で "OK" ボタンを押すと、StageMap.c が出力される。
コードを見て、ただしくマップされているか確認してみる。
src/StageMap.c
#define StageMapWidth 20
#define StageMapHeight 18
#define StageMapBank 0
const unsigned char StageMap[] =
{
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,
0x00,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01
};
先程説明したように GB の 1 マスは 1 バイトなので、この配列は 20×18=360 バイトを持つ。
0x00
が空白、0x01
がブロック部分。
実際に表示される幅に合わせて 20 ずつで区切ってみれば、わかりやすい。
GBMB で作成したマップの画像と比べれば、正しくマッピングされていることがわかると思う。
src/StageMap.c
#define StageMapWidth 20
#define StageMapHeight 18
#define StageMapBank 0
// 0x01 はブロック、0x00 は空白
const unsigned char StageMap[] =
{
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01
};
続く
その2 に続く