- Half-Life 2の序盤シーンでドアが開かず、進行が止まってしまう奇妙なバグが発見された
- 原因はドアが開く際、内側に立っている警備員のつま先がドアの軌道と衝突し、ドアが再び閉じてロックされてしまう現象
- オリジナルコードでも同じ衝突は存在していたが、2004年のx87浮動小数点演算と2013年のSSE演算の精度差によって結果が変わった
- x87環境ではわずかな回転によって足が少し押し出され衝突が解消されるが、SSEでは回転量が足りずドアが閉じる結果になる
- この事例は、浮動小数点精度とコンパイラ差異が実際のゲーム挙動に与える影響を示す代表例である
Half-Life 2 VR移植の過程で発見されたバグ
- 2013年、ValveでTeam Fortress 2をVRへ移植する実験中、同じエンジンを使うHalf-Life 2とPortal 1もVRで動作するようになった
- Portal 1は視点の歪みにより、VRではプレイ不可能なほど酔いやすかった
- Half-Life 2は比較的うまく動作し、ボート場面や箱積み、**マンハック(manhack)**との戦闘などでVR特有の没入感が高まった
- テスト中、開発者がゲームを最初から最後までVRでプレイしていたところ、序盤の列車駅シーンで進行不能状態を発見した
ドアが開かない原因の分析
- 本来のシナリオでは、警備員(実際にはBarney)がドアをノックして「入れ」と言った後、プレイヤーが部屋に入ると次のスクリプトへ進行する
- しかしバグ発生時には、ドアがガタガタした末にロックされて完全に閉じてしまい、警備員はずっとドアを指し続け、プレイヤーは閉じ込められる
- オリジナルのHalf-Life 2ソースコードを再ビルドしても同じ問題が発生し、時間をさかのぼって生まれたバグのように見える現象として混乱を招いた
根本原因: 浮動小数点演算方式の変化
- 2004年の発売当時、Half-Life 2はx87数学命令セットを使用しており、32・64・80ビット精度が混在する構造だった
- 2013年以降のビルドではSSE命令セットがデフォルトで使われ、32ビットまたは64ビットに明確に制限された演算精度が適用された
- どちらの環境でもドアと警備員のつま先は衝突するが、物理エンジンの微細な計算差によって結果が変わる
- x87では衝突時に警備員がごくわずかに回転し、足がドアから外れてドアが開く
- SSEでは回転量がわずかに減るため足がまだ触れたままとなり、ドアが再び閉じてロックされる
修正と解決
- 問題を把握した後、警備員の位置を約1mm後ろへ移動させる簡単な修正で解決した
- デバッグ過程では古いツールの使い方を再び学び直すなど、かなりの時間を要した
- この事例は、精度差とコンパイラ設定の変化が過去のコードの挙動を変えうることを示している
コミュニティの反応
- 開発者たちは「結局いつも問題は浮動小数点精度だ」と共感した
- 一部ではSonic 1・2・3リメイクの衝突計算の変化事例に触れ、類似性を指摘した
- 「コードは同じでもコンパイラは違う」という教訓とともに、レガシーコード保守の難しさを思い起こさせる事例だと評価された
- ほかの開発者たちはFallout 4などでNPCがドアを通り抜けないよう設計した理由を、こうした問題と結びつけて言及した
- 全体として「最も興味深いバグ話の一つ」という反応が多かった
1件のコメント
Hacker Newsの意見
昔ゲーム開発をしていた頃、FPUの計算誤差のせいで特定のPCでだけアサーション失敗が発生したことを思い出した
原因は、手書き入力ソフトウェアがすべてのプロセスにDLLを注入し、FPUモードをデフォルトにリセットしていたことだった
結局、FPU設定コードを初期化段階からイベントループへ移し、サードパーティーDLLの影響を回避した
私の目標の1つは、ValveにNixを使わせることだ
Windowsサポートも進んでいるので、さらに魅力的に見えると思う
そうなれば、元のソースだけでなく当時のツールチェーンや依存関係まで完全に再現できるようになり、こうしたバグの根本原因もはるかに見つけやすくなるはずだ
「DOOR STUCK」のミームを思い出した
関連動画
「Half-Life 2 VRベータ」が実際にプレイ可能なのか気になる
もし可能ならなぜ知らなかったのか、そうでないならなぜまだできないのか不思議だ
Portal VRもぜひやってみたい。ひどいVR酔いをするとよく言われるけれど、私はVR酔いに強いので試す価値がありそうだ
久しぶりにHL2をもう一度やりたくなるほど完成度が高い
x87からSSEへ切り替えると一部の計算が壊れるのはよくあることだ
TF2でも同じことがあり、LinuxビルドではSSEを使っていたため弾薬計算が少し異なっていた
小さい箱から +40 または +41 を得られたが、サーバーOSによって違っていた
新しいサーバーに接続するたび、どのOSか確かめるのが面白かった
x87からSSEに変えるだけでゲームが壊れるのに、x86→ARM変換がどうしてあんなにうまくいくのか不思議だ
80ビットレジスタを使うので精度は高いが、メモリにspillされると精度を失うなど奇妙な挙動をする
以前ソフトウェアシンセサイザーをデバッグしたときも、似たような精度バグに遭遇した
RC回路シミュレーションが0に近づくと状態が切り替わるはずなのに、特定のCPUではdenormal値がフラッシュされず条件を満たさなかった
結局、0.7や0.01あたりの適当なしきい値に調整したら、すべてのプラットフォームでうまく動いた
メタな話だが、Twitterクローンを作るなら、複数段落でブログのように投稿したら即BANする機能を入れたい