上に戻る
jsr $0003;敵の配置や種類をちょこっとだけ弄る
概要
 マップを書き換えたら、対応して敵の配置などを弄れなければダメそうなので、考えてみようという記事。
オブジェクト
 ゲームソフトでは、ゲーム上の処理を効率良く行うために、キャラクター単位等、処理を分けて行うことが多いと思います。その、処理の単位をオブジェクトと呼ぶことにします。
 分かりやすい例として、敵キャラクター1体1体は、オブジェクトとして処理されます。
 もう少し分かりづらい例として、エックス自体、あるいは、エックスや敵の出した武器もオブジェクトです。さらに、敵を倒したとき、画面下に落ちていく破片もオブジェクトです。
 さらに分かりづらい例として、ゲーム進行のための監視を行っているオブジェクトもあります。イメージとしては透明な監視員がいて、エックスがある一定のラインを踏み越えたかどうか常時監視しているようなものを考えてください。
オブジェクトの役割区分(グループ) 概念
 オブジェクトは1つずつ、RAM上にメモリが確保されます。(正確には、固定位置に常時マップされているので、「確保されている」が正しい)
 前項で、「こういうものがオブジェクトです」と列挙しましたが、役割毎に、どのくらいメモリを消費するかというのは変わってきます。ところが、最もメモリを食うものに合わせて確保したらメモリがいくらあっても足りません。また、何かと役割毎に分けておいた方が処理上の効率も良いです。
 そういった理由があるのだと思いますが、オブジェクトは、役割毎に種類わけされています。この、役割毎の区分を、グループと呼ぶことにします。
 余談ですが、ファミコンのロックマンでは、敵も、敵弾も、同じ様にオブジェクトとして、確保されていました。
オブジェクトの役割区分(グループ) 内容
 私の調べた限り、オブジェクトのグループは、8グループ存在します。メモリ上若いアドレスから順番に、A,B,C,D,E,F,G,Hとグループわけを行います。
 それぞれのグループ毎に役割があり、その一覧が以下のとおりです。
グル
ープ
用途(このウェブサイト
での呼称)
メモリ
サイズ
解説
A エックス 20 3 エックス自体を処理するが、なぜ3人分確保されている?
B エックスのエフェクト 20 C よくわかってないです。アーマーも?
C 40 A 仕掛けに使われることも
D エックスの武器 40 9  
E 敵の武器 40 8  
F アイテム 30 10 アイテム以外にも、ゲート等の役割もある
G パーティクル 20 28 「破片」等のエフェクトによく用いられるため、
大量に出現できるようにしてある。
しかし、破片以外の用途にも使われる
(パレット変更オブジェクト?)
H コンダクター 10 10 プレイヤーがゲームを進行できるように監視。
スクロール制御など。
 AグループのオブジェクトのことをObAと表記することにします。以下、ObB,ObC...
 時々、AグループそのもののことをObAと表記したりもします。(雑だなぁ……)
マップ上へ配置されるオブジェクトのグループ
 以上のように8グループのオブジェクトがありますが、マップ上に配置できるものは、ObC、ObF、ObG、ObHの4グループのみです。すなわち、敵、アイテム、パーティクル、コンダクターの4グループです。
 まぁ、武器とかは、それらのグループのオブジェクトが放出するものですし、エックスなんかその辺に置けてもしょうがないので当たり前といっちゃ当たり前なのですが、パーティクル(破片)が置けるのは変な話です。
 マップ上に配置されるObjG(パーティクル)は、実は「破片」等のエフェクト用途ではなく、どうも、パレット変更なんかに関する物のようです。名前と異なる用途になっていますがご了承ください。
 メモリを解析して呼称を決めた後に、マップに置け、また、別の用途があることが判明したので、こんなことになっているのです。
出現ライン
 巨大なマップ上へのオブジェクトを大量に配置するわけですが、その出現処理に関する話です。

 今、Winアプリとしてで2DのACTを作るなら、座標と種類を一定の形式で格納しておいて、フレーム毎にその位置が画面に収まっていれば出現処理、とすれば問題ないです。毎フレーム、出現しうる全てのオブジェクトにおいて走査処理なんていうのは、今のプロセッサからすると負荷的に安いものでしょう。たぶんw
 しかし、話は、当時としてすら遅いといわれたらしいSNESのプロセッサです、そんなことはしていられません。そこで、何らかの最適化が必要とされます。

 最適化のため、配置されたオブジェクトは、いずれかの出現ラインに属します。(※「出現ライン」はこのウェブサイトの呼称です)出現ラインは、「縦線」であり、32ドット単位で巨大なマップを分割します。

出現ラインの図解 水色のライン……マップ全体の領域
緑のライン……出現ライン
赤い●……配置されたオブジェクト(数字は格納順)
細い赤線……そのオブジェクトがどの出現ラインに属しているか


 多くの場合(というか、実際のゲーム中、例外なく?)は、赤●(配置されたオブジェクト)は最近傍の出現ラインに属する。
 逆に、オブジェクトがある場所に出現ラインが引かれている、と見る方が自然かも知れない。

 尚、処理の都合上、出現ライン毎に、●はY座標でソートされ、上から順番に確保されている必要がある、らしい。(出現ラインのXによるソートが優先です)この例の場合、数字の順番に格納されます。たぶん。

 横スクロール時は、横方向に32ドットスクロールするたびに、その瞬間に画面内に入ったラインに属しているオブジェクトの出現処理を行うだけでよいので、負荷の軽減が期待できます。実は縦スクロールでは、1ドットスクロールするたびに、画面に収まる全てのラインの出現処理が行われるため、比較的、負荷軽減効果が低そうです。

 出現ラインのX座標と、そのラインに属するオブジェクトのX座標のデータは独立しているため、画面右端に出現ラインが入った瞬間、画面の中ほど(エックスの目の前)にザコを配置することも可能です。改造では良くある「奇襲」ですね。
 このような配置をすると、左スクロール時は、左端に出現ラインが入った瞬間、画面左端よりもさらに左にオブジェクトが配置されるため、すぐにスクロールアウトし、見かけ上、出現しないことになります。
いいからどこ書き換えんの?
 アドレス3CCE4Bに、ステージ毎の、配置オブジェクトデータへのアドレスがあります。このアドレスは16bitであり、バンクは固定で3Cです。16bitアドレスの配列なので、ステージ01のホーネックステージは、+2した3CCE4Dのデータです。
 こうして得たアドレスの先に、配置オブジェクトのデータがあります。

 配置オブジェクトのデータは、出現ラインのデータと混在しているためバイナリエディタでは少々見づらいかもしれません。
 以下のような順番でデータが格納されています。

(1)処理の始め、もしくは、次のラインへの移行が指示されたとき
 1バイト読み出し、それをそのまま出現ラインのX座標とします。
 実際は20h(32d)倍された座標になります。
 この読み出しが終わると、(2)へ移行します。

(2)配置オブジェクトのデータ
 7バイトで1オブジェクトになっています。順番に[0]-[6]としたとき、
 [6]の最上位ビットが1だと、(1)へ移行し、次のラインを読み込みます。
 0であった場合は、引き続き(2)で、次のオブジェクトデータを読み込みます。

 [0]-[6]の役割は以下の通りです。
Offset役割解説
[0]グループ0/アイテム 1/パーティクル 2/コンダクター 3/敵 それ以外/クラッシュ
[1]YhiY座標の整数部
[2]YheY座標のページ単位
[3]Offset+0Aオブジェクトのメモリのベースアドレス+Aに書き込まれ、「種類」となる
[4]Offset+0Bオブジェクトのメモリのベースアドレス+Bに書き込まれ、「引数」となる
[5]XhiX座標の整数部
[6]次ライン/Xhe1Fとマスクされ、X座標のページ単位になる。最上位ビットは前述の役割。
 唐突にYhiやらYheやら現れましたが、以下のような表記を行います。
 Ylo = Y low = Y座標の小数部
 Yhi = Y high = Y座標の整数部
 Yhe = Y higher = Y座標のページ単位
 Xも同様です。loとhiは多くの人が許してくれると思いますが、heはこのウェブサイトでしか使えません。


 尚、(1)において、一つ前と同じX座標が読み出されると処理を終えます。
 最初のラインを読み出すときは、一つ前の座標がFFであったとして処理されています。
配置オブジェクトのデータの例
 以下のようなデータがあったとします。

08 03 80 03 08 01 00 01 03 C0 03 08 01 10 81 0C 03 80 03 08 01 80 81 0C

 ……凄く見づらいので、改行をします

08
03 80 03 08 01 00 01
03 C0 03 08 01 10 81
0C
03 80 03 08 01 80 81
0C

 コメントをつけると……

08
03 80 03 08 01 00 01
03 C0 03 08 01 10 81
0C
03 80 03 08 01 80 81
0C
出現ライン 08*20=0100
(0100,0380)に種類08/引数01のObC(敵)を配置
(0110,03C0)に種類08/引数01のObC(敵)を配置 最後がマイナスなので次のラインに
出現ライン 0C*20=0180
(0180,0380)に種類08/引数01のObC(敵)を配置 最後がマイナスなので次のラインに
出現ラインだが、一つ前と同じ0Cなので、オブジェクトデータ終了
バイナリエディタではやりづらい
 データが見づらい上、X座標を、ラインと一緒に動かさなければならず、また、ラインも順番どおりになっていないと不具合が起きると思うので、他のラインをまたいだりしてはならないなど、頭こんがらがる要因が多いです。
 一応、一つ上で公開しているエディタもどき「RockMac002」で少し弄ることもできるのですが、ソフトが悪いので結局やりづらいです……

 きちんとしたエディタを作るなら、ROMをちょこちょこ弄るような仕様ではなく、エディタ上でオブジェクトを管理し、(また、ステージ毎に外部ファイルに書き出せるようにして、)書き出し時に、出現ラインは自動的に作成されるような仕様にする必要があるでしょう。わざとずれた場所にラインを引く「奇襲」もサポートすると尚良さそう。書き換え前の容量を超えてはいけない、という、容量の壁が厳しくなりそうですが……
ゲーム中に敵のデータはROMにある?RAMにある?
 なんやかんやで、敵のデータを書き換えたあと、エミュで実行し、その付近で取っておいたセーブステートデータをロードすると、結構正しく動く……のですが、実はちょっとまずいことが起き得ます。
 敵のデータは基本的にROMから逐一読み出されているのですが、一部のデータはステージに入った時に整理され、RAMに格納されます。その2つのデータの整合性の無い状態でプレイをすると、おかしな現象が起こりえるようです。
 具体的には、判定ラインの情報と、敵のY座標(hi/he)と、敵の情報のあるアドレス(2バイト)がRAMに展開されます。そのため、Y位置をずらしたした場合、上下スクロール時におかしなタイミングで出現する事になります。(セーブステートをとったときのY座標で出現する瞬間に出てきてしまう?)
 また、もっと大工事を行い、判定ラインを新設したり、除去したり、オブジェクトを前後のラインに送ったりしたときは、アドレスを保持している都合上、非常にまずい事になることが想定されます。
 結局のところ、敵データもマップと同様、書き換えたら、ステージを読み直すことが推奨のようです。
敵以外も置かれている
 RockMac002で見ると分かるのですが、オブジェクトは敵以外にもかなり大量に配置されています。その一つずつが、スクロールを制御するためのオブジェクトであったり、画像を転送するためのオブジェクトであったり、色アニメーションをセットするためのオブジェクトであったり、再開地点をセットするためのオブジェクトであったりします。本気で書き換えるならこれらも全て丁寧に配置していく必要があります。そのためには、それらのフォーマットも完全に理解する必要があるでしょう。
 一応、チマチマと解析して入るのですが、ほとんど解析が進んでいない上、解析できている部分に関しても、改造しやすいフォーマットではなさそうな結果も出てきており、X3改造の道のりは険しそうです……
 今後数回の予定は、この、ゲームの制御に関するオブジェクトの利用方法について検証していく事になります。
(195)2011年10月16日 プレさ兵衛
inserted by FC2 system