0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【絶対ズレない】WebRTCで「シード値」だけを送って宇宙を同期させる方法

0
Posted at

マルチプレイのゲームを作っていて、**「自分の画面では敵を倒したのに、相手の画面ではまだ生きている」**といった同期ズレに悩んだことはありませんか?

今回は、大規模なシミュレーションや格闘ゲームなどで使われる、データの重さに左右されず、かつ「絶対に計算がズレない」ための**決定論的同期(Deterministic Synchronization)**の実装エッセンスを解説します。


1. 「今の座標」を送るのをやめてみる

普通、マルチプレイを実装しようとすると「A君は今座標(100, 200)にいる」という情報を常に送ろうとします。しかし、これには弱点があります。

  • 通信が重い: オブジェクトが数千、数万と増えると、全座標を送るだけで回線がパンクします。
  • ラグの影響: 座標が届いたときには、相手はもう別の場所にいます。

そこで、発想を逆転させます。「データそのもの」ではなく、「変化のきっかけ(入力)」だけを共有するのです。


2. 絶対にズレないための「3つの鉄則」

全員の画面で全く同じ結果を再現するには、プログラムから「曖昧さ」を徹底的に排除する必要があります。

  1. 共通の時計(Tick)を持つ: ネットワーク全員で「今は第100拍目」という時間の単位を完璧に合わせます。
  2. Math.random() の禁止: 端末ごとに違う結果が出る乱数は使えません。共通の「シード値」から計算される独自の乱数を使います。
  3. 整数で計算する: 0.1 + 0.2 のような小数点計算は、環境によってごく稀に結果がズレます。1ビットの狂いも許さないよう、すべて「整数」で計算します。

3. 実装サンプル:決定論的な状態更新

「同じタイミングで、同じ入力があれば、結果は必ず同じになる」というコードの書き方を見てみましょう。

悪い例(環境によってズレる)

function updateEntity(entity) {
  // Math.random() は端末ごとに結果が違うのでNG!
  if (Math.random() > 0.5) {
    entity.x += 1.5; // 小数点計算も環境によって微差が出る可能性がある
  }
}

良い例(どこでも同じ結果になる)

// 共通のシード値から計算される乱数生成器(簡易版)
class DeterministicRNG {
  constructor(seed) { this.seed = seed; }
  next() {
    this.seed = (this.seed * 1103515245 + 12345) & 0x7FFFFFFF;
    return this.seed;
  }
}

// 決定論的な更新ロジック
function updateState(state, inputs, tick) {
  const rng = new DeterministicRNG(state.baseSeed + tick); // Tickをシードに混ぜる

  // 1. 全員に届いた「そのTickの入力」を処理
  inputs.forEach(input => {
    if (input.type === 'MOVE_LEFT') state.x -= 100; // 整数で計算
    if (input.type === 'MOVE_RIGHT') state.x += 100;
  });

  // 2. 共通の乱数で演出などを計算
  if (rng.next() % 100 < 50) {
    state.energy += 10;
  }
}

4. 「共通の楽譜」を演奏するイメージ

この仕組みを例えるなら、**「演奏データ(録音)を送り合う」のではなく、「楽譜を全員に渡して、せーので演奏してもらう」**ようなものです。

  • 絶対Tick: 全員の時計を「第100拍目」という単位で合わせます。
  • アクション: 「第100拍目に、Aさんがボタンを押した」という最小限の情報だけを共有します。

もし通信が遅れて「過去の入力」が届いた場合は、一瞬だけ過去に巻き戻って計算し直し、現在の時間まで猛スピードで再計算(ロールバック)して追いつきます。


まとめ:データではなく「ルール」を同期させる

「状態」を送り合うのはビデオ動画を共有するようなものですが、「入力とルール」を同期させるのは**「数学の証明を共有する」**ような、非常にスマートな解決策です。

  • 通信量は最小限(ボタン操作の数バイトだけ)
  • 計算は完璧に一致(整数の決定論)
  • 宇宙規模の多人数接続も可能にする

この考え方を取り入れると、ネットワークプログラミングの難易度は上がりますが、その先には「絶対にズレない理想の世界」が待っています。


用語解説

  • 決定論(Determinism): 同じ入力・同じ状態なら、必ず同じ結果が出ること。
  • 入力同期(Input Sync): 座標データではなく、操作ログを同期する手法。
  • シード値(Seed): 乱数のパターンを決定する「種」。これさえ合っていれば、全員が同じ乱数の列を得られます。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?