マリオが土管に入った時に何が起きているのか #phpconfuk

この週末は、福岡で開催されたPHPカンファレンス福岡 2016に遠征参加していました。
(長谷川のトークはこれですが、この解説は追って…)

この懇親会LTですごく面白いものがありました。
ファミコンのマリオが土管に入った時に、画面が暗転するけど、それはどうしてか、というお話です。

このお話、当時のコンピュータアーキテクチャの前提に依存するお話で、それを5分で、という事で、この頃のコンピュータに関する基礎知識を持っている自分は理解できたのですが、聞いてた人でも理解できなかった人も多かったのではないかと思います。

しかし、これは、みんな知ったら驚くに違いない!勿体なすぎる!ということで、解説エントリです。

このLTは資料が公開されているので、これをベースに解説しましょう。

ファミコンのスーパーマリオブラザーズのステージ開始画面。

黒い画面の上部に多くの情報と、真ん中に少しの情報が表示されています。このLTで説明されていることを理解すると

  • なぜ画面の多くが黒いか
  • なぜワールドの数字はそれぞれ1桁なのか

が説明できる様になっています。

ただ、前述のとおり、これにはいくつか前提事項が必要なので先にそちらをご説明します。

ブラウン管の表示の仕組み

ファミコン全盛期の頃、画面表示はブラウン管がその役割を担っていました。
ブラウン管では、画面の遠くにある電子銃から電子線(電子の連射)が発射され、それが画面に衝突することで光が発生します。

電子はまっすぐ飛ぶのでそのままだと画面の中心が光るだけです。ただ、飛んでいるものは電子なので磁力で曲げることができます。上の図の「偏向ヨーク」で磁力を発生させ、電子線を曲げます。この磁力をコントロールすることで電子線を画面の任意の場所に当てることができます。

この仕組みで電子を任意の位置に当てることができるのですが、あくまでも電子線は1本なので、画面上を左から右、上から下に「走査」して画面を表示しています。(この「走査」によって見える線を「走査線」と呼びますね。)

前述のとおり、ブラウン管では電子銃から発射された電子線で左から右、上から下に走査して画面を構成します。
このとき、右端まで到達した電子線を、1ライン下の左端に戻す必要があります。これを「水平帰線」と呼び、それにかかる時間(期間)を「水平帰線期間」と呼びます。

今時のコンピュータでも「H-SYNC」という言葉を聞くことがありますが、これは水平帰線のタイミングで発生する割り込み(トリガの様なもの)です。

また、右下まで捜査したら、電子線を左上に戻す必要があります。これは「垂直帰線」と呼び、それにかかる時間(期間)を「垂直帰線期間」、そのタイミングで発生する割り込みを「V-SYNC」と呼びます。

さて、当時のコンピュータでは、電子銃が走査している時に画面の書き換えを行うと画面に乱れが出るため、画面の書き換えは水平帰線期間か垂直帰線期間にする必要がありました。このうち、水平帰線期間は非常に短く、CPU命令を数命令実行するぐらいが限界です。一方、垂直帰線期間は比較的長いので、垂直帰線期間がメインの画面書き換えのタイミングです。

余談1:
画面を走査する時に、1回走査が終わったら次の1回は電子銃を0.5本分下に移動して走査し、次の走査では元に戻す、つまり、1回ごとに0.5本分画面をずらして表示することで、見かけ上、倍の解像度がある様に見せる表示方法もあります。この様な方法はインターレース方式と呼ばれます。
一方、対して特にずらさずに表示する方式はプログレッシブ方式と呼ばれます。

テレビ番組の最後のスタッフロールなど、左右方向の動きの速い動画を今時の液晶画面などに表示した時に縦線がギザギザになることがありますが、これは、インターレースからプログレッシブに変換(I/P変換)する時に、1回目の走査と2回目の走査で本来は時間差があるものを1枚の絵に合成しているためです。

余談2:
「テレビが消える時みたいなエフェクト」というのがあります。画面が表示された状態から全体がタテに縮んで上下中央の1本線になり、その1本線が左右からヨコに縮んで中心の点になり、消えていく様な。

これはブラウン管に電力供給がなくなって、電子線を操作するための磁力が無くなり、走査できなくなって電子線が中央に集まっているのが画面に映っています。

マリオが土管に入った時に何が起きているのか

さて、スーパーマリオブラザーズのステージ開始画面の説明のためには、まだ必要な前提事項があるのですが、ここまでの説明でも今回のエントリのタイトル、「マリオが土管に入った時に何が起きているか」は説明できます。

ファミコンの画面は8 x 8ピクセルのキャラクタを敷き詰めて表示しています。ファミコンの画面は256 x 240ピクセルなので32 x 30キャラクタ分ですね。
そして、上記スライドにも書かれていますが、比較的長い垂直帰線期間(Vertical Blanking)でも、ファミコンでは約32キャラクタ分しか描画することができません。つまり、垂直帰線区間には横1列分のキャラクタを描画するのがやっと、という訳です。

ここでカンの良い方だと、「ドラクエなどの全画面のスクロールはどうやっているのだろう」「垂直帰線期間には横1列分しかキャラクタを書き換えられないのに」と気付くかもしれません。その気づきは鋭くて、まさにその通りです。
ただ、ファミコンは画面をスクロールさせることができるので毎回画面全体を書き換える必要はありません。
例えば下から上にスクロールするゲームであれば、「少しずつスクロールさせて8ピクセルスクロールしたら、次に画面内に入ってくるところに、(垂直帰線期間に)1列キャラクタを描画する」とします。この様にすればスクロールのたびに画面全体を書き換えなくても、ゲームに必要な画面を描画することができます。

この方法で、ほとんどのケースは画面描画に困らないのですが、唯一困るのが「マリオが土管に入った時」です。このケースでは、「地上ステージの画面」から「地下ステージの画面」へと画面全体を書き換える必要があります。

この様な、画面全体の再描画を実現するために、ファミコンが持っている「画面表示をOFFにする」機能を使い、「地下ステージの画面」を描画し終わるまで画面表示をOFFにして、背後でゆっくり画面を描画しているのです。

これは「マリオが土管に入った時」だけでなく、「ドラクエで階段を降りたとき」も同じです。ファミコンで場面転換が発生するときは、一定時間の暗転が必要、ということですね。

という訳で「マリオが土管に入った時に何が起きているのか」への返答としては「画面を書き換えている」となる訳ですね。

さて、これでこのエントリのタイトルまではたどり着きました。
でも、せっかくなのでスライドの5ページ目まで行きましょう。

余談:
「場面転換が発生するときは、一定時間の暗転が必要」ではあるのですが、あくまでも「全画面を一気に書き換えられない」だけなので、「少しずつ書き換えるけどその過程を演出として見せる」実装もあります。

この動画はファミコンのファイナルファンタジーです
冒頭にプレイヤーが城に入っていますが、これは書き換えの過程を見せている例です。
前述の「画面表示をOFFにする」は垂直帰線期間だけでなく、水平帰線区間でもできるので、この城に入る時のエフェクトは、フィールド画面から暗転、暗転から城の内部の描画までは画面を書き換えながら徐々に描画範囲を広げているのでしょう。

上のファイナルファンタジーの動画では、城に入った直後にボスのいる建物にプレイヤーが入り、このときは一気に画面を書き換えている様に見えます。でも、ハードウェアの制約としてそれはできないので、別のテクニックを使ってこれを表現しているのだと思います。

ファミコンではキャラクタに使える色数が「52色中3色+透明」で、この「3色」の組合せをパレットと呼び、任意のタイミングで切り替えることが出来ます。
今回の例では、おそらく、パレットを「全て白」で指定した状態で城の中に入り、ボスのいる建物にプレーヤーが入った時に本来のパレットに切り替えているのではないかと思います。

スプライト

ではスライドの5ページにたどり着くための前提事項を解説します。

この画面では、マリオの周辺に背景が透けています。

mario-large

この「透けさせる処理」、ピクセルごとに重ね合わせ処理が必要になるので、結構重い処理です。そのため、この頃のハードウェアには「スプライト」という仕組みが用意されています。スプライトを使うと、CPUで「透けさせる処理」をしなくてもハードウェアで処理されます。

ただ、スプライトには通常、表示数に関する制限があります。
ファミコンの場合は以下の感じです。(細かいところは割愛しています)

  • スプライト1つは8 x 8ピクセル。
  • 色数は前述のキャラクタ同様、「52色中3色+透明」
  • スプライトは画面上に64個まで表示可能。
  • スプライトは横に8個まで並べて表示可能。*1

この *1 が制約としては一番厳しい制約になります。
この制約は、水平帰線区間に処理をしなければならないという制約と関連しており、ファミコンに限らず、ゲームボーイ、MSXなど、他の機種のスプライトにも同様の制約があります。

余談:
ドラクエ3では、プレーヤのパーティや町の人を、8 x 8ピクセルのスプライト4つ(16 x 16ピクセル)で表現しています。
プレーヤのパーティは最大4人なので、これだけで、横にスプライトが8個並んでしまうことになり、これ以降は横にスプライトを配置しても表示されません。
そのため、町の中などで、主人公のパーティ4人が横に並び、さらに町の人などが横に並ぶと町の人を表示できません。
ただ、まったく表示されないとゲームの進行上困るので、横に9個以上のスプライトが並ぶ時は、短い時間で順番に非表示にしていました。

これが、ファミコンのゲームで横にキャラが並ぶとチラつく理由です。そして、できるだけそのチラつきを抑えるために、ドラクエ4の仲間は馬車に乗り、グラディウスのオプションは2つになってしまったのですね。

スーパーマリオブラザーズのステージ開始画面

さて、これでこの画面を説明する要素が揃いました。
この画面はタイトル画面でスタートボタンを押した直後に表示される画面なのですが、おそらく以下のような処理をしています。

  • 画面上部2行に情報を表示する。
  • 画面上部2行目の表示が終わったら、(スプライト以外の)画面表示をOFFにする。 *2
  • 1行目の「WORLD」「1-1」、2行目のマリオと「x 3」をスプライトで表示する。

*2 では、全体の操作としては「画面をOFFにする」なのですが、この時点で画面上部2行目までは表示が終わっている(走査が終わっている)ので、結果としてはオフが行われた以降の画面だけが非表示になります。

我々がキャラクタだと思っていた文字はスプライトだったのですね!
びっくりでした。

そして、1行目は「WORLD 1-1」と、ぴったり8文字なので、これ以上文字数が増えるとスプライトの制限で消えてしまう。
つまり、「10-1」とか「1-10」という表示はできないことになりますね。

余談:
このロジックは推測ベースで、検証はなかなか難しいです。

動画サイトなどに上がっている30fpsの動画を見ると、タイトル画面からスタートボタンを押した時の画面の遷移は、タイトル画面 → 全面空色 x 1枚 → 全面黒 x 2枚 → ステージ開始画面、になっています。
この間に垂直帰線期間が4回あるので、その中でパレットの変更、上部2行の描画、スプライトの配置をしているのでしょう。垂直帰線期間2回は確実に「上部2行の描画」で使ってしまいます。

画面全体を書き換えるには垂直帰線期間20回分(約0.8秒)ほどかかる、つまり、この画面遷移でタイトル画面を消去することは出来なそうなので、この「全面空色」「全面黒」は画面表示をOFFにした状態で合っているとは思います。

ただ、可能性として、画面をOFFにしているのは「上2行が描画し終わった場所から、WORLD 1-1の上まで」という可能性もありそうです。この場合、「WORLD 1-1」と「x 3」の表示、マリオのスプライト表示は、画面をOFFにしている間にしていることになります。

ですが…どなたか正解をご存じの方、いらっしゃったら是非教えてください!
と言いつつ、コードを読むか、当時の中の人の降臨を待つか、しか、正解にたどり着く道は無い様な気がします。

まとめ

という訳で、「マリオが土管に入った時に何が起きているのか」および「スーパーマリオブラザーズのステージ開始画面では何が起きているのか」というお話でした。

最近のプログラムは最近のプログラムでいろいろ工夫が必要ですが、ファミコン時代のプログラムにこんな苦労があるとはびっくりでした。

こんな面白いトークが聞けるPHPカンファレンス福岡を用意してくださったスタッフのみなさん、スピーカーのみなさん、ありがとうございました!
そして、PHPカンファレンス福岡 2016の懇親会会場で8bitトークに付き合ってくださった皆さま、ありがとうございました。むっちゃ楽しかった!!

ということで興奮気味にPHPカンファレンス福岡の(偏った)参加レポートでした。