85DATA HOMEへ
MYCAR情報掲示板BLOGプロフィール

HOME → ソフトウェア → X1でゼビウスを作る!

第2回 時間測定による自機(ソルバルウ)の動作とスクロール速度

時間測定

X1は時間測定やタイマ割込みの機能が貧弱な機種のようですが、一定の速度でゲームを進行させる事は重要です。
X1の場合、垂直帰線期間 (以降「Vブランク」と書きます) は I/Oポート 1A01Hのbit7 により確認する事が可能です。そこで、この情報を利用し時間測定を行う事にしました。

 LD BC,1A01H
 IN A,(C)             ;BIT7=0 の時、Vブランク

上記の例では、AレジスタのBIT7を調べれば、Vブランクであるか否かの判断ができます。 

@ 前回調査したレジスタ値(のBIT7)と、現在のレジスタ値(のBIT7) が異なる時
A 現在のレジスタ値がVブランクである時

よって、@とAの条件が満たされた場合が、Vブランクに切り替わったタイミングであると判断出来ます。

しかし、常にこれを監視していては処理が止まってしまいますので、この”Vブランク検出・ルーチン”を ゲームのループ内に複数回コールする仕組みとします。つまりポーリングです。

Vブランクのサイクルは、約1/62秒 (以降 「1/62秒」と書きます※文末の補足を参照下さい。私は、1/60秒であると勘違いしていました) = 0.016145秒です。
したがって、”検出ルーチン”を、は0.016145秒以内に1回以上はコールしなければなりません。
(でないとVブランクの検出を取りこぼしてしまいます)

本移植作では、処理コストの掛かる描画ルーチン (”ソフトウェア・スプライトルーチン”、”スクロール・ルーチン”、”アンドアジェネシス描画ルーチン”) をコールする度に”Vブランク検出・ルーチン”もコールするようにしています。

さらに、この”Vブランク検出・ルーチン”にて、Vブランクに切り替わったタイミングで、自機の移動ルーチンやサウンド再生ルーチンをコールするようにしています。
よって、1/62秒単位で、自機の移動 (やサウンド再生)が可能となります。
つまり、”1/62秒毎にソフトウェア割り込み”を行っているようなものです。

さらに、Vブランク回数をカウントしており、ループ最後の”ウェイト処理”にてVブランク回数が6回になるまで待ちます。(
1ループは0.09687秒 (1/62秒×6回) になります)

これにより、一定の速度でゲームが進行出来るようになります。
(自機の移動ルーチンやサウンド再生ルーチンは、1ループ内で6回コールされる事になります)

【処理の概要】


この手法は、X1でゲームを作る時は、常套手段かもしれませんね。
(改めて書くほどの内容では無いのですが、まぁ備忘録として記録しておきます)

ゲームの画面サイズやキャラクタの大きさ

本家ゼビウスのゲーム画面は、縦288ドット。(横228ドット)
X1の画面で表示可能なのは縦200ドットです。

よって、小さいドット構成にしなければ画面に収まりません。
約69.4%の縮尺率 (200/288= 約69.4%) で作れば良いのですが、キリが悪いので62.5%の縮尺率で作ります。

縦ドット数 = 176ドット = Int(本家288ドット×62.5%/8ドット)×8ドット
横ドット数 = 136ドット = Int(本家224ドット×62.5%/8ドット)×8ドット

62.5%にする理由は、処理の軽減(高速化)とPCGの節約です。
キャラクタサイズは本家16×16ドットを本移植では10×10ドットにします。これが62.5%の縮尺率なのです。

前章に書いた「キャラクタ描画 (ソフトウェア・スプライトルーチン)」の処理軽減がねらいです。
10ドットにした場合2Byte (16ビット=16ドット)で収束できます。2Byte内で6回のビットシフトが可能であり、描画速度に貢献できるはずです。(下図参照)


 16ビットのシフトイメージ (黄色部が、グラフィックデータ)

10ドットを超えたキャラクタサイズの場合、3Byteによるビットシフトが必要となります。2Byteと3Byteでは速度面で大きな違いとなります。

後述のソルバルウ(や照準、ザッパー)においては、PCGを使用します。同様にキャラクタサイズが10ドットであれば、ずれたパターンを定義するのも縦横2×2のキャラクタ数で収束可能です。

ただし、(前章でも書きましたが)ガルバーラ・ガルデロータ・ソルの大きなキャラクタは20ドット(1ラインで3Byte)になります。
この場合は、ビットシフトをさせず、キャラクタの配置は8ドット単位に割り切りました。

自機(ソルバルウ)の動作

1/62秒単位で、自機の移動ルーチンがコールされますが、このルーチンに多くの処理コストを掛ける訳にはいきません。

そこで、X1の特徴でもあるPCGを利用する事にしました。

蛇足
PCGとはテキスト文字(8×8ドット)をドット単位で8色の定義が可能。もちろん表示は文字として扱えるので高速で描画が可能です。
グラフィックの場合 8Byte×3プレーン(RGB)=24ByteのデータをVRAMへ転送する事が必要ですが、PCGの場合であれば、1Byteで済みます。
グラフィック描画と比較すれば24倍も速いという事です。

PCGは8×8ドット単位での定義ですが、以下の様にドットをずらしたパターンを用意する事により、2ドット単位で動かせるようにします。

蛇足
PCGデータ(画像データ)は下図の左上だけを、ピットシフトをしながらPCG定義を行います。また、PCG定義が終了したら、その画像データのメモリ領域は別の用途しとして使用します。

自機(ソルバルウ)自体のドット構成は10×10ドットなので、ひとつのパターンで4個のPCG定義が必要となります。

横方向に4パターン、縦方向に4パターンの合計16パターンが必要となります。
(PCGの定義数で言えば、4倍の64個必要となる)
なんと、贅沢(無駄な?)PCGの使い方でしょう。

自機を動かすという事は、同時に照準も動かす事になりますので自機と同様に16パターンを定義します。

なんだか、野暮ったい照準ですね。ちょっと太すぎる感がありますが、スクロール背景がタイリングパターンなので、この位にしておかないと、照準が滲んでしまうのです。

照準が地上物と重なった場合のパターンは準備していません。(本家は照準の一部が赤点滅となる)
残念ながら、PCGの定義数が足りないのです。

点滅はテキストVRAMのアトリビュートのBIT2を変化させる事で、実現させました。

通常時は$27 (00100111B)とし、点滅時には、$23 (00100011B)とさせる。

赤を点滅させたかったのですが、マゼンダ(赤紫)の点滅になってしまいました。


さらに、弾(ザッパー)も16パターン用意し、1/62秒毎に動かせるようにしました。
弾(ザッパー)の移動単位は8ドットにしていますが、1/62秒毎に動かしているので滑らかに見えます。

この3つのキャラクタだけで、PCG定義数は160個にもおよんでしまいました。
(自機64個+照準64個+弾32個)

普通ならば、こんな事はしないのですが (前記事「スクロールとキャラクタ描画について」で書いた通り) 他のキャラクタや背景は全てがグラフィックなので、この手法も有効ではないかと思った訳です。

スクロール速度について

上述の通り、0.09687秒※※毎(1ループ毎)にスクロールさせますので、スクロール速度は以下の表の通りになります。
本家(オリジナル)とも比較してみましょう。
(X1版と記載してあるのは、本移植作を示しています。電波新聞社製ではありません)

X1版の方が本家に比べ僅かに速いのですが、Vブランク回数を7回待つようにすると遅くなってしまいますし、グラフィック描画のキャラクタの動き(速度)と自機の動き(速度)のバランスが悪くなってしまいます。

今回は、これで妥協しました。・・・といっても、それほど違和感がある程では無いと思っています。

補足

1/62秒

エミュレータ上で動作させると1/60秒がVブランクの周期でした。
しかし、実機では少し異なるという事をX1 CENTERの管理人さんに教えていただいきました。
本記事内に1/62秒と記載しておりますが、正確には1/61.938417秒 (0.016145秒) のようです。


※※0.09687秒

エミュレータ上で動作させると0.1秒でした。
つまり、X1のVブランクのタイミングは、0.016145秒毎なので6フレームの場合0.09687秒となるそうです。

以下はX1 CENTERの管理さんからいただいたコメントの抜粋です

X1の標準設定におけるフレーム周期は
14.31818MHz / (8dot x 112(R0+1) ) / (8x 32(R4+1) + 2(R5) ) = 61.938417Hz

CRTCレジスタのR0,R4,R5を、上手くいじれば本家と同じ速度でゲームが進行出来るようになる・・・・のかもしれません。