VGS-ZeroからGBAへの移植が捗る

# はじめに

ここ半年間ぐらいVGS-Zeroで開発していたゲーム（Battle AirForce）が完成したので、このゲームをゲームボーイアドバンス（GBA）へ移植する試みを始めてみました。

https://store.steampowered.com/app/3476490/Battle_AirForce/

GBA版の開発状況はTwitterで呟いています。

https://x.com/suzukiplan/status/1900304704934134163

VGS-ZeroのVDPはGBAを一切参考せずに創ったのですが、偶然にもVGS-ZeroとGBAは仕様の共通点が多くあります。

上記のツイートでも言及していますが、開発したゲームのGBA版をスイッチオンラインで無料配信して、VGS-Zero版をMy Nintendo Storeで販売するスタイルが確立できれば面白いかもと思っています。

> スイッチオンライン（有料サブスクリプション）の会員数は2024年時点で3,400万人ほどなので、開発者はコスパの良いプロモーションができ、会員は無料で遊べるゲームが増え、任天堂は会員数の維持や増加を見込むことができるWin-Win-Winの関係が構築できれば理想的。

もちろん、任天堂に話は通してません。 

まずは私が BAF の GBA版 が完成次第、企画を任天堂に持ち込んでみようと考えているところです。

本書では、技術的な観点で何故VGS-ZeroからGBAへの移植が容易なのかを紹介します。

# GBA の仕様（かなりザックリ）

詳細はこちらで確認できます。

https://problemkaputt.de/gbatek.htm

まず、GBAには6つの画面モードがありますが VGS-Zero の VDP と仕様が近いのは mode0 なので、mode0 に絞って説明します。

mode0 の仕様はざっくり次の通りです:

|項目|VGS-Zero|GBA (mode0)|
|:-|:-:|:-:|
|描画方式|キャラクタパターン方式|（同左）|
|解像度|240x192|240x160|
|表示色数|16色x16パレット|16色x16パレット<br>or<br>256色|
|色仕様|RGB555|（同左）|
|基本パターンサイズ|8x8|（同左）|
|パターン数|256 or DPM|1024|
|レイヤー数|2面<br>(BG+FG)|4面<br>(BG0〜3）|
|nametable (map)|32x32|32x32 or 64x64|
|スプライト数|256|128|
|スプライトサイズ|8x8〜128x128|8x8〜64x64|
|特殊効果|n/a|拡大・回転・モザイク|

どちらにも一長一短があるものの、トンチを効かせればなんとかなりそうに見えます。

16色16パレットモードを用いれば、**画像類は一切加工することなくそのままVGS-ZeroからGBAに持っていける** メリットがかなり大きいので、VGS-Zeroからの移植であればmode0縛りにすることをオススメします。

トンチが必要そうなポイントとしては、以下のような項目があります。

- 解像度が縦32px狭くなるためUI等のデザイン調整が必要
- VGS-ZeroのDPM相当の機能が無いためパターンのやりくりが必要
- VRAM更新はBLANK期間（※）にしか出来ない（VGS-Zeroはいつでもできる）のでVRAM更新タイミングについての処理シーケンスの調整が必要になる可能性がある

:::note info
BLANK期間は H-BLANK (水平ブランク) と V-BLANK (垂直ブランク) があり、H-BLANK については DISPCNT レジスタの bit-5 を set にすることで OAM へのアクセスができますが、基本的には V-BLANK 期間が VRAM へアクセスできるタイミングになるという考え方で良いと思われます。

GBA のスキャンラインは内部的には 228 lines なので **68 lines が V-BLANK 期間** となるようです。（以下、gbatekから抜粋）

```
  Visible (*) 160 lines, 11.749 ms, 197120 cycles - 70% of v-time
  V-Blanking   68 lines,  4.994 ms,  83776 cycles - 30% of v-time
  Total       228 lines, 16.743 ms, 280896 cycles - ca. 59.737 Hz
```

キャラクタの座標などは基本的に WRAM で保持しておき、Visible期間にそれらの更新を行い、V-Blanking で WRAM から VRAM へ値を反映させるようなシーケンスで処理を実装することが妥当だと考えられます。
:::

あとは、グラフィック関係以外ではBGM関係が中々悩ましいです。

> GBAにはGB相当のサウンド（矩形波2ch+波形メモリ1ch+ノイズ1ch）と2系統のPCMがあり、公式SDKのサウンドドライバ（mp2kやm4a）を用いればPCMを用いたソフトシンセ(?)の多チャンネル演奏もできるようですが、それを使うと当然重くなります。（個人的にはBGMはGB音源、効果音はPCMという形態が一番無難かなと）

入力系統（ゲームパッド）については何も考えずにそのまま移植できます。

> GBAのゲームパッドはVGS-Zeroの上位互換（LRボタンの有無以外は同じ）なため。

# 移植が捗るSDK (WIP)

VGS-Zero からのゲーム移植に適した SDK (devkitPro用) を開発中です。

https://github.com/suzukiplan/gbasdk/

> BAFをGBAへ移植する過程で見つけた必要な機能をどんどん追加していこうと思っているところです。

上記SDKには便利なツールセットも含まれていて、`bmp2pal` で 256色 Bitmap から GBA 用のパレットデータを作成でき、`bmp2chr` で縦横が8の倍数の 256色 or 16色 Bitmap から GBA 用のパターンデータを作成することができます。

これらのツールセットだけでもGBA対応アプリの開発が結構楽になるかもしれません。

`Hello, World!` を表示する example は次のような実装になっています。

```c
#include "vdp.h"
#include "palette_dat.h"
#include "font_dat.h"

int main(void)
{
    vdp_init();                                 // VDPを初期化
    vdp_force_vblank(ON);                       // いつでもVRAM更新できる状態にする（※画面が真っ白になるので注意）
    vdp_palette_init(palette_dat);              // 16x16パレットを初期化
    vdp_set_tile(0, font_dat, font_dat_size);   // フォント画像をVRAM（パターン番号0〜255）へ読み込む
    vdp_cls();                                  // 画面をクリア
    vdp_print_bg(0, 9, 10, "HELLO,WORLD!");     // BG0 の (9,10) の位置に HELLO,WORLD! を表示
    vdp_force_vblank(OFF);                      // VBLANK時のみ画面更新できる状態にする
    while (ON) { vdp_wait_vblank(); }           // VBLANK待ちを繰り返す無限ループ
}
```

上記の実行結果は次の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2161417/3d61b462-bfa8-40b1-a7fd-dd3c1d341125.png)

この SDK を利用するには前提条件として devkitPro が必要ですが、そのインストールについては以下の記事がとても参考になりました。（macOS勢向け）

https://qiita.com/yamakentoc/items/3e268c16d1334cddc54b