Z80 ゲーム機を作ろう(その7)
さて,前回は,BG の表示を行う基板を作成しました。
ただ,この BG は,格子状にドットパターンを並べたものなので,滑からな動きをするキャラクターの表示などには向いていません。
文字通り背景表示を想定した機能です。
そこで,今回は,ゲームには必須なキャラクターの表示を行う機能として「スプライト」機能を作って行きたいと思います。
スプライトって何だ?
古いアーケードゲームや家庭用ゲーム機を遊んでいた方なら聞いたことがあると思います。
また,パソコンでは,MSX や,X68000 などにハードウェアとして装備されていましたね。
例によって,ウィキペディア に詳しい説明があります(笑)。
今回作るスプライト機能は,簡単に言うと,16×16 ドットの大きさのパターンを,画面上の好きな位置に表示できるようにするものです。
BG と違って,スプライト同士を重ねて表示することもできます。
だったら,BG を使わないで,すべてスプライトで表示すればいいんでないの…?って思うのですが,さすがにそうはならないです(笑)。(そういうゲーム機もあります)
このスプライト,画面に表示するにはどうするかというと…,スプライト 1 枚につき,4 バイトの設定用メモリがあり,そこに,パターン番号,X 位置,Y 位置,カラー番号を設定すると,16×16 ドットのパターンを表示してくれます。
とても高速に設定できて,大変便利なのですが,その分回路も複雑で,表示できる枚数も限りがあるのです。
ラインバッファを使って表示します
BG の場合は,格子状に 8×8 ドットのパターンが敷き詰められているので,画面を(ドットクロックに合わせて)走査していく中で,それぞれの位置が確定します。
なので,画面を走査しながら,ドットパターンを比較的簡単に表示することができるんですね。
対して,スプライトは,それぞれが表示座標を持っているので,どこに表示されるかは,それぞれのスプライトの表示座標と,現在の走査線位置を比較チェックしなくてはいけません。
加えてパターン番号やカラー番号の決定もあるし,すべてのスプライトが同じ座標に重なって表示されていたら,画面を走査するのに合わせて表示するのでは,とても間に合いません。
そこでどうするのかというと,画面の走査に合わせてドットを表示する時に,あらかじめ走査線 1 ライン分のメモリに表示するスプライトのドットを書き込んでおいて,そのメモリ内容を読み取って画面に 1 ライン分のドットを表示していきます。この時に使う走査線 1 ライン分の表示用メモリを「ラインバッファ」と言います。
あれ?でも,そのラインバッファの内容は,いつ書き込むんでしょう…?少なくとも表示するラインを走査する前に書き込まれている必要がありますよね。
モニターが走査線 1 ラインを表示する直前には,水平ブランク期間という画面に表示されていない期間があります。
時間としては短い期間ですが,このタイミングで,上で説明した,どのスプライトが今回の走査線上に重なっているか(表示する必要があるか)をチェックします。
ハードウェアによっては,この水平ブランク期間で,表示するスプライトの決定からラインバッファの書き込みまでを行うものもありますが,今回は,水平ブランク期間中に,2 ライン先で表示されるスプライトを その Y 位置と走査線位置からチェックして決定し,ブランク期間直後の 1 ライン表示期間中に,決定したそれぞれのスプライトの該当するラインのドットをラインバッファに書き込み,さらに次の 1 ライン表示時に,ラインバッファの内容を画面に出力する…という流れになります。
でも,これだとラインバッファへの書き込みと読み込みが同時に行われてしまいますよね?
なので,ラインバッファは,書き込み用と読み込み用で 2 つ用意して,交換しながら使います。いわゆるダブルバッファっていうやつですね。
スプライトと言えば横並び問題
この横並び問題。よく聞きますよね。
そうです。
ファミコンとかでキャラクターがちらちらするアレです。
いや,実は,ちらちらするのが問題ではなくて,同一ライン上に表示できるスプライト枚数に上限があり,消えてしまうスプライトがあることが問題なのです。
同一ライン上に,優先順位の高いものから一定枚数表示すると,それ以降のスプライトは画面上に表示されません。
でも,それだとゲームとしては不都合なケースが多いので,表示の優先順をローテーションすることによって,この表示されないスプライトを固定しないようにしているんですね。
では,なぜ同一ライン上に表示できる枚数に上限があるのでしょう?
もうおわかりですね…。
上で説明したラインバッファを処理できる時間に限りがあるからですね。
今回は,この辺りの処理を 10MHz クロックで処理するのですが,1 ラインで処理できるのが 16 枚分となります。
全体では 32 枚表示できるのですが,同一ラインに 17 枚以上並ぶと,17 枚目以降のスプライトが消えることになります。
スプライトメモリ基板
とりあえず,基板を作りました。
スプライト機能自体,1 枚の基板には収まりませんので,今回はスプライトの属性などを設定するメモリ周りを作りました。このメモリは,スプライト 1 枚につき,4 バイトの情報を持ちます。
- パターン番号
- X 座標
- Y 座標
- カラー番号とフリップ情報
MSX で言うところの「スプライトアトリビュートテーブル」,任天堂のゲーム機で言うところの「OAM」に相当します。
BG のメモリは,表示サイクルの隙間で,CPU から書き込むことができたのですが,このスプライトメモリは,そんな隙間の時間もないので,垂直ブランク期間のみ,CPU から書き込み可能となります。また,書き込み専用で読み出しはできません。
Z80 と接続
そして,6 階建てになりました。
まぁ,この基板だけでは,スプライトを表示することはできないので,今回はここまでです。
画面表示は次回以降となってしまいます。
さて,次回は,スプライトメモリの Y 位置と走査線位置の比較回路や,キャラジェネ ROM からラインバッファへの書き込み回路などを作る予定です。
なんか,また,1 枚に収まらないような気がするので,画面表示はさらに先に…となるかもしれません。
はしめまして。
ハチロクと申します。40代の粗忽物でごさいます。
仕事はWEBのプログラマでして、インターネット関係ソフトウェアをやっております。
LEDチカチカぐらいは体験がありましたが、本格的な電子工作は全くの初級からでしたが、こちらでワイヤラッピングを知り、見よう見まねでZ80、sram、pic(uart)でHello worldを返す程度の回路は作れました。
ここからスプライトの勉強をしようと記事を拝見しつつ、mr doのマニュアルなどを見ながら勉強しています。
ただ、VGAの同期信号をカウンタならべて作り、
カラーバーを出すところまでは出来たのですが、
その先が非常に大変そうです。
不躾でずうずうしいお願いなのですが、もしよろしければ
回路図やFPGAのソースコードを拝見させては頂けないでしょうか。
当然お見せ頂いても公開したりは一切致しません。
電子工作に興味がずっとあり、
お金もかかるし進捗もいまいちで、試作失敗も数多く、
いまいちどモチベーションを持ち直すきっかけにさせて頂けないかと思います。
ぜひとも私の無謀なチャレンジに助け舟と思い、どうかお助けくださいませ。
メールでのご返信まちします。
よろしくお願い致します。