東方老桜夢の技術解説 枠付きスクロールについて
東方老桜夢では、弾幕密度を上げるためにゲーム画面を半分サイズにし、 左側にゲーム画面、右側にスコアなどの表示を行っています。
ファミコンはBG面を1つしか持たないため、ハードウェアの機能では
枠が付いた状態で1ドット単位で背景がスクロールする画面を
作ることはできません。
ソフトウェアで工夫することで実現しています。
仕組み
基本的な仕組みは、ネームテーブル書き換えのソフトウェアスクロール と CHRバンク切り替えによる1ドットスクロール での補間です。
ネームテーブル書き換えのソフトウェアスクロールとは、
MSX1などのレトロPCでよくみる画面書き換えによる8ドット単位のスクロールです。
東方老桜夢では、スクロールする背景部分のネームテーブルを全面書き換えています。
ファミコンでは、1フレーム辺りのVRAMへのデータ転送量は十分にはありません。
そこで、画面を2つ用意し、片方(表面)を表示している間、もう片方(裏面)を数フレームかけて書き換え、
表面と裏面を入れ替える方法を採用しています。
いわゆるダブルバッファ法です。
CHRバンク切り替えによる1ドットスクロールとは、
1ドットずつずれたキャラクタパターンを8つのCHRバンクに格納し、
CHRバンクを順番に切り替えることで、見かけ上1ドット単位で
スクロールしているように見せる手法です。
画面がスクロールするアニメーションを見せていると言ってもよいでしょう、
スクロールする方向は、生成するキャラクタパターンのずれの方向で決まります。
必要であれば、同じ画面内で異なる方向へスクロールすることも可能です。
(5面道中や、妖夢、藍のスペカ背景など)
(1~8 はCHRバンクを順番に切り替えて1ドットずつずれた絵を出す。
9で表面と裏面を切り替えてネームテーブルを書き換えた絵を出す、CHRバンクも最初のバンクに戻す。)
レイヤー
スクロールする背景部分は、仮想的に4階層のレイヤーを持たせています。
各レイヤーは表示/非表示を設定でき、表示状態で最も優先度が高いレイヤーが
VRAMに転送する画面となります。
レイヤーの内訳は次の通りです。
優先度 | VRAM転送元データの場所 | 用途 | |
---|---|---|---|
レイヤー3 | 高 | WRAM | 自機ボム表示 |
レイヤー2 | WRAM | 敵スペカの弾丸表示など | |
レイヤー1 | PRG-ROM | 敵スペカの背景 | |
レイヤー0 | 低 | PRG-ROM | 通常の背景 |
レイヤー0とレイヤー1は、自機や敵キャラの位置に依存しない背景であるため、
決められた背景データをPRG-ROMに配置し、シーケンスデータに従って
VRAMに転送します。
一方、レイヤー2とレイヤー3は、自機や敵キャラの依存する背景のため、
WRAMに設けられた仮想VRAMに動的にデータを作成して、
そのデータをVRAMに転送します。
ネームテーブルの書き換えの詳細
スクロールする背景部分のネームテーブルの書き換えは、
8フレームを1単位として処理します。
これは、1フレームで1ドットスクロールするとして、
8フレームで1キャラ分(8ドット)ずれて表面と裏面が入れ替わることを
想定したためです。
スクロールする背景部分のネームテーブルの書き換えの処理は、 PRG-ROMのデータを転送する場合とWRAMの仮想VRAMから転送する場合で 違いがあります。
PRG-ROMからの転送
1単位8フレーム中の各処理は次の通りです。
フレーム | 処理 |
---|---|
0フレーム目 | 転送なし |
1フレーム目 | 画面の1/6を転送(16キャラ×4ライン) |
2フレーム目 | 画面の1/6を転送(16キャラ×4ライン) |
3フレーム目 | 画面の1/6を転送(16キャラ×4ライン) |
4フレーム目 | 画面の1/6を転送(16キャラ×4ライン) |
5フレーム目 | 画面の1/6を転送(16キャラ×4ライン) |
6フレーム目 | 画面の1/6を転送(16キャラ×4ライン) |
7フレーム目 | アトリビュート領域を転送、表面と裏面の入れ替え |
1~6フレーム目では、1フレーム辺りのVRAMへの最大転送量の半分を使って
裏面のスクロールする背景部分を書き換えます。
VRAMへの最大転送量の残り半分は、スコアなどの書き換えに使用します。
7フレーム目では、アトリビュート領域(BGのパレット指定データ)と
表面と裏面の入れ替えを同フレームで行っています。
これは、とある理由でアトリビュート領域は表面裏面で共通となっているため、
表面と裏面を入れ替えるタイミングでアトリビュート領域を更新しなければ
ならないためです。
1フレームで1ドットずれるスクロールのスピードは演出的には速く見えるため、
実際には32フレームや64フレームなどの2のn乗倍遅いスピードで
使用しています。
この時、ネームテーブル書き換えの処理は、表面と裏面を入れ替える
8フレーム前から開始して、それより前のフレームでは転送処理を休止しています。
WRAMからの転送
1単位8フレーム中の各処理は次の通りです。
フレーム | 処理 |
---|---|
0フレーム目 | 画面の1/3を転送(16キャラ×8ライン) |
1フレーム目 | 画面の1/3を転送(16キャラ×8ライン) |
2フレーム目 | 画面の1/3を転送(16キャラ×8ライン) |
3フレーム目 | アトリビュート領域の転送 (表示するレイヤー切り替え後、1回のみ) 表面と裏面の入れ替え |
4フレーム目 | 画面の1/3を転送(16キャラ×8ライン) |
5フレーム目 | 画面の1/3を転送(16キャラ×8ライン) |
6フレーム目 | 画面の1/3を転送(16キャラ×8ライン) |
7フレーム目 | アトリビュート領域の転送 (表示するレイヤー切り替え後、1回のみ) 表面と裏面の入れ替え |
WRAMからの転送では、仮想VRAMに動的に作成したデータを いち早く画面に反映させるため、4フレームで画面を書き換え、 8フレーム中2回表面と裏面を入れ替えています。
0~2、4~6フレーム目では、1フレーム辺りのVRAMへの最大転送量を全て使って
裏面のスクロールする背景部分を書き換えます。
3、7フレーム目では、表示するレイヤーを切り替えた後1回だけ
アトリビュート領域を転送します。
アトリビュート領域の転送を行った以降のフレームでは、
スコアなどの書き換えを行います。
WRAMの仮想VRAMは、処理落ちが発生していなければ、更新されているため、 VRAMへの転送処理も常時稼働する動きとなります。