29
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ゲームギアで弾幕STGを創ってみた

Last updated at Posted at 2024-05-17

はじめに

以前、VGS-Zeroというラズパイで動作する自作ゲーム機を紹介させていただきました。

今回、上記の記事でも触れているVGS-Zeroのローンチタイトル「Battle Marine」(下記)をゲームギアに移植してみました。

本書はBattle Marineをゲームギアへ移植した経緯や技術的に苦労した点などを纏めたものとなります。

なお、今回ゲームギア(実機)で動作できるソフトとしてBattle Marineを開発しましたが、セガの公式なライセンス商品ではなく、飽くまでも ゲームギアと互換性のある同人ソフト となります。

何故ゲームギア?

Battle Marine のゲームジャンルは「弾幕STG」です。

もしかすると、この点については議論の余地があるかもしれませんが、少なくとも私は弾幕STGとして創ったつもりです。

「Z80のゲーム機」としてはチート級のスペックのVDPを搭載しているVGS-Zeroならともなく、テクノロジー的には1985年頃相当のゲームギアで弾幕STGを創るというのは、中々クレイジーな話かもしれません。

狂気の沙汰ほど面白い。

私はBattle Marine(VGS-Zero版)のプロモーションの一環として今回ゲームギアへの移植を実施したので、衆目の関心を集めやすいネタであることはとても重要です。

烏滸がましい話かもしれませんが、もしも Battle Marine が 「ゲームギアにおける弾幕STGの決定版」 のようなゲームになることができれば Battle Marine (IP) にとって良いプロモーションになりそうです。

今から東方Projectや怒首領蜂のような「弾幕STGの決定版」を創るのはほぼ100%無理だと思っていますが、「ゲームギアにおける」というエクステンション付きであればわんちゃん狙えるかも...と。

もちろん、東方Projectや怒首領蜂の新作が現在ゲームギア向けに開発中ということであれば太刀打ちできませんが、多分それは無いのではないかなと。

ゲームギアのSTG事情

ゲームギアでクオリティが高いSTGとしては(私が知っている範囲では)ファンタジーゾーン、ギャラガ、GGアレスタ、グリフォンなどがあり、どれもとてもハイクオリティーなゲームだと思いますが「弾幕STGか?」と問われると :thinking:

これは、ゲームギアが「スペック的に弾幕STGを創るのが無理だった」わけではなく、シンプルに「時期の問題」だと思われます。

「弾幕STG」の人気が高まったのは1997年の怒首領蜂(アーケードゲーム)の頃からで、ゲームギアのセガ公式ライセンスソフトは1996年発売のGソニックが最後なので、弾幕STGが人気ジャンルとして認知されてから開発されたゲームギアソフトは(少なくとも公式ライセンスソフトでは)存在しないものと思われます。

なお、弾幕STGは怒首領蜂(1997年)が最初という訳ではありません。

ファミコンに「サマーカーニバル'92 烈火」(1992年)という弾幕STG(※)があり、それはゴッホの絵画のように後年になってから評価されましたが、発売当時はそれほど人気があったようには見えませんでした。

※烈火(正確なジャンルは弾幕STGではなくスーパーハードシューティングゲーム)が弾幕STGかという点については議論の余地があるかもしれませんが、烈火の続編(という説がある)バトルガレッガ(1996年)が怒首領蜂に影響を与えたという説があるので、私は烈火を「弾幕STGの源泉」と考えています。

もしも、1992年時点で烈火がヒットしていれば、ゲームギアに弾幕STGが登場していた世界線もあったかもしれません。

烈火の分析(脱線)

STGというジャンルは保守的なゲーマーに好まれると私は考えています。

これは STG に限った話ではなく、「面白いかどうかが分からない尖った作品」(革新的な作品)よりも「面白いことを知っている作品」(保守的な作品)を遊びたいと思う人の方が一般的には多いと思いますが、特に STG は 「アーケード文化」の文脈で発達したジャンルなので家庭用ではユーザーの保守的な傾向が他ジャンルよりも強い と分析しました。

インターネットが普及していなかった 1992 年当時に STG をヒットさせるには、先ずは50円〜100円で安価にプレイできるアーケードゲームとして稼働させ、そこで認知を得てから家庭用へ移植するステップを踏む必要があったと考えられます。

つまり、烈火がヒットできなかったのは「アーケードをやらなかった為」だと私は考察しました。

余談ですが、烈火の続編にあたる(※という説がある)「バトルガレッガ」はアーケードで稼働してヒットしました。(バトルガレッガ続編説については、wikiの関連項目を参照)

バトルガレッガは、2016年に PS4 と XBOX ONE 向けにも移植されています。
https://m2stg.com/battle-garegga/

実際、ファミコンで100万本以上売れた STG の大半(ゼビウス、戦場の狼、グラディウス、ツインビーなど)は、アーケードゲームが原作の移植作品です。

ただし、スターソルジャーはファミコンが原作でありながら100万本以上のセールスを記録しています。スターソルジャーがヒットした要因を色々なサイトを見て分析した結果、タレント(高橋名人)を使ったコロコロコミックとのメディアミックス戦略が好走したもの と考察しました。

今風に例えると「有名ユーチューバーにゲーム実況されて人気ゲームになった」という文脈に少しだけ似ているかもしれません。

ゲームを売るにはプロモーション(認知活動)のやり方がとても重要だと思います。

プロモーションとは?

プロモーションとは、単にテレビ局にお金を払ってテレビで CM を流せば良いというものではありません。

その時の社会情勢などで大きく様変わりする人間の認知機能をハックして、効果的に商品の販売促進をする高度な技術と嗅覚が必要なものだと私は考えています。当然ながら銀の弾丸のようなものは存在しませんが、重要なのは「社会コンテキストの理解」かもしれません。

だから、社会コンテキストをまるで理解する気が無く「自作ゲームを創るためにゲーム機創っちゃいました〜」みたいなことをしている私にはプロモーションは難しいのかもしれないと思う今日この頃です。

幸い今回のゲームギア版Battle Marineはプロモを担当してくれる方が別に居るので、これで売れなかったらシンプルに「Battle Marineが面白くなかった」という結論が得られるかもしれません。

今回のゲームギア移植は「プロモーションが目的」と先述しましたが、その深意は「Battle Marine が面白いか面白くないかについての結論」を得ることなのかもしれません。

もちろん、私はものすごく面白いゲームだと思っているのですが、作者の評価ほどアテにならないものは無いとも思っています。

自分で作った料理は何でも美味しいものです。

結論が得られれば、何の未練もなく次回作の創作に専念することができます。

技術のはなし

「売れるか」とか「ゲームが面白いのか」などの難しい話題はさておき、技術屋の視点では 「烈火がファミコンで開発可能なら、ファミコンより少しだけハイスペックなゲームギアなら余裕だろ?」 とまでは思っていませんが、少なくとも100%無理というレベルのものでもないことは、烈火の事例がある以上自明(技術的に可能)だと考えられます。

問題は私と烈火のプログラマ(現ケイブの矢川さんという方らしい)との技術力の差ですね...この点は正直全く自信ありませんが、レジェンド級のプログラマへのチャレンジとかアツイ。

そんな訳で、2024年3月初旬からゲームギア版Battle Marineの開発をスタートしました。

あわよくば「ゲームギアにおける弾幕STGの決定版」としてBattle Marineが認知されればベストですが、開発プロジェクトを開始するに当たり、Steam版Battle Marineのエッセンスだけでも再現した新たなゲームギア作品を創ることが出来ればそれで OK 程度に意識のハードルを下げておきました。

むやみにハードルを上げるといつまでたっても完成しなくなってしまい、かといって時間を掛けた分だけゲームが面白くなることは無いと思っているので、完成の見込みがある目標設定をすることは重要です。

という訳で、開発期間は3ヶ月以内 という工数上限を設定しました。

オリジナル版(VGS-Zero版)は1ヶ月以内の上限設定でしたが、今回は慣れないゲーム機(ゲームギア)+フルアセンブリ言語ということで余裕を見て長めに設定。

その範囲内で全力全開の本気で開発します。

ゲームギアのスペック

ゲームギアは8ビットゲーム機としては「ほぼ最強」と言われています。

もっともそれは私が勝手に言っているだけですが :sweat_smile:

ゲームギアの「スペック」や「ほぼ最強の根拠」は以下の記事で詳述しています。

1990年発売のゲームギアは、テクノロジー的には 1985年発売(5年落ち)のセガマークIIIと概ね等価 です。

以下、「弾幕STGの開発ターゲット機種」としてゲームギアのスペックをざっくりと分析した結果を記します。

画面解像度とスプライト密度

弾幕STGを開発するには「多くのスプライトが使える事」が望ましいです。

ゲーメストか何かのインタビューか何かで昔IKDさんがそんなことを言っていたような気がします。(ソースを確認できなかったので記憶違いかも :sweat_smile:

ゲームギアが利用できるスプライト数は最大 64 枚です。(ファミコンとセガマークIIIも 64 枚)

一方、VGS-Zero で利用できるスプライト数は256枚なので、VGS-Zeroで開発したゲームをそのまま移植すると 画面がスカスカな状態 になってしまいます。

スプライト数が少なくても解像度が低ければスプライトの画面密度を高くすることができるため、弾幕STGを開発する上ではスプライトの枚数だけでなく解像度(の低さ)も重要になってきます。

(各機種の解像度)

  • ゲームギア: 160x144 ピクセル
  • VGS-Zero: 240x192 ピクセル
  • セガマークIII: 256x192 ピクセル
  • ファミコン: 256x224 ピクセル

画面に表示可能な総ピクセルを数えてみると、ゲームギアは 23040(160x144)、VGS-Zeroは 46080(240x192)で丁度 1:2 の関係 になります。

画面密度の低さ(S値; スカスカ係数)総ピクセル数÷スプライト数 の計算式で求めると以下のようになります。

(各機種のS値)

  • VGS-Zero: 180(240x192÷256)
  • ゲームギア: 360(160x144÷64)
  • セガマークIII: 768(256x192÷64)
  • ファミコン: 896 (256x224÷64)

S値が低いほど「弾幕STGが開発し易い環境」と定量的な判断ができます。

より正確なことを言うと、スプライトの基本サイズはゲームギア、セガマークIII、ファミコンは 8x16 or 8x8 ピクセルなのに対して、VGS-ZeroはOAMレコード毎に8x8〜128x128のサイズで定義できるので、S値もあまりアテにはなるものではありません。(基本的に敵弾には小さなスプライトが使われるので概ね大丈夫だとは思いますが)

更に正確なことを言えば、ゲームギアのスプライトは倍サイズに引き伸ばすことができ、その機能を用いることでスカスカ係数を 1/4 に下げることができます。ただし、全部(64枚)のスプライトが引き伸ばされるので「キャラクタを全てモザイクのようなものにする」という大きすぎる代償がありますが...(ちなみにこの機能はセガマークIIIだとVDPのバグのため使用できません)

倍スプライトを用いて S値 を低くしたゲームの事例は、私が知る範囲ではゲームギアには存在しません。(別機種では MSX1 のレリクスとか?)

幸いゲームギアは「解像度が低い」という大きなアドバンテージ(?)があるので、S値が VGS-Zero 比で 4 倍以上あるセガマークIIIやファミコンと比べれば「弾幕STGの開発難度は低い」といえます。

それでもゲームギアと VGS-Zero のS値には2倍の乖離があるため、完全に移植をすることは不可能です。(その点はゲームデザインの調整でカバー)

処理速度(CPU)

ゲームギアの CPU は 約3.5MHzの Z80 で、VGS-Zero の CPU は 16MHz の Z80 です。

同じ Z80 ですが、ドラゴンボール Z で例えると概ねヤムチャとサイバイマンぐらいの絶望的な戦闘能力差があるイメージでしょうか。

ただし、VGS-Zero 版 Battle Marine は C 言語(SDCC)で開発されていて、SDCC は最適化の面でそれほど優れていないこともあり、フルアセンブリ言語で記述すればゲームギアの Z80A でも(処理速度に限れば)VGS-Zero 版に匹敵するゲームが開発可能と判断しました。

移植結果

概ね想定通りに移植できました。

折角なので実機で動かしている様子を紹介します。

弾幕STGのエッセンスが全て入っているかは怪しいところですが、私の技術力ではこれが限界でした...

折角実機で動かせる形にしたので、ゲームギア版 Battle Marine は物理メディア(カセット)でのパッケージ販売をしたいと考えています。物理メディアで販売するには量産化や小売店との交渉などに時間が掛かると思われるため、発売はもうしばらく先になるかもしれません。

※今回、パブリッシャーやプロモーターとしてのお仕事は別の方にお任せしているので私はノータッチです(その辺のことは苦手なのでとても助かります:bow:

その間に、ゲームギアの実機を使ったゲーム実況配信のノウハウなどを研究しようと思っているところです。

今回、開発中の動画をツイッターで何度か公開したのですが、エミュレータで撮影した動画よりも実機で撮影した動画のエンゲージメントが高く(反応が良く)、海外の相互フォロアーさんから「面白そう!」とか「遊ばせてくれ!」などのDMを頂いております。

円安ということもあり VGS-Zero 版 Battle Marine は海外中心にプロモーションしていることもあって海外からの反応が良いです。ゲームギアも国内だけでなく海外での人気が高いかもしれません。参考までにゲームギアの出荷台数は日本では200万台弱(178万台?)で、全世界では1,400万台(参考)とのことです。

要するに、エミュレータよりも実機を使ったゲーム実況配信の方が話題になりやすいかもしれません。

ただし、実機を使ったゲーム実況配信は少し難しいので、色々と研究中です。

そもそも実機の入手が大変ですが...(後述)

技術的に苦労した点

今回の開発で技術的に苦労した点を記します。

(1) フルアセ

VGS-Zero版Battle MarineはオールC言語で開発してますが、今回はオールマシン語(Z80)で開発しました。(流石にアセンブラは使っていますが)

私は趣味でZ80エミュレータの開発をしているので、Z80についてはそこそこ詳しい方だと思います。

それでもアセンブリ言語でプログラミングするのは別腹ですね。

実は、Z80のアセンブリ言語でプログラミングするのは今回が初めての超初心者です。

超初心者ではありますが、Z80の全命令、命令毎のクロック数、隠し命令を含む全ての挙動を完全に把握しているので、「どの命令を使えば高速に動かせるコードが書けるのか」といった技術的なノウハウだけならあるのですが、C言語なら2時間もあれば作れるプログラムを1日かけて作るイメージでした。

そんな生産効率が悪いアセンブリ言語での開発ですが幾つかのメリットを見出すこともできました。

例えば「左から来る潜水艦」と「右から来る潜水艦」という敵キャラのアルゴリズムを作る時、高級言語なら共通の関数(例: move_submarine 関数とか)に移動方向のパラメタを与えるなりして作ると思います。

しかし、関数を共通化すると関数内部のブランチ数(判定処理数)が多くなってしまいます。

アセンブリ言語でプログラムを組んだ場合、判定処理数に比例してコードの複雑度が高くなってしまうため、move_submarine_leftmove_submarine_right という風に別々の関数(サブルーチン)として実装したくなります。

コードとしては言うまでもなく「保守性の悪いクソコード」になりますが、判定処理数が最小化されることで「処理速度が早くなる」という恩恵があります。

物理カートリッジでの販売を前提とした場合、販売後は昨今のゲームのようにプログラムをアップデートをすることが不可能(致命的なバグがあればリコールが必要)なので、「将来の保守性」についてはある程度は目を瞑ることができます。

要するに、完成から3ヶ月後にはもう修正できない程度の極悪な保守性のコード であっても実用上の問題は何らありません。それならコードの保守性をかなぐり捨てて「性能確保」に全振りするのが正解です...多分。

フルアセンブリ言語で記述したプログラムの場合、バグの修正漏れが発生すると、バグ対策に割かれる時間が高級言語とは比べ物にならないレベルで長くなるため 「バグ即FIX」 を鉄則としました。

バグは時間の経過で成長し、成長したバグの対策難度(対策工数=コスト)が指数関数的に増加することはご存知かと思われますが、フルアセンブリ言語で書いたコードの場合、バグを1日積み残すだけでも雨後の筍の如く成長して修正コストが鬼のように高くなるため、バグを見つけたら issue チケットを切っておいてテストが終わってから対策...などと悠長なことをしているとカジュアルに詰みます。(詰んだ部分はモリっと作り直す必要があるので、大きなプログラムを組む場合、ある程度詰むことも前提 として影響を極小化しながらコーディングするという謎の職人スキルも必要になります)

ここまで読んで賢明なプログラマなら「デメリットしか無いじゃないか...」と憤慨されるかもしれません。

確かにその通りです :sweat_smile:

ですが、生産性効率が悪いからこそ細部の実装を丁寧に創ることができる ので、結果的に作家性が高いゲームを創りやすいかも?という印象です。

言い方を変えれば 「魂を吹き込み易い」 です。

そんな合理性の欠片もない謎のメリットが、フルアセンブリ言語で組む最大のメリットで、これは実際にフルアセンブリ言語でゲームを創ってみないと実感できないことかもしれません。

君ならできるよ...

大変ではあるものの、全ては「慣れ」で何とかできるレベルかなと。

幸い、VGS-Zero ならゲームギアよりもキレイなフルアセンブリ言語でのコーディングが可能なので、VGS-Zero で創る次回作はフルアセンブリ言語で組んでみても良いかもしれません。

VGS-Zero の場合、次節で詳述する VDP wait が不要です。

VGS-Zero は VRAM I/O を IN/OUT 命令ではなく LD 命令(mmap I/O)で記述できるメリットがありますが、実際にゲームギアでプログラミングをしてみた結果、それ以上に VDP wait が存在しないこと のメリットが物凄く大きいことを実感しました。

(2) VDP wait

ゲームギアでは 0xBF ポートに VRAM アドレスなどを設定してから 0xBE ポートの連続的な入出力で 1 バイトづつ VRAM I/O を行いますが、0xBE ポートへの連続的なアクセス には 28Hz(※Z80A クロックレート換算)のウェイトを入れなければなりません。

以下に DE へ設定された VRAM アドレスに対して、 HL で設定された文字列ポインタ(0x00 終端のキャラクターコード配列をポイント)から文字を書き込むコードを示します。

.vdp_putstr
    call vdp_setreg_de
vdp_putstr_loop:
    ld a, (hl)              ; 7Hz
    cp $00                  ; 7Hz
    ret z                   ; 5Hz or 11Hz
    out ($be), a
    inc hl                  ; 6Hz (6)
    nop                     ; 4Hz (10)
    nop                     ; 4Hz (14)
    nop                     ; 4Hz (18)
    nop                     ; 4Hz (22)
    ld a, $00               ; 7Hz (29)
    out ($be), a
    jmp vdp_putstr_loop     ; 10Hz

28Hz 以上のウェイトを入れなかった場合、VRAM へのデータ転送がロストしてしまいます。

そしてデータ転送がロストすると下図のように描画が強かに乱れます。

上述のサブルーチン vdp_putstr は汎用サブルーチンのため、VDPウェイトの待ち時間を有効活用しきれてませんが、処理性能の低い Z80A だと VDPウェイトによるオーバーヘッドがトータルでかなり大きくなってしまいます。

そこで、VDPウェイト時間を利用して次ステップの処理を先回し実行する という、構造化プログラミングをガン無視したスパゲティコードですらリーダブルコードに見えてくるレベルのクソコード実装による最適化実装をガンガンすることで性能を稼ぎました。

この世の全ての穢を詰め込んだかのようなその「クソコード目録」は、ゲームが完成する頃には「聖典」へと進化していました。

ゲームが完成した今の時点で見つかったバグはもう治せる自信がありません :sweat_smile:

もしも、ほぼ完成した段階で若干不可思議な動作をする箇所を指摘された時、恐らく私はニッコリと微笑みながらプログラマが全ての対話を拒否する時に使うあの禁忌スペル「仕様です」を躊躇なく詠唱するかもしれません。

私の名誉のために補足すると、本業の仕事では禁忌スペル「仕様です」「無理です」「お前がやれ」を使ったことは無いです。ちゃんと対話をした上で、その修正の必要性について納得のいく説明がされなかった時に要求を拒否することならありますが。

もちろん、致命的なものなら(マスターアップ前なら)修正しますが。

そのような不幸な事故を未然に防ぐためにも、やはり「バグ即FIX」が超重要ということになります。(あとは調整できる項目を #define のリテラルに絞るなどのごく一般的な対応も重要)

(3) 実機調達

当初、開発は全てエミュレータを使ったクロス開発のみで行う予定でしたが、先述の VDP ウェイトのエミュレーションが完璧に実装されたエミュレータは無いようだったので実機調達をすることにしました。

ゲームギアは発売から既に30年以上経過しているので、オリジナルの電解コンデンサ、液晶(バックライトを含む)、カートリッジスロットあたりの部品は流石にそろそろ耐久限界(寿命)だと思われます。

幸い、syf.nl(オランダ)、RetroSix(英国)、Aliexpress(中国)などの通販サイトで大半の交換部品(互換部品)を入手できるので、部品調達の難度は比較的低めかと思われます。

ただし、ICチップには互換製品が無いので、メインボードを互換ボードへ交換したい場合はジャンクの実機からリワークをする必要があり、若干難度が高め(出費も高め)になります。

ジャンク品のゲームギアを修理をする場合、どちらかといえば「後期モデル」のジャンク品を入手することをオススメします。

日本で発売されたゲームギアには、Z80とVDPが別チップのもの(前期モデル)とワンチップのもの(後期モデル)の2種類があり、後期モデルであればまだ天命が尽きていない部品が載っている可能性が高く、メインボード交換時のICチップのリワーク作業量も半分で済みます。

私は前期モデルを使ってますが

前期モデルと後期モデルは、本体裏のシリアル番号の下にあるモデル番号で見分けることができます。モデル番号部分がプラスティック加工のもの(凸で文字が書かれているもの)が前期モデル、シールで貼られているものが後期モデルです。(確証はないですが多分)

電子工作初心者の私でもコンデンサ交換と液晶交換ぐらいなら出来ますが、運良くメルカリでコンデンサ交換と液晶交換されたゲームギア本体が比較的お手軽な価格(3万円ほど)で売られていたのでそれを購入しました。

なお、カートリッジスロットの調子だけあまり良くなかったので、RetroSix製の互換カートリッジスロットを家電のケンちゃんで購入して自分で換装しました。

image.png

カートリッジスロットの交換は、元のカートリッジスロットのピンをニッパーで切断して互換カートリッジ基板をはんだ付けするだけなので結構簡単にできます。(以下の動画を見て作業すれば大丈夫だと思います)

なお、私は元のカートリッジスロットのピンを短くカットし過ぎてしまい、新しいカートリッジ基板の通電が安定しないミスをやらかしました...スズメッキ線を差し込んではんだ付けしたところ無事導通が安定したので、心配な方はスズメッキ線も調達しておいた方が良いかもしれません。(数百円で買えます)

ゲームが中々起動せずに困った時は、カセットをふーふーする前にスロット交換をしてみましょう。

なお、カセットをふーふーすると唾液で接点がサビてしまうのでオススメできません。単純な接点の問題は酸化膜に起因する場合が多いらしいので、錆取り潤滑油(いわゆる接点復活剤)で拭き取ると良いです。

錆取り潤滑油は、スロット以外にもボタン接点の清掃にも使うことができ、ボタン接点を清掃するとコントローラの入力レスポンスがかなり良くなるため、本体と併せて調達することをオススメします。

また、カートリッジについては CUBIC Style さんが販売しているフラッシュカートリッジとライターを使いました。

販売するカートリッジは CUBIC Style さんのものを使う予定なので、なるべく現品に近い環境でテストをしたかったという事情があります。

その他の手段としては EverDrive GG(だいたい2万円ぐらい)や Flash Gear PRO (Aliexpress で購入可能で概ね5千円〜1万円前後)などの microSD を使ったカートリッジを使う方法もあるかと思われますが、私は持っていないので未確認です。(EverDriveでは正常に動いたようです)

実機環境の準備に掛かった費用はだいたいトータル 5 万円ぐらいでしょうか...SteamDeckよりは安いですが、それでも中々えげつないですね。金額だけの話ではなく一定の電子工作スキルも必要なのことも普通の人はハードルが高いのではないかなと。

電子工作に興味がある方にとっての入門用としては最適かもしれません。

ただ、シンプルに「ゲームをプレイしたい」ということであれば Analogue Pocket がオススメです。

本体 220 ドル、ゲームギアアダプタ 30 ドル で 250 ドル (1 ドル 150 円換算なら 37,500 円) + 送料 (60 ドル) 程度のお値段なので、実機を修理して使うのとコスト面で大差が無く、電子工作スキルも一切不要なので手間も少ないものと思われます。

(4) 画面欠け対応

ファミコンの場合、画面の端 8px はテレビに表示されない可能性があるため、重要な情報は画面端に表示してはならないという不文律のルールがあります。

液晶画面を備えた携帯ゲーム機であればそういった点を考慮しなくて良いだろうと思っていたのですが、私の実機環境(※LCDは互換品)では画面の上1pxが見えない形になっていました。

この問題についてはハードウェアスクロール機能を使って画面を下へ 2px 縦スクロールするという力技で対処しておきました。(今回スクロール機能はスタッフロールぐらいでしか使わないのでこの力技を使うことができます)

なお、ゲームギアの縦スクロールには MVS フラグの設定で画面の右端16px(2セル)をスクロールさせない機能があります。(MVS フラグは VDP レジスタ 0 番の第 7 ビットで設定します)

私は当初、無意味にMVSフラグをセットしていたので、何故か右端がスクロールされずに困惑しました :sweat_smile:

(5) スプライト水平上限

ゲームギアは水平方向に表示できるスプライト数が 8 枚までという制限があり、9枚以上表示した場合、優先度が低い(OAMのアドレスが大きい)スプライトの表示が省略されます。

そのため、敵キャラ毎に OAM レコードの位置を固定してしまうと「常に見えない敵」が出てくる可能性があります。そこで、敵キャラの OAM レコード(優先度)を1フレーム毎にローテーションさせることで、水平上限になった時は「チラつかせる形」にするのが望ましいです。チラつくのはユーザー体験としては良く無いですが見えない敵が出てくるよりはマシです。

ローテーションは毎フレーム次のような処理を実行することで実現しました。

  1. 全ての敵キャラ OAM の Y 座標に 0xD0 (非表示) をセット
  2. 敵キャラ毎に使用できる OAM インデックス値をローテーション
  3. 敵キャラの移動(描画)処理で「動きが無くても」毎回 Y 座標だけは必ずセットするルールで実装

ただでさえ処理がキツキツの中、このような処理を実装しなければならないのはオーバーヘッド的な意味でかなり厳しかったです。

とはいえ不可避なことなのでヒーヒー言いながら実装しました...水平上限はとても厄介です。

(6) atan2 対応

私が「弾幕STG」を実装する上で不可避な要素だと思っているのが「自機狙い」の敵弾なので、Battle Marine でも自機狙いを実装しました。

自機狙いのアルゴリズムとしては、三角関数(アークタンジェント)を用いる方法とDDAを用いる方法が一般的かと思われます。

三角関数の場合、sin/cosのテーブル(256バイト)とatan2テーブル(座標粒度によりサイズが異なる)を準備する必要がある関係で多くのメモリ(※read only でも OK)が必要になるデメリットがありますが、メモリさえ準備できればテーブル探索で角度計算ができるため「計算量が少ない」というメリットがあります。(つまり、高速です)

DDAはテーブルではなく計算により角度を合わせるアルゴリズムなので多くのメモリを必要としませんが、処理性能が遅くなります。

という訳で、シビアな性能が要求される今回のケースでは「三角関数一択」です。

ただし、座標系が 256x256 だと 65,536 bytes の atan2 テーブルが必要になり、そのために 4 バンク使う余裕はありません。バンクにもあまり余裕が無いので、今回は 64x64 の座標系 (4,096 bytes) の atan2 テーブルを準備して、それを用いて自機狙いを実装しました。

64x64 座標系への変換は、スプライト座標系(256x256)を SRL 命令 (8Hz) を 2 回呼んでシフトするだけなのでとても高速です。

座標系を粗くすることで自機狙いの精度がかなり悪くなりましたが、上記動画を見る限り概ね許容範囲の精度だろうと判断しました。

(7) スプライトパターンのやりくり

ゲームギアでは、8x8 サイズのキャラクタパターンを BG では 512 - 64 枚使うことができますが、スプライトでは 256 枚(BG の半分)しか使うことができません。

更にスプライトサイズを 8x16 にすると 128 枚しか使えません。

スプライトにはアトリビュート領域が無いため、8x8 と 8x16 を混在させることができず、更に左右反転や上下反転などのパターン数を節約する上で重要な機能を使用することもできないため、スプライトパターンのやりくり難度が高めです。

Battle Marine にはステージの切り替えがなく、1つのロングステージをプレイして段々レベルが上がっていくゲームデザインになっているため、ステージ切り替えのタイミングでキャラクタパターンを更新することでパターンを増やすテクニックを使うことができません。

一般的なSTGのようにステージ切り替え型にしようかとも考えたのですが、ステージ切り替えを入れてしまうとゲームのテンポが悪くなり、プレイヤーがゲームオーバー後にリトライをしたくなるリトライ性が低下してしまいます。

そこで、「128枚以内に全部のキャラクタパターンを詰め込む」という難解なパズルへのチャレンジを試みました。

幸い、私はパズルがかなり得意なので128枚キッチリ全部使い切りました :muscle:

(8) メインコードのやりくり

ゲームギアのメモリマップは、16KB 区切り x 4 ページ(64KB)になっていて、先頭 3 ページがプログラムバンク、最後の 1 ページが RAM というページ構造になっています。

ページ 0 はバンク 0 (ROM の先頭 16KB) に固定されていて、ページ 1 と 2 が任意バンクに切り替え可能というのが、一般的なゲームギアカートリッジ基板に搭載されているバンクコントローラの仕様です。

ただし、私はあまりバンク構造を複雑にしたくなかったので、メインプログラムをバンク 0, 1 & ページ 0, 1 に固定して、データ(一部サブプログラム)をページ 2 でバンク切り替えして使うメモリレイアウトで開発を進めました。

つまり、Z80 のプログラムで使用できるメインプログラムのコードサイズの上限は 32KB ということになります。

当初、フルアセで記述したプログラムコードが 32KB も行くことは無いだろう...と甘くみていたのですが、開発が終盤に差し掛かる頃にはかなりギリギリの状態になりました。

コードが書けなくなったらそこで試合終了です。

29
16
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?