タイマーバグ

Last-modified: 2025-12-02 (火) 19:52:48

概要

Ver1.1.1にて確認されたバグ。
「エアライド」モード、ならびに「ウエライド」モードの「フリーラン」において、開始からの時間経過とともに、ゲーム内の時間の流れ(タイマーの表示)が実時間の流れに対して変動するというもの。
ただし、タイマーの流れる速さが変動するだけで、マシンの速度、同じ操作をしたときに進む距離などは一定のままであるため、時間の流れが遅くなればマシンの動きは相対的に速くなり、逆もまた然り、という問題が生じる。
これによって意図的に「時間の流れがかなり遅くなる状態」にして、プレイヤーの実力よりも優れた(=タイムが短い)記録を出すことが可能となっている。
おそらく両モードの「タイムアタック」や「シティトライアル」の「ドライブ」などにおいても同様の現象が起きているものと想定されるが、以下に記すような事象であるため大きな問題となるのは現状「フリーラン」のみである。

確認されている事象

時間の流れる速さの変化

フリーランをはじめ本ゲーム内には時間表示(タイマー)が各モード内にて表示されている。
このゲーム内時間(IGT:In Game Time)が示す「1秒」は疑似的に実時間の1秒に概ね合うはずだが、この「IGTの1秒」が「実時間の1秒」に対して速くなったり遅くなったりすることが確認されいてる。
※本質的には実時間とのずれは大きな問題ではなく、時間経過とともに同一ゲーム内で時間の流れる速さがぶれることが問題あることに注意。

上記の通り操作に伴うマシンの挙動自体には一切影響を与えないので、時間の流れが遅い時は相対的にマシンが速く動くと捉えても問題ない。
(通常より10%時間の流れが遅い場合、計測される記録は 1×{100/(100-10)}=111.11% つまり、11%速いタイムが記録されるようなこと)
厳密にはフレーム単位で細かくぶれが生じたりはしているものの、大きな傾向として一定期間は同じようなずれになるため、初期状態を「モード0」として、以下モード1,2,3...と仮称することとした。
各モードでは下記のような時間変動になることが理論と検証によって確認されている。概ね、時間が経てば経つほど速いほうにも遅いほうにも変動は大きくなると理解しておくとよい。

モード開始時刻[秒]時の流れn倍速(%)実質速度上昇率備考
00100%0%初期状態 ほぼ一定(厳密には100秒に1F程度のぶれはある)
11024100.3%-0.3%開始から約17分、タイムは僅かに遅くなる
2204899.7%+0.3%開始から約34分、タイムは僅かに速くなる
316384105.5%-5.2%開始から約4時間半、タイムはかなり遅くなる
43276893.75%+6.7%開始から約9時間、タイムはかなり速くなる
5262144187.5%-46.7%開始から約77時間、世界は倍速で動き始める
65242880.0%+∞%開始から約116時間、世界は静止する

要は9時間放置すると記録を作る上において最強環境ができあがる。悪用厳禁。
特にモード4などはフレームごとの下二桁表記を見れば簡単にわかるので、知っててやるのは自分の首を絞めるだけになるため絶対にやらないこと。
また、開始時刻が固定されている場合実時間とのずれは生じるものの、全プレイヤーが同じ条件でプレイできることになるため、タイムアタックやシティトライアルでは大きな問題とはならない。

保存される記録

当然ながら、ゲーム内に保存される記録はゲーム内時間:IGTを用いるので、それぞれの時間の流れの中でのIGTによって記録される。
モード3で記録を作れば遅くなるし、モード4で記録すれば速くなる。
マシンごとにコース記録が残る今作では、モード4などで最速記録を作ってしまった場合、今後のアップデートによってタイマーバグが解消された場合、二度と更新できない記録が残ってしまう可能性があるので注意。(現状消せないわけではないが、他コースの記録もまとめて消すしかない)

ゴーストの速度変化

ゴースト(ベスト軌跡)の記録は、(コース内座標,同LAP内経過時間)といったような要素で保存されているようである。
要するに、そのLAPを開始してからIGTで何秒経った時にどこにいるのか、というのを記録し連続的に描画しているに過ぎない。
異なるモードで記録されたゴーストも、現在プレイしているモードでの時間経過に即してゴーストが表示されることになる。
よって、たとえばモード4で記録した最速ゴーストを「やりなおす」からモード0で追いかけるとまず追いつかない。逆にモード0~3で作ったゴーストはモード4になった瞬間に容易く追い抜くことが可能になる。

なぜフレーム管理しなかったのか…?

Speedriderでの扱い

これに伴い、2025/11/30時点ではSpeedriderではフリーランの記録受付を停止している。
今後のアップデートによる改善、もしくはアップデートが見込まれない場合はレギュレーションの制定を待たれたい。
それでもフリーランを行う場合、桜井氏の言うように「自身との闘い」として取り組むようにしよう。

原因

前提として『カービィのエアライダー』は60FPS(1秒間に60枚の画像を表示することで映像として見せる)のゲームである。
したがって、ゲーム内時間(IGT:In Game Time)を疑似的に実時間と合わせて表示するためには、IGTの1秒を60分割して1秒以下の単位を描画することで、60Fで1秒進んだことにするという手法をとる必要がある。(これ自体は本ゲームに限ったことではなく、一般的な話である)
このIGTは「タイマー」という形でフリーランをはじめ本ゲームの各モード内でプレイヤーにも確認できる形で表示されているが、このタイマーがIGTの1秒進む間に必ずしも60Fを丁度使っていないということが当バグの表面的な原因である。

具体的には各モードで下記のようなIGT1秒当たりの消費フレーム数になっている。
上の表での時の流れの倍速率もこのフレーム数をもとに算出したに過ぎない。

モードIGT1sあたりの消費フレーム数時の流れn倍速(%)備考
060.0100%基本一定、ただし100秒に1回くらい61Fになっている
159.8100.3%基本60F、ただし5秒に1回くらい59Fになる
260.299.7%基本60F、ただし5秒に1回くらい61Fになる
356.9105.5%基本57F、ただし9秒に1回くらい56Fになる
464.093.75%基本64F
532.0187.5%基本32F
60.0%世界の静止

これらの本質的な原因は恐らく1秒以下の計算において小数を扱うことになるが、その際に単精度浮動小数点数を用いており、その丸め誤差によるものであると推定されている。
そちらの内容はかなり専門的な領域になるので、知りたい方のみ下記を参照されたい。

もっと詳しく知りたい方向け

この項目では、タイマーのバグが発生する本質的な原因・浮動小数点数の仕様について解説する。
前項までとは別人による記述なので、文体の違い等は許容していただきたい。
また、この項目で説明するゲーム内の仕様等は検証と外部からの観測により推測したもので、実際の内部の処理とは異なる可能性があることは留意すること。

まず、浮動小数点数とはコンピュータ上で実数を処理する方式の1つであり、符号部・指数部・仮数部からなる。
単精度浮動小数点数は符号部1bit、指数部8bit、仮数部23bitの合計32bit、
倍精度不動小数点数は符号部1bit、指数部11bit、仮数部52bitの合計64bitで構成される。(半精度・四倍精度等もあるが省く)
以降、エアライダーのタイマーで使われている単精度浮動小数点数を主軸に解説する。

単精度浮動小数点数では数値を (-1)^符号部 * 2^(指数部-127) * (1+仮数部) で表現する。
簡単に説明すると、符号部は数値の正負、指数部は2進数表記での小数点の位置、仮数部は整数部分を1としたときの小数点以下の数値を表す。
仮数部は23bitあり、上位のbitからそれぞれ 0.5, 0.25, 0.125, 0.0625... とそれぞれ 2^(-n) の数値を持ち、それらの和で数値を表現する。

例えば、10.5 を単精度浮動小数点数で表現すると 0 10000010 01010000000000000000000 (2進数) となり、
(-1)^0 * 2^(130-127) * (1+0.25+0.0625) = 10.5 と正確に表現できる。
また、これは小数点付きの2進数で 1010.1 と表現することもできる。

しかし、1/60 = 0.0166666... のような 2^(-n) の組み合わせで表現できない数値を単精度浮動小数点数で表現した時には様相が異なる。
結論から言うと 1/60 は 0.00000100010001000100010001001 (2進数) で、これを10進数に直すと 0.01666666753590106964111328125 となり、少々の誤差が発生する。
これは、23bitの有限の仮数部で表現できるように数値を丸めた際に発生する誤差である。
この段階では誤差が発生するのは10進数での小数点第9位からで、そこまで気にならないレベルの誤差であるが、エアライダーでのタイマーの仕様と合わさり問題が発生することとなる。

エアライダーのタイマー、とりわけフリーランでは、開始時を0として毎フレーム1/60(秒)を加算していったものを合計タイムとして持っている。
最初こそタイマーは正常に動作しているように見えるが、合計タイムが大きくなってくると問題が発生する。

手っ取り早く説明するために、まずは以下の表を見ていただきたい。
2^n+1/60とその単精度浮動小数点数での2進数表記、それを再度10進数に直したものの対応表である。

2進数表記(単精度浮動小数点数)10進数
1+1/601.000001000100010001000101.0166666507720947265625
2+1/6010.00000100010001000100012.0166666507720947265625
4+1/60100.0000010001000100010014.016666889190673828125
8+1/601000.000001000100010001008.016666412353515625
64+1/601000000.0000010001000100164.01667022705078125
128+1/6010000000.0000010001000100128.01666259765625
1024+1/6010000000000.00000100010011024.0167236328125
2048+1/60100000000000.0000010001002048.0166015625
16384+1/60100000000000000.00000100116384.017578125
32768+1/601000000000000000.0000010032768.015625
262144+1/601000000000000000000.00001262144.03125
524288+1/6010000000000000000000.0000524288

整数部が大きくなる毎に小数部に割かれるbit数が減り、小数部分の丸めの幅が大きくなっていく(=精度が下がる)のがお分かりいただけるだろうか。

浮動小数点数では仮数部で整数部も小数部も表現するが、bit数に限りがあるため、数値が大きくなるほど下位の数値の精度が失われていくのである。
これが時間が経過するほどタイマーの精度が悪くなっていく本質的な原因である。
最終的には1フレームに加算される秒数が0に丸められてしまい、タイマーが全く動かなくなってしまう。

では、これを解決するにはどうすればよいか?
最も簡単なものでは単精度浮動小数点数を使わず倍精度浮動小数点数を使うことである。
倍精度浮動小数点数でタイマーを作れば、タイマーの小数点第2位にフレーム単位の誤差が現れるよりも先にSwitch2が寿命を迎える。(はず…)
ただし、こちらも微小ながら誤差が発生していることには変わりは無いので、例えば現実時間で2分ぴったりでも1'59"99と表示されたりする。

もう一つは経過したフレーム数を整数で持ち、それをタイムに変換して表示すること。
これは前作でも使われており、整数型がオーバーフローを起こさない限りは正しいタイムを表示することができる。

以上。