feature image

2018年11月25日 | ブログ記事

スーパーマリオ64学入門【アドベントカレンダー2018 32日目】

※この記事はすごく長いです(半月程前に見たことある注意文、11000文字くらい)
・所々省いて説明しているところがあります(10日くらい前に見たことある注意文)

みなさんこんにちは!おそらく初めまして!18のEruです。本業はPです。担当?実質全員。シンデレラ6thメラド最高でした(名古屋も楽しみ~、ナゴド2日目現地なのでお願いします)。先日HOTCHPOTCH FESTIV@L!!とGLORIOUS ST@GE!のBDが自宅に届きました(最高)。初星演舞届くの待ってます。ミリオン福岡2日目受かってました(CD積みたくないなぁ)。シャニマスは全部落ちました(ゲーム先行?期待したら負け)。金がないです(切実)。

ところで今日でAdCも32日目だそうで。AdCは一般に12月1日に始まるようですね。ってことは早いものでもう新年が明けてしまっているようですね。皆さん、あけましておめでとうございます。本年もよろしくお願いいたします。

さて32を2倍すると64、ということで皆さんは『スーパーマリオ64』(以下マリオ64)という作品をご存知ですか?1996年に任天堂から発売された3Dアクションゲーム(ボールを相手のゴールにシュゥゥゥーッ!!する超!エキサイティン!!なゲームではない)です。所謂3Dマリオシリーズの第1作目に当たりますね。私、生まれるどころか受精卵ですらない

マリオ64は全世界で最もRTAプレイヤーが多いゲームです(speedrun.com調べ)。これは累計プレイヤー数であってもアクティブプレイヤー数であっても最も多いです(speedrun.com調べ)。日本人も多くの人がこのゲームのRTAをしています。未だにプレイヤー数の多いマリオ64ですが、やはり昔のゲームなだけはあり、バグが多いゲームです。だからこそ、多くの人がいまだにプレイしているのかもしれません。

このブログではこの色々なバグがゲーム内のどういうシステムによって生じるのか、いくつか解説したいと思います。あわよくば皆さんが3Dゲームを作るときの参考になればよいなと。

以下で解説していることは全て『スーパーマリオ64DS』では再現不可です。また一部は振動パック対応版及びVC版『スーパーマリオ64』で再現不可です。
それとマリオ64(DS版、VC版でも構わない)を遊んだことがあったり、プレイ動画を見たことがあったりでマリオ64をかすかにでも覚えていると読みやすいと思います(こんなのあったなあ、言われてみればあった気がする程度で構いません)。


ケツワープ

おそらくマリオ64を知っている人にとっては馴染み深いバグだと思います。その見た目がマリオのケツの力で大きな速度を得ているように見えることからケツワープの愛称がついていますが、このバグは一般にBLJ(Backwards Long Jump)とよばれます。和訳すると後ろ幅跳びといったところでしょうか。残念ながら、振動パック対応版とVC版ではこのバグは使用できません。

このBLJの主な使い道はフレーム単位(マリオ64は30fps)で行われるオブジェクトの判定を抜けることですね。勿論、その速度を使って高速移動をすることもできます。
下は実際にBLJを行って壁抜けをしている動画です。一瞬映るスターが描かれた扉を通過しています。あ、音は入ってないので外でご覧の方も突然爆音が流れる、なんてことはないのでご安心を。動画時間も短いので、通信料を食うこともないかと。(gifにしようかと思ったんですが、gifは流れ続けるので視界に迷惑かなと思いました。)

ではどうしてこのような現象が起きるのでしょうか。

BLJの原理

マリオ64では幅跳びのスピードが走る速度よりも速く、約1.5倍の速度になります。なので、幅跳びの着地の際にマリオは減速します。この時システム上では大きさが着地直前の速さの約5割である負の速度(マリオの向いている方が正)がマリオに足されているのです。それを踏まえて考えてみましょう。さて幅跳びで後ろ向きに着地するとどうなるでしょうか。
後ろ向きで着地する直前マリオは負の速度を持っています。そして着地すると元々マリオが持っている負の速度に加えて負の速度が足されます。つまり、マリオの負の速度が大きくなるということです。
さらに着地直後に幅跳びをすると、初速が負の速度のマリオが幅跳びをすることになります。飛んでいる間、後ろ向きに進むよう入力すると、着地する直前には飛び始めた時よりもさらに大きな負の速度を得ます。そして着地した際にさらに負の速度を得ます。これを繰り返せばマリオが非常に大きな負の速度を得る、というのがBLJの原理です。

負の速度で着地することが想定されていないこととマリオの速度に下限がないことが原因でしょうか。
因みにマリオの速度の上限ですが、動作によって異なります。走っているときで約31.5、幅跳びや飛び込みが約49、壁キックに関しては上限がないです。下限がないとは言いましたが、マリオの速度はfloat型なので一応それの下限はありますね、10^38ですが(これがTASVideosによると、どうやら-3.402823 10^38ではなく-2.2939977225680862 10^38らしいのです。詳しいことはよくわかりません)。あ、ここまでの速度は全てXZ平面(水平面方向)での速度です。

ちょっと寄り道ですが、Y方向(鉛直方向)の速度(正確には空中での速度)についても書いておきます。Y方向の速度は地面に足がついていないときで1フレームに4ずつ減少します。地面の上ではたとえ斜面であっても上下に動くリフトの上であっても変化しません。また、Y方向の速度には自由落下時の場合、下限が存在し、その値は-75です。一方で上限はないです。というのもY方向の速度はジャンプなどでしか得ることはできないので、大きな値を手に入れるはずがないからです。例えば各ジャンプでのY方向の初速は1段目が42、2段目が52、3段目が69、バック宙や横宙返り、壁キックは62です。

ついでに面白い話をしておくと、VSC(Vertical Speed Conservation)と呼ばれる現象があります。これは垂直方向、つまりY方向の速度について、一部の動作で着地した際に速度が0にならず保持されるというものです(着地後実際にその速度で動いてはいません)。一部の動作とは飛び込みからの復帰やヒップドロップ、滑り込み等です。そして、Y方向の速度は歩いたり走ったりしてしまうと即座に0になってしまいますが、パンチをした際には0になりません。さらにパンチはマリオを少しだけ前に進めます。パンチで少しづつ進みながらVSCの状態で床の端まで来るとそのVSCによるY方向の速度に従った行動をマリオがとります。こんな感じですね。

動画の左上のSPDというのがY方向の速度です。このようにもしVSCが正ならば、ジャンプをせずともマリオを宙に浮かせることができるのです(普通にジャンプしろよ、というのはそれはそうです)。

速度の保持と言えばもうひとつ面白い話を。水中の速度は地上の速度とは別で管理されているので、水に入った瞬間の地上の速度が水中でずっと保持されます。

動画の左上のSPDは今回はXZ方向の速度です。確かに水中では速度が変化していません。なので最後に初速100で柱を駆け上がることができます。途中で87から100へ速度が増えていますが、これは水面が滑る斜面と接しているとき、その水面と斜面の接している部分をなぞるように移動すると速度が増えていくためです。
他にもBLJで大きな速度を手に入れた後、水中に入り別の場所で陸上に上がると、BLJの速度が残っているのであとはお察しの通りです。

階段なのにBLJができない!?

話を戻して(流石に脱線しすぎました)、もっとBLJを詳しく見ていきましょう。このBLJは段差や坂などの斜面になっているところであればどこでもできます。有名なのはRTAでよく見られる3階に昇る階段でのBLJと最後のクッパコース「てんくうの たたかい!」(BitS)へ続く無限階段でのBLJですね。先ほどの動画は3階へ昇る階段でのBLJです。

ところでマリオ64には地下へ続く階段がありますが、ここでBLJをしている人はいません。ここでBLJをしなければ抜けられない扉が特にないため、見かける機会がそもそもないに等しいのですが。にしても誰もやっていないのはどういうことなのでしょう。
と、ここまで引っ張っといてなんですが副題の通り、やってないのではなくそもそもできないのです。

地下へ続く階段と3階へ続く階段を比較してみましょう。その段差の1段ごとの高さと横幅は下の通りです。青が床、緑が壁として判定されます。
Comparison_stairs_1
ここに書かれている数字ですが、特に単位とかはないので比較参考としてマリオについて書いておきます。マリオの見た目の高さは144で、マリオはの当たり判定は半径37、高さ160の円柱です(高さ160以下の空間は通れません)。この円柱の底面の中心がマリオの座標位置になります。しゃがむと当たり判定の高さが100になります。ジャンプの高さは1段目が242、2段目が364、3段目が630、バック宙や横宙返りは512です。また、最高速度での幅跳びの飛距離は1530です。ちょっと前に出てきたマリオの速度ですが、これは1フレームあたりに進むこの値の量です。

話を戻して。マリオは壁にぶつかると壁から跳ね返されます。一般的に壁との距離が50以下になるとマリオは壁から跳ね返されます。跳ね返りを受けるとマリオはそれ以上進むことができません。正確には動いたときに同時に壁から離れる方向のベクトルを受けます。例えば、跳ね返りを受ける壁に飛び込むと反動を受けて、少し後ろに戻りますね。
階段の判定は異なります。もし上の通りならば階段を上るとき、1段毎に跳ね返りを受けてしまって、いつまでたっても登れませんからね。

では階段の壁から跳ね返される場所はどうなっているのでしょうか。実際には4種類の判定が存在するのですが、今回は直接マリオと関係ある1種類のみを示します。
Comparison_stairs_2-1
この判定枠は壁から下に30下がった位置に、左右に50ずつ広がっています。見てわかる通り、地下へ続く階段の方は跳ね返りの判定枠が階段から飛び出しています。地下へ続く階段の1段毎の床幅が38しかないために、50ずつ広がっている判定枠が飛び出してしまっているというわけです。もう分かることですが、この階段では壁にぶつかって着地した際に負の速度がなくなってしまうのです。

この階段では他にもこの飛び出している判定のせいで、その場で上にジャンプすると後ろに下がってしまったり、ボディアタックで飛び込むと壁にぶつかった判定で押し返されてしまったりします。実機をお持ちの方はぜひ他の階段と比べてみてください。でも、城内1階の真ん中の階段は見た目は階段でもシステムでは坂道なので比較には向いてません、あしからず。



HOLP

HOLPとはHeld Object's Last Placeの略です。和訳すると「最後に物体(オブジェクト)を持った位置」ですね。HOLPはマリオがオブジェクトを放つときに固定される座標です。マリオがオブジェクトを持つと、マリオの手の位置にHOLPが現れます。マリオがオブジェクトを持った状態で動くと、当然マリオとともにHOLPも移動します。そしてマリオがオブジェクトを投げると、マリオとともに一緒に移動していたHOLPはその座標で固定され、そのHOLPの位置から、持っていたオブジェクトが放たれるという仕組みです。固定されたHOLPは、次にマリオが放てるオブジェクトを持つまで変わることはありません。

物を持つ

物を持つとき、マリオは確かにそこにあったものを持っているように見えます。が、マリオが持っているオブジェクトは持つ前のオブジェクトとは異なるものです。マリオは見た目は同じオブジェクトを持っているというわけです。では、持つ前にそこにあったオブジェクトはどこへいってしまったのでしょうか。

正解は「どこへもいっていない」です。オブジェクトはその位置にあるのです。ですが、当然見えませんし、触ることもできません。このような見えない、触れないオブジェクトがいる領域をlimboと呼ぶことにします。limboは英語で中間の場所という意味です。

さてこのlimboですが、実はすべてのこのような見えない、触れないオブジェクトがlimboで動かずにいるわけではありません。例えば、limboのボムへいの座標は常にマリオの前に100上に60の位置に更新され続けます。あるいは子ペンギンを持つとlimboの子ペンギンの座標は常にマリオの位置に更新され続けます。下に持てるオブジェクトのlimboのオブジェクトの座標についてまとめてみました(Markdownの表は使い勝手が悪すぎる、Excelとかいう表作成ソフト最高)。バッタブロックは持つと跳ねて三回跳ねると破裂するブロックのことです。
limbo2

一方でマリオの持っているオブジェクトは何なのでしょうか。これは見た目が同じだけの借り物に過ぎません。実際のオブジェクトとの交換証に過ぎないのです。投げるときにこの交換証がlimboにあった実際のオブジェクトと交換され、HOLPからそのオブジェクトが飛び出してくるというわけです。正確には投げた際にはHOLPから前方向に32進んだ位置から飛び出し(マリオの手の動きに合わせるためです)、しゃがむなどして落とした際にはマリオと同じ高さにオブジェクトは出現します。持っているオブジェクトは交換証に過ぎないので例えば(実際に持つことは本来はできませんが)クリボーを持った際にダメージを受けることはありません。

HOLPを固定する

もっとHOLPを詳しく見ていきましょう。今回の目的は物を持ってもHOLPが移動しないことです。さて、マリオ64ではマリオが帽子を取得すると帽子をかぶる動作をします。帽子をかぶるためには帽子を手に持たなければなりません。勿論、マリオが帽子を投げることはできません。では、マリオが帽子をかぶる動作をした直後(システム上ではまだかぶる動作中となっている)にもう一つ帽子を取得したらどうなるでしょう。答えは下の動画で示されています。

お分かりいただけただろうか。ではもう一度、はいらないですね。マリオは手に帽子を持つ状態になるのです。完全にバグですね。ではこの状態で物を持った時、HOLPはどうなるでしょうか。

なんとHOLPがマリオの手の位置に移動してこないのです。帽子を手に持っていることが情報として先に来ていることが原因でしょうか。ではこの帽子を手に持った状態でボムへいを持ち、そして投げるとどこに飛ぶでしょうか。

それは帽子を手に持った状態になる前に物を投げた地点、つまり更新されていないHOLPです。これがHOLPの固定です。つまり、どこからでも物を投げさえすれば、帽子を手に持った状態になる前に投げた場所=HOLPへとそのオブジェクトを投げることができるのです。

動画では何も持っていないように見えますが、実際にはボムへいを持っています。これはマリオが手に帽子を持っているために見た目が同じオブジェクトをマリオの手に置けないことが原因です。また、マリオの少し前から煙が出ていますが、これがlimboのボムへいの座標における導火線の位置に等しいです。

因みにマリオ64では各コースを形成する際、同じ座標空間に別のマップやオブジェクトをロードして形成しているので、例えばコース1「ボムへいのせんじょう」で固定したHOLPをコース15「レインボー クルーズ」で使用することも可能です。

無を持つ

マリオがオブジェクトを持ったという判定をされてから、オブジェクトがlimboに送られるまでの間に約1フレームが存在します(ここで"約"としたのは前後することがあるからです)。では今にも爆発しそうなボムへいを持った際に偶然にもこの1フレームの間でボムへいが爆発してしまったとしましょう。ボムへいは爆発してしまっているのでオブジェクトとしてなくなったことになっていますが、オブジェクトを持った判定はされているのでマリオは今、交換証をもっています。これが無を持つ、ということです。あくまで無はnullではなく、交換証です。見た目は何も持っていないため無を持つと呼んでいます。さて、limboには今なにも送られていませんから、交換証の交換先が存在しない状態になっています。では交換証の交換先は何になるのでしょう。

実はマリオが手に持っている無は空いているスロットです。なので次にスロットに埋まったオブジェクト(正確には持っている空スロットに埋まったオブジェクト)の交換証がマリオの手に収まります。正確にはそのオブジェクトのクローンですね。クローンをマリオは持っているためマリオがその交換証を持っているにもかかわらず元のオブジェクトはlimboへ移動しないので、見えますし、触れることもできます。しかし投げた瞬間にクローンの元のオブジェクトも突如消え、HOLPから現れることになります。ただ、実際に消えているのはそのクローンなので、またロードし直すとそのオブジェクトは再度表れます。

投げたクローンは元のオブジェクトと基本的に同じ動きをします。が、投げられることが想定されていないオブジェクトは投げられた後、動くことはありません(ダメージ判定などはそのままです)。例えばクリボーを投げるとクリボーは宙で静止します。このクローンクリボーにぶつかると当然ダメージを受けますし踏んだり蹴ったりすればノックバックを受けます。しかしそのような反応はクローンクリボー一体につき一度しか受けません。つまり、一度そのクローンからダメージを受けてしまうとそのクローンを倒すことはできないのです。また、仮に踏んだり蹴ったりして倒したことにしてもクローンクリボーが消えることはありません(コインも出てきません)。

因みに持てるオブジェクトはボムへいのような投げられるオブジェクトに限らず、クリボー、コイン、クロマメ(現在の名称はケロンパ)が吐く炎、ちくわブロック、水しぶき、気泡、風、砂ぼこり、竜巻、渦潮、ボムへいの爆発で出てくる煙、ヒップドロップなどで出てくる星のエフェクト、あまつさえスターまでオブジェクトとしてコース内をプレイ中にロードされるものであれば何でも可能です(一部オブジェクト(ワンワン(鎖は含まない)、ホルヘイ、獲得条件が特にないスターなど)はコースに入ったときにロードされるためクローンを作れません)。

この無の取得で最も使われるのはクリボーをロードして投げ(これは固定したHOLPへの場合もある)、クリボーによる階段のようなものを作り、本来ジャンプなしでは上れない段差を上ることです。クリボーは投げられることが想定されていないため、投げられるとその場で静止しますし、クリボーを踏むとマリオが反動で宙に浮くので、足場として都合がいいというわけです(ジャンプしろよそれはそう定期)。


PU

PUとはParallel Universeの略です。和訳すると平行宇宙ですね。PUは元の世界と比べると、構造は同じですが多くのデータが読み込まれていません。実際に読み込まれるのは足場と天井の判定くらいです。水や壁の判定(マリオ64で壁と判定されるものは鉛直方向からの傾きが数度で、それ以上傾くと天井か床と判定される)も読み込まれていません。

この動画のSPDはXZ平面での速度です。BLJで飛んだあと、マリオは確かに"何か"の上には乗っているのですが、肝心の"何か"が全く見えません。

そもそもPUってなんだという話をしていませんでした。マリオの座標はfloatで管理されてます。一方で、この世界の座標空間はshortで管理されています。ものすごくオーバーフローできそうですね。というわけで、マリオの座標をオーバーフローさせてみた結果生まれたのがPUです。
floatのマリオの座標がshortに変換された結果、床と天井の判定はマリオのshortの座標で行われているので、マリオは床の上に乗ることができ、天井に頭をぶつけることができます。しかし、オブジェクトの当たり判定などはマリオのfloatの座標による当たり判定の円柱(前述の半径37高さ160の円柱)で行われているので、敵に当たることもコインを取ることもできません。これがPUです。

さて、マリオ64では足場に乗っているか乗っていないか(コース内かコース外か)の判定を1フレームで4回行っています。正確には1フレームごとに移動距離を4等分したその全てで足場の上にいたかどうかを判定しています。この判定で一か所でもマリオが足場の上にいなければマリオは次の場所へ動きません。PUは最低でも約65536といいう大きな距離を移動しなければならないのでこの移動距離を等分した際に足場の上に乗っていないという可能性は十二分にあります。例えば2つPUを移動しようと思った際に1つ目と2つ目の間で足場がなければ、マリオは1つしかPUを移動することはできないというわけです。であれば4つPUを(つまり262144)移動してしまえば問題ないですね。この4つ分のPUをQPU(Quadraple Parallel Universe)と呼びます。

そもそもなぜPUを移動するのかという話ですが、例えば元の世界で壁一枚挟んだ先にあるスター(正規ルートで向かうとジャンプせざるを得ない)をとるためにPUを使用して遠回りをすることでその壁を突破することが可能であったりします(ジャンプしろ定期)。PUの移動では少しのマリオの動きで大きく座標を変化させられるため、時間は多少かかりますが壁をこえることなどは簡単にできるのです。他にも水の判定がないので本来立てない水の底へ行くことができ、そこから元の世界へ戻ると、水中判定がでるまで僅かに1フレームあるので、その1フレームで色々なことができます。

数値としての速度と実際の速度の差

先ほど説明した速度についてですが、実は少しだけ異なっています。XZ方向の速度ですが、この速度は坂道の角度によって実際の速度との差が生まれてしまいます。

ご覧の通り、同じ速度で走っているのにもかかわらず坂を歩いているときの方が明らかに遅いです。これが数値としてシステム内で管理されている速度と実際にゲーム内でマリオが出している速度の差です。なので例えば1QPU進むのに必要な速度が、平坦であれば262144前後(移動距離によって前後するので)で十分なのですが、坂であるとそれ以上の速度が必要になってくるのです。

例えば3つのQPUを経由して元に戻ってくることを考えてみましょう。1つ目のQPUに移動するためには坂が急で例えば450000近い速度がいるとします。2つ目のQPUに移動するときは平坦なので262144で足りるとします。3つ目のQPUに移動するにはなだらかな斜面なので334000くらい速度がいるとします。そして元に戻るにはやはりこれも平坦なので262144で足りるとします。
普通に移動してしまうと2つ目のPUに移動したときには速度が262144以下になってしまっています。これでは3つ目に移動できません。このような時は速度を倍して2QPU移動してしまえばよいです。つまり最初に速度を900000近くにして、2つ目の時点で524288であれば、3つ目への334000が得られます。こんな感じでPUの移動は行われます。



ABC

ABCとはA Button Challengeの略です。決してAtCoder Beginner Contestの略ではないです。いいですかみなさん、ABCといえばA Button Challengeです、ここテストに出ますよ。
話を戻して、ABCとはAボタンを極力使わないで、120枚のStarを全て手に入れようというものです。Aボタン禁止縛りだと思って構いません。

Aボタンでできるマリオのアクションはジャンプ、しゃがんでいるときでバック宙、方向反転時で横宙返りといったジャンプ全般及び壁キック、大砲を打つ、金網に捕まる、木や棒などに捕まる(これは飛び込みをすることで避けられます)、木や棒から離れる(これはダメージを受けるか一番下まで降りることで避けられます)、泳ぐです(多分)。これらを縛っているので段差を上ることすらままならないですね。飛び込みからの復帰が基本的に稼げる最高高度です。ここまでで何度かジャンプしろよ定期がありましたが、これはこのためです。

現在の全120枚StarにおけるAボタンの使用回数は20回(加えてAボタン押しっぱなしが2か所存在、これを動画制作者さんはAボタン0.5回と呼んでいます)です。この企画が始まったときは211回だったので結構減りました。今まで説明したことが使われている動画が多数あります。気になる方はこちらの再生リストから見ることができます。ぜひご覧ください。因みに動画説明欄に解説が書かれていたり、一部動画では動画内にて解説があったりするのですが、すべて英語ですのであしからず。



終わりに

これを書く際に動画を参考にさせていただき、また一部動画や画像を借りさせていただきましたUncommentatedPannenさん(旧pannenkoek2012さん)、並びにTyler Kehneさんをはじめとするスーパーマリオ64を研究している他の皆様、スーパーマリオ64のTAS/TAP制作者及びRTAルート構築者の皆様、本当にありがとうございます。


参考動画
Walls, Floors, & Ceilings Part 2
Walls, Floors, & Ceilings Part 3
Units, Speed, & Sense of Scale
JRB Blast to the Stone Pillar 0x A Presses
How Holding Objects Really Works
Releasing Objects
The Art of Cloning
SM64 - Mario Wings to the Sky - 0x A Presses
SM64 - Watch for Rolling Rocks - 0.5x A Presses (Commentated)


明日はsupmiku39さんとNocks_onさんです。明日のAdCも楽しみですね。



むずかしくてよくわかんなかった~(o・∇・o)







終わりだよ~



Eru
Eru icon
この記事を書いた人
Eru

文字解読とか譜面解析とかしてる人。最近耳コピまがいの動画が売れている。本業はプロデューサーなのに担当がいないとはこはいかに。

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

ERC20トークンを用いた宝探しゲーム(真)の提案【アドベントカレンダー2018 10日目】 feature image
2018年11月3日
ERC20トークンを用いた宝探しゲーム(真)の提案【アドベントカレンダー2018 10日目】
Azon icon Azon
2018年12月23日
LogicProXでのサラウンド設定,オーケストラ用テンプレ作成,その他の小ネタ
SolunaEureka icon SolunaEureka
2018年12月16日
ICPCアジア地区横浜大会参加記【アドベントカレンダー2018 52日目】
eiya icon eiya
2018年11月30日
Flutterでスマホアプリを作ってみ(た | よう)【アドベントカレンダー2018 37日目】
Fourmsushi icon Fourmsushi
2018年12月23日
線形解読法
nari icon nari
2018年12月3日
ハル研究所プログラミングコンテスト 2018に参加しました[アドベントカレンダー2018 40日目]
ninja icon ninja
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記