DQ3 ループアニメーションの仕組み

DQ6のマップをインポートした際に気がついたのですが、燭台の炎など、明らかにパレットアニメーションではなく、画像自体が変化しているアニメーションがあります。これを再現できないと完全に移植できたとはいえないので例によって必要に迫られて調べてみました。

  • SR: $0A5B38 ダーマ神殿進入時初期化処理
0A5B64 JSL $C77889 SR: $077889 引数:1#$CAE5D7 ループアニメーション2設定
  • SR: $0AE5D7 ダーマ神殿ループアニメーション設定
0AE5D7 JSL $CBDF05 SR: $0BDF05 ループアニメーション設定(BG1,2用)
0AE5DB JSL $CBDF25 SR: $0BDF25 ループアニメーション設定(BG1,2用)
0AE5DF JSL $CBE064 SR: $0BE064 パレットアニメーション設定(夜のみ)
0AE5E3 JSL $CAEB35 SR: $0AEB35 パレットアニメーション設定?
0AE5E7 RTL return
  • SR: $0BDF05 ループアニメーション設定(BG1,2用)
0BDF05 LDA #$000E A=#$000E
0BDF08 JSL $C472D7 SR: $0472D7 ループアニメーションアクセス(BG1,2用)
0BDF0C RTL return
  • SR: $0472D7 ループアニメーションアクセス(BG1,2用)
04730B JSL $C90501 SR: $090501 引数:1#$00 引数:2#$0007 引数:3#$C54E84 引数:4#$4A 構造体アクセス Y番目の先頭アドレスをDP($00,第4引数)にセット

ざっくり言うと、最終的に$054E84-の固定長のデータにアクセスしています。上で例示した#$0E番目のレコードは以下のようになっています。便宜上ループアニメーションインデックスと呼びます。

アニメーションパターン開始アドレス ループアニメーション実データ開始アドレス 使用SNES4BPP画像数
EE502B EE5034 04

それぞれのアドレスの先は可変長のデータです。まず1つめは#$FFをターミネーターにして2バイト1セットでループするフレーム数*1、パターンIDを記述します。$2E502Bから9バイトは以下のようになっています。

フレーム数 パターンID
08 00
08 01
08 02
08 03
FF

8フレームごとに0→1→2→3→0とループしていく、ということを意味します。

次に実データを見ていきます。まず各フレームごとの開始アドレス(3バイト*パターン数)が来ます。

ID0 EE5040
ID1 EE5048
ID2 EE5050
ID3 EE5058

1パターンごとにいくつのSNES4BPP画像を使用するかはループアニメーションインデックスの7バイト目で規定しています。この場合は4です。ターミネーターは存在しません。

ID0 22CE 22D7 22CD 22D6
ID1 3EE0 3EDA 3EDF 3ED9
ID2 3EE1 3EDC 3EDF 3EDB
ID3 3EE3 3EDE 3EE2 3EDD

これは8フレームごとにマップ中のSNES4BPP画像ID #$22CEのものを#$3EE0、#$3EE1、#$3EE3に置き換えていく、という事を意味します(残りも同様)。画像IDで指定しているので、反転した画像も対象になるようです。データフォーマットはスプライトと非常によく似ています。わかってしまえばそれほど難しくないわけで、ループ中に使われる画像も一緒に抜いてきて追加して上記のデータをポチポチとバイナリエディタで打っていけば移植完了です。データ量も大して大きくないので手打ちで乗り切りました。この手法はすごろく場のスライムのアニメーションにも使用されており、強エンカウントの画像をスライムからドラゴンに差し替えた時も同様のレコードを追加して対応しています。

*1:正確にはフレーム数ではなく恐らく1処理単位と思われるが便宜上フレームと呼ぶ