前回の高速化のヒミツ①は、一部でそれなりに話題になりましたので、そのままその②の解説です。今回は処理を端折るとなります。前回の記事をご覧になっていない方はこちらからご参照ください。
- 描画を端折る
突然ですが私は描画の際に、正確かつ正しい処理はどこまで必要なのかと疑問を感じまして。例えば、地形との重ね合わせです。正しく処理しようと思ったら、マスクデータと背景データを論理積してから、重ねる画像データを論理和して、元の画像表示位置に描き戻すという動作を行います。

GVRAMから画像を拾う

表示対象の形状マスクをANDする

表示対象をORして重ね合わせる

これ、如何にも重い処理ですよね。以前作成し始めて挫折した PC-6001mk2 の ARPG は、こんな処理を毎回していまして、それに加えて樹木が上から重なってくるという、とても重い処理を行っていました。

GVRAMから画像を拾う

表示対象の形状マスクをANDする

表示対象をORして重ね合わせる

GVRAMに描き戻す
これ、如何にも重い処理ですよね。以前作成し始めて挫折した PC-6001mk2 の ARPG は、こんな処理を毎回していまして、それに加えて樹木が上から重なってくるという、とても重い処理を行っていました。
今回、XeGraderを作成するにあたって、前回の反省を行っています。それは、本当にこの処理は必要だったのかと。考えてみてください。アクションゲームです。その場に立ち止まっていたら、あっという間に狙い撃ちされてミスになります。つまり、プレイヤーは常に激しく動き続けているわけです。そんな中で細かいディテールを再現する必要はなかったのではないかと。
そこで、ステージの背景の基本は3つのパターンの単純な組み合わせとして、さらにもっと基本的な部分としては、2色のタイリングまでとしてしまい、怪物やプレイヤーの背景は、この単純化された2色のタイリングで予め塗りつぶしておいた画像データを、本当に単純に画面に描画するだけで良いのではないかと。先に例を上げたスライムですが、PC-8001mk2版で実際に使用している画像データがこちらです。

重ね合わせ済の画像を単純描画しているだけですから、本来処理するべきANDORの処理とは比較にならないぐらい高速に描画が完了します。因みに現在開発中の PC-6001mk2 版のスライム画像はこんな感じにしてみました。

もはや重ね合わせしてる状態とほぼ変わりません。まあ、当然ですが、きちんと背景と合っているわけではないので、赤い点がない場所に突然赤い点が出来ます。ところが実際に試してみると全く気にならないのです。これは脳が錯覚を起こしているのではないかと思っています。
※ なんとなんとのSFC実機用コントローラの新製品です。マジっすか…

重ね合わせ済の画像を単純描画しているだけですから、本来処理するべきANDORの処理とは比較にならないぐらい高速に描画が完了します。因みに現在開発中の PC-6001mk2 版のスライム画像はこんな感じにしてみました。

もはや重ね合わせしてる状態とほぼ変わりません。まあ、当然ですが、きちんと背景と合っているわけではないので、赤い点がない場所に突然赤い点が出来ます。ところが実際に試してみると全く気にならないのです。これは脳が錯覚を起こしているのではないかと思っています。
※ なんとなんとのSFC実機用コントローラの新製品です。マジっすか…
- 消去を端折る
キャラが移動するには「消して」「描いて」の繰り返しとなります。消す処理ですが、今までだとこんな処理をしていました。
- キャラの下のマップチップ番号を取得する
- マップチップ番号に応じた画像データアドレスを取得する
- キャラの以前の位置に描画する
- この描画をキャラのサイズに応じて繰り返す
マップチップサイズは通常は所謂1キャラとなります。そして、怪物などの基本サイズは2x2キャラとなりますので、上記の消去はキャラを消すのに都合4回も走る事になります。これも意外と馬鹿にならないぐらい重いのです。そこでXeGraderではかなり思い切った作戦に出ました。それは、GVRAMと同じサイズで背景バッファを用意して、そこから単純コピーで消去するというアルゴリズムです。
これがどれくらい思い切ってるかというと、Z80のメモリ空間全てが最大連続で64KBのうち、背景バッファに16KBと、なんと1/4もバッファだけで使い切っているのですから。ただ、それでも残り48KBはありますので、なんとかメモリに入ると思いました。実は最初はざっくりとはメモリ計算してたのですが、最後は「勘」です。これぐらいかなあと決めていきました。結果、当初予定20面が、18面+αに減ったんですが、これはまあ、うん、はい、仕方がないっすよね^^;
そして、もう一つの工夫もあります。それは GVRAMのアドレスと、背景バッファのアドレスを同一にするという事です。背景データを拾ってくるのに、いちいち別のアドレス計算をしてたら処理時間がもったいないです。PC-8001mk2 では背景画像バッファの位置を $0000 からとしました。GVRAM は $8000 からなので、最上位1bitを 1 にすれば実アドレス、0 にすれば背景バッファアドレスになります。
現在作成中の PC-6001mk2 では、さらにこの考えを推し進めて、背景バッファの位置を全く同じ $8000 にしました。このおかげで、こんな荒業でキャラを消す処理を行っています。
; ; HL 消去アドレス ; Acc 縦ライン数 ; EraseChara16: ld bc, -VRAM.WIDTH ld e, a ld d, a push hl call .erase pop hl set 5, h ld e, d .erase di ld (.stack), sp .loop ld sp, hl ; SP コピー元 pop af push af add hl, bc dec e jp nz, .loop .stack equ $ + 1 .exit ld sp, 0 ; SP 元に戻す ei ret
ミソは .loop の下にある pop af : push af です。一見何をやってるのか完全に意味不明です。実はコレ、戦士のカートリッジのアクセス対象を、読み込みは EXRAM、書き込みは INRAM(GVRAM)としているのです。だから同じところから読むと、消去するための画像データの取得になって、同じところに書き込むと今度は GVRAM への書き込みになるというわけです。今はループとしていますが、戦士カートリッジのお陰でメモリに余裕が出てますので、16個程度は展開してしまうつもりでいます。
なお、これが戦士Ⅱカートリッジだと、本体RAMをカートリッジ内蔵の拡張RAMにまるっと置き換えて、M1ウェイトをoffに出来ます。すると、かなりの高速化が出来るというわけです。戦士Ⅱカートリッジの設計回路は現在非公開なので、今回はCloneが手に入る初代戦士カートリッジとしてます。ちょっとだけ残念ですけど仕方がないですよね…
コロンバスサークル
2024-09-20
※こちらも実機用のHDMI変換出力です。NewFC以外では変換元にS端子出力を使用しているとのことなので、もしかすると高画質かと期待しちゃいますよね-
※こちらも実機用のHDMI変換出力です。NewFC以外では変換元にS端子出力を使用しているとのことなので、もしかすると高画質かと期待しちゃいますよね-
コメント