Seaside Laboratory

Articles

KFX マニアックス

今や希少種となってしまった KFX ユーザー向けに作られた「重箱の隅つつく」系の解析資料。気が向き次第更新していく予定。

プログラム解析系のネタを書くときには、飛龍++さんの逆アセソースを参考にしている。

最終更新日 2023-02-04

voice.lst

WAV ファイルの使用上限

公式アナウンスでは 0 ~ 8 までの 9 個しか使えないとされているが、プログラム側の実装は、静的な配列を 20 個確保する作りになっているため、実際には 0 ~ 19 までの 20 個から予約番号である 9 番を除いた 19 個を使うことができる。あまり多くの WAV ファイルを使わないのであれば、無理して危険なマイナス値を使う必要はない。

マイナス値のボイスを使うと残像が出なくなる理由

ボイス (DirectSoundBuffer) の手前にはスプライト (DirectDrawSurface) が配置されているので、負のオフセット値を使ったアドレッシングをするとスプライトエリアが破壊されてしまう。とくに残像用のスプライトはボイス寄りに配置されているので、早い段階で破壊され、正常に描画されなくなる。

スプライトデータには余分に確保された未使用のエリアが存在するので、ある程度の破壊になら耐えることができる。

motion.lst

行番号が持つ意味

CARROT 純正のキャラ作成用ドキュメントには、

MOTION.LST は 1299 行までとなっています。必殺技等で使えるのは、1000 ~ 1299 行までです。

と書いてあり、単なるキャラを作る上でのガイドラインかと思ってしまうがそうではない。KFX では一部の処理に、行番号を使った状態判別が存在する。

  • 空中の相手に CL が 0 か 1 の基本技を当てたときに、空中復帰かダウンかを選択する時。
  • 浮いている状態の相手に攻撃を当てた時、次にどの行へ遷移するのか選択する時。

どの CL を食らったのかを行番号から逆算する仕様になっているので、モーションを詰め込む目的で指定外の行を使用すると正常に動作しなくなってしまう。

Sp カウンタのリセット

キャラクターはモーションの進行状況を表すカウンタ変数を持っている。KFX は増加するタイプのカウンタを採用しており、以下の動作を繰り返す。

  1. カウンタに 1 を足す。
  2. カウンタが Sp 未満の場合は最初に戻る。
  3. カウンタが Sp 以上になったら 0 でリセットして次の行へジャンプ。

ところが、何故かキャンセルした時だけカウンタを -1 でリセットするという仕様になっているため、結果としてキャンセル時のみ 1 フレーム間延びする。

Ci ってなんぞや

リストに使われているパラメーター名には英単語の略称のようなものが使われており、例えば Sp なら Speed、De なら Depth のように簡単に推測できるものがほとんどだが、Ci だけは何の略称なのか不明である。

キャラクターの属性情報を雑多にまとめたものなので Character Information の略かと思ったが、hitcheck.exe 起動時に表示されるパラメーターは「CIRCO」となっていて謎は深まるばかり・・・。

ジャンプ開始時に Ci を 0 したときの挙動

コマンドに上入力が含まれる必殺技 (サマーソルトキック等) は、入力が少しでも遅れると先にジャンプが発動してしまい、必殺技が出なくなる。このシビアな入力を緩和するため、ジャンプ開始行の Ci を 0 にすることで、先にジャンプが発動してしまったとしてもキャンセルによって必殺技に遷移させる、というテクニックが考案された。

このテクニック、よくよく考えてみると実に奇妙なのである。Ci が 0 ということは、ジャンプモーションの行にいるとはいえプログラム側での認識はニュートラルでしかなく、ニュートラルはジャンプに遷移できるので、理論上は上を押し続けている限りジャンプ→ニュートラル→ジャンプ・・・と無限にループすることになる。

実際そうならないのは KFX のモーション遷移処理が少々特殊なのが理由で、コマンド発動前と後の行番号を比較し、変化があったときだけ遷移をするので、同じモーションへの遷移は変化がなかったと見なされキャンセルが抑制される。

ずれる音声

音声の再生は Sp カウンタが 0 の時だけという条件になっている。これは、新しい行にジャンプしたタイミングだけ音声を再生させるという意図がある。「Sp カウンタのリセット」に書いてある通り、キャンセル時は Sp カウンタが -1 でリセットされるため、音声が再生されるまでに 1 フレームの遅延が発生してしまう。

KFX はヒットストップが短いので、あまり気になることはないが、キャンセルして「チョーすごい技」を出すと暗転によってモーションが長時間停止してしまうので、体感できるレベルの再生遅延が起きる。

大気圏離脱現象

ゲームをプレイしていると極稀にキャラクターが空へ飛んで行ってしまい、しばらく落ちてこないことがある。

KFX はキャラクターが攻撃を受けると、しゃがみ中であればしゃがみ食らい、空中であれば吹き飛びモーション後に復帰して着地といった様に、状態に応じた食らいモーションが適用されるようになっている。ただ、CL が 6 の攻撃だけは少し特殊で、キャラクターがどんな状態であっても「ふらふら食らい」が適用される。

キャラクターをオフセット移動させるために、Y ベクトルに大きな値を指定して移動した際、運悪く「ふらふら食らい」を受けてしまうと、Y ベクトルがリセットされず、もの凄い勢いで空へ飛んで行ってしまう。

飛び道具が行の先頭で出ない理由

飛び道具発射指定を行頭に書くと処理がスキップされてしまうという有名なバグが存在する。飛び道具を発射するタイミングかどうかは「ずれる音声」に書かれているものと同様の条件になっているので、この条件を満たさない状況になると飛び道具は発射されなくなってしまう。

KFX は以下のような順番で処理を行っているので、コマンド経由で特定のモーションにジャンプした場合、飛び道具発射処理のタイミングでは Sp カウンタが必ず 1 になってしまうという構造的なバグを抱えている。

  1. コマンド発動
  2. フレーム進行 (Sp カウンタ増加)
  3. 飛び道具発射

キャンセル時は「ずれる音声」と同様に Sp カウンタがずれるので、1 フレーム遅れる形で発射される。

何でもキャラクター基準

KFX はキャラクターと飛び道具の処理がごっちゃになっているので、色々と問題が多い。有名なバグとしては以下のようなものがある。

  • ヒットしているのは飛び道具なのにキャラクター側のヒット分岐が有効になる。
  • 曙フィニッシュになるかどうかは、キャラクター側の Ci で決まる。

この他にも、攻撃結果がキャラクターの座標によって変わってしまうというマイナーなバグが存在する。飛び道具の攻撃であってもキャラクター側の状態が参照されてしまうため、飛び道具が相手に当たる瞬間にジャンプすると空中からの攻撃と判定され、仰け反り時間が大幅に増加する。

command.lst

謎に包まれた移動コマンド

移動コマンドは通常のコマンドとは違い、専用のプログラムで処理されているので、コマンド参照に必要な CmnNum とモーション参照に必要な AmnNum 以外全て無視される。CmnNum はプログラム側からの番号指定があるため、実質カスタマイズ可能はのは AmnNum だけとなる。

行番号が変更できなかったり、Std が 000 でも動作してしまうといった不可解な挙動は、全て上記仕様によって起こされる現象である。

Std が 000 のコマンド

Std が 000 だった場合、キャラクターの状態と Std が一致することがないため、コマンドが発動しないと考えるのが自然であるが実際は違う。command.lst 読み込み時に Std が 000 の行は Command の読み込みがスキップされ、長さ 0 として扱われる。Command が存在しない行となるので、コマンド発動処理時に参照されなくなる。

KFA には Std が 000 のキャンセルコマンドを設定することで、ゲージを使ったガードキャンセル技を作るといった様々なテクニックが存在するが、KFX だと行が存在しないものとして扱われるため、このテクニックを使うことはできない。

多ゲージ消費技の仕組み

同じコマンドを記述することで多ゲージ消費技が作れるというのは、意図して用意された仕様や応用テクニックではなく、単にバグである。

command.lst は小さい番号から順に読み込まれ、コマンドの有無にかかわらず全コマンド (0 ~ 129) がチェックされる。条件を満たすコマンドが見つかる度に 1 ゲージ消費され、発動可能と判定された一番最後のコマンドが採用される。

enemy.lst

距離認識バグ

エネミーが正常に動作しているのか検証をするのが難しいこともあってか、あまり表面には出てこなかったが、エネミーの動作には以下のようなバグが存在する。

  • 相手との距離を保持した変数を確率値で上書き破壊する。
  • 行遷移する度に確率値を計算し直しているので不正確。

あとはバグというより、仕様的なもの。

  • 使える行数は 40 行。
  • 使える文字は "12346789pPkKnN" だけ。5 などの範囲外値は保証外。
  • 先頭にあるボタン入力は無視される。

enemy.lst は「レバー + ボタン」というコマンドを前提としているのか、ボタンを読む前に必ずレバー分をスキップする構造になっているので、先頭にボタンがあるとスキップされてしまうので読み取ることができない。

stage.bmp

ステージ画像の折り返し

ステージのサイズはゲーム上では 640x240 となっているが、画像ファイル上では 320x480 となっていて、横長のステージを途中で折り返したような形で格納されている。これはおそらく DirectDraw の制約に因るもので、DirectX 3 SDK のドキュメントには以下のように書かれている。

DirectDraw does not permit the creation of display memory surfaces wider than the primary surface.

DirectDraw はプライマリサーフェスよりも大きなディスプレイメモリサーフェスを作成することができません。

DirectDraw は画面 (プライマリサーフェス) のサイズを超えるスプライト (ディスプレイメモリサーフェス) を扱うことができないので、ステージ画像はこの制約に引っかかってしまう。制約は「画像を複数のパーツに分ける」という単純な方法で回避可能だが、ラスターで構成されているビットマップ画像を左右で分離しようとすると座標計算が面倒になるので、上下で分離するという方法がとられたと思われる。

ただ、実際の処理は一度にまとめて読み込んでいるので、この特殊な形状は古い DirectDraw に対応していた時代の名残のようなものと言える。KFX の前身にあたる KOKFX は DirectX 3 を使っているため、ステージ画像は STAGE0.BMP と STAGE1.BMP という 2 つのファイルに分かれている。

この仕様は KFA に引き継がれる際に「左右半分に割ったものを縦に配置」と解釈されることになったが、正しくは「画面サイズに分割して縦に配置」なので、もし KFX が大きなステージに対応していたら、サイズ 1280x240 のステージは 640x480 ではなく 320x960 になっていた可能性がある。

その他

リソースデータのカスタマイズ

マニュアルには記載されていないが、下記ファイル名と同じファイルを KFX のディレクトリに配置することで、リソースデーターの一部をカスタマイズすることができる。

ファイル名 デフォルト 用途
moji.bmp リソースの NUMBITMAP ビットマップフォント
load1.bmp load.bmp OPTION 画面背景
load2.bmp load.bmp INFORMATION 画面背景
load3.bmp load.bmp CHARACTER SELECT 背景背景
load4.bmp load.bmp VS 画面背景
load5.bmp load.bmp 起動時とエンディング背景
kfx.wav なし 起動時の効果音

moji.bmp のみ KFX 1.31 から対応 (といっても最終版) で、ディレクトリも system ではなく kfx.exe と同じ場所に配置する必要がある。

マニュアルの間違い

KFX 付属マニュアルに書かれている悲しい間違い。

是非ご覧下さい
CMAKER フォルダにはキャラクター作成キット、解説テキストを用意しました。

KFX 1.00 にしか収録されなかったので、キャラクターを作る気満々になっても作成キットが見つからず挫折すること間違いなし。本体とは別に、kfxcm100.lzh という名前で単体配布されていた時期もあった。

作った技をゲーム中で見る場合、HOME CLR キーで判定を表示しておいて、ROLL UP と ROLL DOWN でスピード調節をすることができるんですが、それを使用するとどのように技が出ているかわかりやすいでしょう。

スピード調節機能は存在しない。便利そうな機能故に無かった時の落胆が大きい。

エンディングの間違い

エンディングが始まると「CONGLATULATIONS」という文字が上に向かって流れていくが、正しい表記は「CONGRATULATIONS」。