PPU > スプライト

概要

SNESには128個の独立したスプライトがある。 OAM (Object Attribute Memory) にデータを書き込むことで 表示する画像や座標の設定をする。

OAM

OAM全体は544バイトで構成され、 512バイトの下位テーブルと、32バイトの上位テーブルに分けられる。 双方共に128項目を入れることができる。 OAM は 0x2102 レジスタでアクセスするアドレスを決め、 0x2103 のビット 0 でテーブル選択を行い、 0x2104 を通じて書き込むか、もしくは 0x2138 から読み込まれる。 上位テーブルは32バイト分しかなく、0x2102 の下位 4 ビットのみが テーブルのインデックスとして使われる。

内部のOAMアドレスは、スキャンラインの描画中に無効になる。 無効化は決定的に起こるが、どのようにして決定されるのかは分からない。 OAMアドレスに対して何か操作がある瞬間か、 スプライトが現在のスキャンラインに現れる瞬間が 無効化の決定に関係しているのだろうと考えられている。 内部OAMアドレスはブランク強制期間中でない場合、 V-Blank 開始時に 0x2102/0x2103 からリロードされる。 0x2100 のビット 7 が 1 から 0 に変更された時にも リロード処理は発生する。

1バイトの読み込み/書き込み時にアドレスはインクリメントされる。 (内部アドレスは10ビット分あり、ビット9がテーブル選択、 ビット0~8がテーブルのインデックスを表す。) 読み込み動作は、そのまま今のバイトを読み込む。 下位テーブルに対する書き込みは、ワードサイズのバッファに行き、 上位バイトも追加で書き込まれた時に、OAMの適切なワードに書き込まれる。 読み込み、書き込みが交互に起こった場合、ワード値の 上位バイトが書き込みの代わりに読み込まれることになり、 書き込み内容はOAMに反映されなくなる。 このように交互に動作させると、書き込みは上位バイトにのみ行われ、 その上、下位バイトにはゴミデータが書き込まれてしまう。

順を追って説明すると、

  • OAMを全て0にして始める。
  • 1 を書き込み
  • 読み込み
  • 読み込み
  • 2 を書き込み
  • 読み込み
  • 3 を書き込み

この動作の時、 OAM : 01 00 00 02 00 03 のようなデータが書き込まれると予想することができるが、実際には

OAM : 00 00 01 02 01 03 のように書き込まれる。

上位テーブルへの書き込みの方は期待通りに動作する。

項目フォーマット

下位テーブルの1項分目のフォーマットは次のような4バイトで構成される。

OBJ*4+0: xxxxxxxx
OBJ*4+1: yyyyyyyy
OBJ*4+2: cccccccc
OBJ*4+3: vhoopppN

上位テーブルの1項目分のフォーマットは次のような2ビットで構成される。

bit 0/2/4/6 of byte OBJ/4: X
bit 1/3/5/7 of byte OBJ/4: s

記号の詳細

  • Xxxxxxxxx : スプライトのX座標。 符号については下記参照。
  • yyyyyyyy : スプライトのY座標。 0 ~ 239 の時は画面内に表示される。 画面から隠れた部分の頭の部分が -1 ~ -63 の位置にある時、 画面の上からスプライトが出現する。 とても大きいサイズのスプライトの場合、 下の端が隠れた時にも画面の上に戻ってくる。
  • cccccccc : スプライトの最初のタイル。 VRAMアドレスの計算方法は下記参照。 これは 'rrrrcccc' とも考えることができ、 16x16 サイズのキャラクタテーブルの行 (row) と列 (column) を指す。
  • N : スプライトのネームテーブル。 VRAMアドレスの計算方法は下記参照。
  • ppp : スプライトのパレット。 最初のパレットの位置は 128 + ppp * 16
  • oo : スプライトの優先順位。 詳細は下記参照。
  • h/v : 水平/垂直反転フラグ。 反転処理は個々のタイルに対してではなく、スプライト全体に及ぶ。 しかし、2つの正方形のスプライトから構成される 長方形のスプライトの場合は、垂直に反転する。 (つまり、"01234567" の行の場合、 "76543210" ではなく、 "32107654" となる)
  • s : スプライトのサイズフラグ。 詳細は下記参照。

スプライトのサイズは 0x2101 のビット 5 ~ 7 と OAM の サイズフラグで調節する。 0x2101 では全てのスプライトのサイズを調節する。 OAM サイズフラグが 0 の時は小さい方のサイズが、 1 の時は大きいほうのサイズが選択される。

パレット

スプライト用に、16色のパレットが8セット用意されている。 それらは CGRAM のインデックス 128 から始まる。 OAM で指定される 'ppp' は、 (128 + ppp * 16) ~ (128 + ppp * 16 + 15) の範囲を指す。 長方形でないスプライトの場合、パレットの最初のエントリは透明色を表す。

スプライトのパレット 4 ~ 7 は色計算と共有される。

VRAM上のキャラクタテーブル

スプライト用に 2 つの 16x16 サイズのキャラクタテーブルが VRAM に用意されている。 これらは BG タイルマップと同じように動作する。 タイル 0x00 は、タイル 0x0F の右側のタイルで、タイル 0xF0 の下側にある。 タイル 0x10 は、タイル 0x00 の下側のタイルで、タイル 0x1F の右側、 タイル 0xFF は、タイル 0xF0 の左側のタイルで、タイル 0x0F の上側にある。 どのキャラクタテーブルを使用するかは OAM の N ビットで設定される。 16x16 のサイズを使って、タイル 0xFF を指定した場合、キャラクタは 0xFF, 0xF0, 0x0F, 0x00 で構成されることになる。 最初に、テーブルは 0x2101 のネームベースビットで指定され、 次に 0x2101 のネームビットでオフセットが指定される。 VRAM 上のスプライトの最初のタイルのワードアドレスは次の式で表される。

((Base<<13) + (cccccccc<<4) + (N ? ((Name+1)<<12) : 0)) & 0x7fff

キャラクタデータの詳細は BG 参照。

スプライトの優先順位

スプライトには優先順位についての考え方が2種類ある。 1つは OAM の優先順位ビットで、BG に対してスプライトの 表示順位を調整する。詳しくは BG 参照。

もう1つは、スプライト同士の間の表示順位で、 これはスプライトのインデックスと優先順位ローテーションによって調整される。

優先順位ローテーションは 0x2103 のビット 7 でセットする。 このビットが 0 の時、スプライト 0 が最初のスプライトになる。 1 の場合、現在の内部 OAM ワードアドレスから (OAM アドレス無効化処理は影響しない) 、 スプライトに優先順位が設定される (OAMAddr&0xFE)>>1 。 0x2102/0x2103 に 0x104 が 4 バイトで書かれた時、 次のフレームでスプライト 3 が優先順位を得る。 しかし、OAM アドレスリセット によって、内部OAMアドレスは 0x104 にリセットされるので、さらに次のフレームでは スプライト 2 が優先順位を得る。

有名な1つの奇妙な動作 : 0x2102/0x2103 を 0xA に設定した時、 4n+2*(A&1)+1 バイトが書き込まれる。 (例えば、so the next byte written would go to the last byte in the 4-byte sprite record) sprite ((OAMAddr>>1)+Y)&0x7F has priority (where Y is the current line as addressed by sprites). Thus, if you put all 128 8x8 sprites at Y=63, write $8000 to $2102/3, then read 3 bytes from $2138, you will see sprites 63-70 having priority on successive scanlines.

"最初のスプライト"は、OAM の優先順位ビットの設定に関わらず 他のスプライトよりも前に(一番前に)表示される。 "最初のスプライト" + 1のスプライトは "最初のスプライト" + 2の前に、 "最初のスプライト" + 2のスプライトは "最初のスプライト" + 3の前に表示され、 これは "最初のスプライト" + 127 まで続く。 (127から0にラッピングされる。) 一番前のスプライトのみがBGに対する優先順位を設定できることに注意して欲しい。 "最初のスプライト" + 3 と "最初のスプライト" + 4 は交換可能だが、 "最初のスプライト" + 3 が優先順位 0 、"最初のスプライト" + 4 が優先順位 3 に設定されている時、両方のスプライトがBGの裏に隠されてしまう。 "最初のスプライト" + 4 が BG の前面に来るのが普通の考え方で、 直感に反することのように見えるが、たくさんのゲームがこの挙動に従って動いている。

スプライトの描画

スプライトはスキャンライン毎に描画される。

  1. OBJ が X=256 の時 (X=-256)、X=0 の時と同じように"範囲"と"時間"を考慮する必要がある。 注 : 実際にX=0に描画するわけではない。
  2. 範囲 : スキャンライン中の、 "最初のスプライト" から、32 個の最初のスプライトが描画用に選択される。 これらのスプライトは、 -size < X < 256 に入っている中から選ばれる。 1つのスキャンライン内に 32 個以上のスプライトがあった場合、0x213e のビット 6 がセットされる。
  3. 時間 : "範囲"の中の最後のスプライトから考えて、8x8 サイズで 34 個のタイルが選択される。 (左から右へ、反転(フリップ)後の) 34 個以上のタイルがあった場合、0x213e のビット 7 がセットされる。 これは、-8 < X < 256 の範囲の中から選ばれる。
  4. "範囲"と"時間" により、タイルと本当のX座標が決定され、パレットと優先順位を考慮して描かれる。 (X=256,X=-256 のものは X=0 には配置されない。)

詳細は PPU/画面のレンダリング を参照

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2017年08月28日 13:27