省メモリには左右反転でのデータ削減は大きかったです。ちょっと考えたら分かると思いますが、左右移動は完全にデータが半分になります。あと、上下移動に関してもピッタリ半分になります。右足を前に出す動作と左足を前に出す動作、反転できるのです。

PCGデータ定義を反転するのですが、当然ハードウェアにそんな機能はありません。単純にPCG定義データはビット反転になるだけではなく、PCG配置データも反対から並べないと実現できません。結果、例えばキャラが右に移動すると処理が重くて、左に移動すると処理が軽いみたいな、処理速度のばらつきが起きてしまいました。割り込みが無いPC-8001での処理速度のばらつきやふらつきは、即座に音楽再生速度のふらつきに繋がります。


  • 負荷を分散させる
速度のふらつきは、パターン反転時と通常描画時の処理時間の違いで発生しています。速いほうを遅い方に合わせる、つまり速い時はウェイトを入れると安定はしますが、CPUの処理能力を無駄にしてしまいます。そのため、ウェイトを入れる方法では無く分散処理を模索する事にしました。

パターン反転が重いのであれば、それを、重い・普通・重い・普通・重い・普通・etc...と交互になるようにパターンを配置すればどうかと考えました。つまりは、右に移動するときは、通常・反転・通常・反転・通常・反転とします。そして、左に移動する時は、反転・通常・反転・通常・反転・通常とするのです。
r00l00r01l01r02l02
このアイディアにより、処理速度の分散化に成功しました!
※ み、ミスターX…

  • 踊りカウンタ方式からブロッキングに
以前は、芸夢狂人さんがI/O誌に投稿していた「踊りカウンタ」で制御する方法を実践していました。私も Unityを弄るまでは、この考え方で作成していました。ただ、よくよく考えるとこの方法はCPUをかなり無駄遣いしている事になります。
踊りカウンタ方式
実行時間が 00,01,02,03 と進むのが横軸です。踊りカウンタは、カウンタ値がゼロになったら動かすという考え方です。この例では軽くするために処理タイミングをズラしてありますが、それでもフレームによっては重い軽いが出ています。また、さらにカウンタ値の確認というCPUの無駄がありますし、赤色(ヒーロー無敵)が発生すると処理落ち必須となってしまいます。

そこで考えたのが、各処理をブロックとしてまとめて、フレーム毎に呼び出し先を変えるという手法です。メインループは 2F 単位となります。これは人間の移動がどうしても 2F かかってしまうために、やむなしとした部分です。流石に100人動かすのは重かったです…。
ブロック単位
ゲーム中、パワーエサを食うと主人公であるヒーローは加速します。その加速処理は呼び出しブロックを一つ追加する事で簡単に実現しました。加速中は5ブロックから6ブロックになります。なお。各ブロックの処理毎に PCG再定義が入ります(パワーエサと火炎放射は除く)ので、垂直ブランキングによるウェイトが入ります。
ヒーローの移動を追加
これ、実はヒーローが加速している…に加えて、他の処理がちょっとずつ遅くなっているのです。図が理解できると分かるんですが、プレイヤーの移動速度は1/5から2/6に向上しています。呼び出しを2倍にしても、分母が増えているので実際には2倍速になっていないのです。そして、プレイヤー移動以外の全ての処理は、1/5から1/6にほんの僅か低下します。

結果、その相対速度差により、ヒーローの加速が目立つというわけです。パワーアップ中は主にヒーローにプレイヤーは注力しているので、ちょっとした錯覚を利用しているというワケなんです。PC-8001が遅いので、普通に処理すると重く感じてしまうため、全体を逆に速度低下させることにより高速移動を実現しているように見せかけているのです。
※ マラソンとかウオーキングだとこれが良さそう。

  • その他小さな高速化と安定化のために
一度ゲームが動き始めてしまうと、二度とBASICは正しく動作しなくなるほど、メモリ内容は変更されます。そのため、カセットテープロード直後だけ、画面のハード的な初期化は BASIC ROM 呼び出しとして本編プログラムから取り除く事にしました。実行速度が不要な処理は、積極的に BASIC ROMを活用すると良いかと思います。

移動による以前の位置にいるキャラの消去は、移動変化の差分だけとしています。例えば、下方向に移動している時は、上の1ライン分だけ消せば良いです。全てを消して描いてを繰り返すより、処理範囲が狭くなり、また、ちらつきも軽減される事になります。欠点は、4方向全て専用処理になってしまうことでしょうか。

さらに例えば、右の移動処理と左の移動処理は違いは僅かなので、ヘッダーでプログラム書き換えで方向を決めた後で同一ルーチンを流用しています。これでかなり大幅にメモリの削減に繋がりました。Z80のコードは予想以上にメモリを食っていたりするので…。

画面全体のフェードイン/フェードアウトは、アトリビュートを数バイト書き換えるだけで実現しました。こういう処理にはアトリビュートの構造は便利ですね。あと、ここまで省メモリを突き詰めても、オープニングデモとエンディングデモは、到底メモリに収まらないと分かりましたので、このプログラムはゲームの本質部分では無いと判断して、別空間に分離する事にしました。これは、0x6000 ~ 0x7FFF に拡張メモリがある機種のみ、デモが動作する仕様として解決しています。デモプログラムってかなりメモリを食うんですよね…。止せば良いのにスタッフクレジットまで実装したので、それはもう💦


Newシティヒーローメイキング・インデックス
  1. Newシティヒーローメイキング
  2. スタッフアサイン
  3. PCG再定義
  4. アトリビュート制御
  5. メモリ省力化
  6. マップデータの圧縮
  7. 動作の安定化
  8. 人間100人を動かすために

※ 先日うっかり日焼けをしてエラい目に遭いました。これの導入を考えています。