クライアント予測をいちばん簡単に説明する
マルチプレイの移動処理は複雑に見えます。実際には 3 ステップです。予測し、比較し、補正する。
なぜ必要か
サーバー権威がなければチートは防げません。しかし入力を毎回サーバーへ送って応答を待つと、キーを押してから 80 ms 後に動くキャラクターをプレイヤーは見ることになります。それでは遊べません。
解決策は、クライアントが移動をその場で適用しつつ、同時に入力をサーバーへ送ることです。つまり未来を予測するのです。
3 つのステップ
予測:クライアントは入力を即座にシミュレートし、結果をティック番号付きで履歴バッファに書き込みます。
比較:サーバーが自身の結果を送ってきたら、クライアントはそのティックで保存した結果と比較します。
補正:差が許容範囲内なら何もしません。そうでなければサーバーの位置に合わせ、そのティック以降の入力をすべて再実行します。
実装上の落とし穴
シミュレーションは決定論的でなければなりません。同じ初期状態から同じ入力を与えれば同じ結果になる必要があります。Time.deltaTime に依存する物理ステップはこれを壊します。固定ティックを使ってください。
補正は急激に行わないこと。小さな差を数フレームかけてならすことで、プレイヤーがワープしたように感じるのを防げます。
void OnServerState(ServerState s)
{
_history.DiscardBefore(s.Tick);
if (Vector3.Distance(s.Position, _history[s.Tick].Position) < TOLERANCE)
return; // tahmin dogru, dokunma
transform.position = s.Position; // duzelt
for (int t = s.Tick + 1; t <= _localTick; t++)
Simulate(_history[t].Input); // ve yeniden oyna
}
csharpReconciliation.cs
どこから始めるか
まずシングルプレイの移動を固定ティック上に構築し、決定論的にします。ネットワーク層はその後です。
この順序を逆にしたチームは、移動のバグと同期のバグを同時にデバッグする羽目になります。経験から言っています。