📌 この記事は Zenn に投稿した内容のアーカイブです。
はじめに:高速対戦ゲームにおけるネットコードの本質
ゲームプレイ(GamePlay)エンジニアにとって、最重要テーマのひとつが ネット同期(netcode) です。
特に Overwatch のような高速リアルタイムアクションでは、「レスポンスの速さ(responsive)」 がゲーム性を決定づけます。
そのため、プレイヤーの操作は サーバーの応答を待たずに即時反映 する必要があります。
これを実現するのが 予測(prediction / pre-presentation) です。
ただし、クライアントを信頼できない(チート対策)というFPSの前提は20年変わっていません。
そのうえで「即時応答」と「サーバー権威」の両立を図るのがネットコード設計の核心になります。
即時応答が必要な操作
移動
スキル
武器(発射や装填などのスキル付き武器)
当たり判定(Hit Registration)
共通原則はただ一つ:
「プレイヤーがボタンを押したら即、見た目の挙動が起きること」
どれほど ping が高くても、遅延を感じさせてはならない。
しかし、予測を行う以上、避けられない問題があります──
それが 予測ミス(misprediction) です。
予測ミスは、
「クライアント側では成功したように見えた操作が、サーバー上では成立していない」
状態を指します。
Overwatch では、予測ミスが起きても「操作を遅延させる」のではなく、
できる限り予測ミスを減らす設計(=確定性 Determinism) が採用されています。
予測ミスの例:サーバーとの食い違い
ping 250ms。
クライアントでは「ジャンプした」つもりでも、サーバー上では Mei の氷結を受けていた 。
その結果、クライアントは一度ジャンプの予測を行った後、サーバーからの正しい状態(氷結)で「強制的に巻き戻される」。
このように、超高速応答を目指すほど、予測ミスが発生しやすくなります。
▼ ここから:低ミス予測を支える「確定性(Determinism)」
以下では Overwatch が採用している ECS + Deterministic Simulation の要点を整理します。
1. 時間の量子化:Command Frame
確定的なシミュレーションは、
時間の同期
固定更新周期
値の量子化
に依存します。
サーバーとクライアントは、Command Frame(命令フレーム) 単位で同期を取ります。
- 1フレーム = 16ms(大会時は 7ms)
ECS では、プレイヤー入力を扱うすべての System は通常の Update ではなくUpdateFixed(固定フレーム用の更新)で動作します。
2. クライアントは常にサーバーより「半RTT + 1フレーム」先を走る

例:RTT が 160ms の場合
半分 → 80ms
+1 フレーム(16ms)
つまりクライアントは常に 約96ms 未来を走っている 状態です。
なぜ?
クライアントが即時にプレイヤー入力を反映し続けるには、現在時刻(NOW)に限りなく近く動作する必要があるからです。
3. 予測 → サーバー確認 → 一致チェック
クライアントは以下を行います:
現在の入力を元に予測シミュレーション
その結果をブラッシュアップしつつ、入力をサーバーへ送信
サーバーが後から「この時刻の正しい状態」を送り返す
クライアントは リングバッファに保存した過去の状態 と照合
一致 → そのまま継続 / 不一致 → Reconciliation(巻き戻し+再計算)

一致していればスムーズに次へ進み、
不一致なら「サーバーの状態へ巻き戻して」その後の入力を全部再適用します。
4. Reconciliation:巻き戻しと再適用
予測ミスが起きた場合:
サーバーから戻ってきた「正しい状態」を採用
その時点から現在までの「入力ログ」を全て再生(リプレイ)
現在時刻まで追いつく

例:
クライアントは Tracer が走っていると予測していたが、サーバー上ではMcCreeのフラッシュでスタン状態だった。
巻き戻して再計算すると、挙動がサーバーと完全に揃う。
5. ネットワークが不安定なとき:クライアントの「時間膨張」
クライアントの入力パケットはカスタム UDP(信頼制)で送られますが、
それでもパケットロスは起こる 。
パケットロスが続くと、サーバーは「最後に届いた入力」を元に予測するしかありません。
そこで、Overwatch が採用したのが:
クライアントの Time Dilation(時間膨張)
クライアントが、
通常:1frame = 16ms
膨張:1frame ≒ 15.2ms
のように 僅かに高速でシミュレーション を行い、サーバーより先行幅を広げる技術です。
結果:
入力パケットが「より早く」サーバーに届く
サーバー側の入力バッファが太くなる(余裕が出来る)
パケットロスの穴を埋めやすくなる
これは、ping やパケットロスが揺れまくる環境(国際回線、宇宙ステーションなど)でも機能します。
6. サーバーが「回復した」と判断したら
ネットワークが安定すると、サーバーは:
「OK、もう大丈夫」
という合図を出します。
クライアントは:
時間膨張を解除(frame 時間を元に戻す)
入力報告頻度を通常に戻す
サーバーもバッファを縮小
これにより、遅延・丢包・予測ミスの影響を最小限に抑えつつレスポンス性を維持できます。
7. 入力の「スライディングウィンドウ」送信
サーバーが最後に確認したフレームが例えば 4 で、
クライアントが現在 19 フレームまで進んでいる場合:
クライアントは 4〜19 のすべての入力ログをまとめて送る 。
これにより:
パケットロスが発生しても次のパケットで穴埋め可能
サーバーは入力の抜けをほぼ確実に補完できる
移動予測は破綻しにくくなる
この手法は古い FPS(例:Quake)の時代から存在する伝統技術です。
8. すべての要素を同時に起こした最悪ケースのデモ
以下の全要素が同時に発生:
ping 変動
パケットロス
時間膨張
入力スライディングウィンドウ
予測ミス
サーバー側 correction
それでもゲームプレイは成立する──
これが Overwatch のネットコードの強さです。
まとめ
Overwatch のネットコードは次の技術の組み合わせで成り立っています:
クライアント予測(Prediction)
サーバー権威(Server Authoritative)
巻き戻し(Reconciliation)
確定的シミュレーション(Determinism)
スライディングウィンドウ入力
ネットワーク不安定時の時間膨張(Time Dilation)
結果として、低遅延・高レスポンス・高一貫性 を同時に実現しています。
※このブログは GDC2017【Overwatch Gameplay Architecture andNetcode 】を参考にしました。