ネットワーク化されたリアルタイムゲームにゲーム状態のスナップショットシステムをどのように実装しますか?


12

ネットワーククラスのプロジェクトとして、シンプルなクライアントサーバーリアルタイムマルチプレイヤーゲームを作成したいと思います。

リアルタイムのマルチプレイヤーネットワークモデルについて多くのことを読んでおり、クライアントとサーバーの関係と遅延補償のテクニックを理解しています。

私がしたいことは、Quake 3ネットワークモデルに似たものです。基本的に、サーバーはゲーム全体の状態のスナップショットを保存します。クライアントから入力を受け取ると、サーバーは変更を反映した新しいスナップショットを作成します。次に、新しいスナップショットと最後のスナップショットの差を計算し、それらを同期するためにそれらをクライアントに送信します。

このアプローチは本当に堅実に思えます-クライアントとサーバーが安定した接続を持っている場合、それらを同期させるために必要最小限のデータのみが送信されます。クライアントの同期が外れると、完全なスナップショットも要求できます。

ただし、スナップショットシステムを実装する良い方法を見つけることはできません。シングルプレイヤープログラミングアーキテクチャから離れて、ゲームの状態を次のように保存する方法を考えるのは本当に難しいと思います。

  • すべてのデータはロジックから分離されています
  • ゲームの状態のスナップショット間の差を計算できます
  • ゲームエンティティはコードを使用して簡単に操作できます

スナップショットクラスはどのように実装されますか?エンティティとそのデータはどのように保存されますか?すべてのクライアントエンティティには、サーバー上のIDと一致するIDがありますか?

スナップショットの違いはどのように計算されますか?

一般的に:ゲーム状態のスナップショットシステムはどのように実装されますか?


4
+1。これは単一の質問には少し広すぎますが、IMOはおもしろい答えで大まかにカバーできる興味深いトピックです。
Kromsterは

スナップショット(実際の世界)を1つだけ保存し、入ってくるすべての変更をこの通常の世界状態に保存し、変更をリストなどに保存してみませんか。次に、すべてのクライアントに変更を送信する時間は、リストの内容をすべてのクライアントに送信し、リストをクリアするとき、ゼロ(変更)から始まります。たぶん、これは2つのスナップショットを保存するほど良くありませんが、このアプローチを使用すると、2つのスナップショットを高速に比較する方法についてアルゴリズムを心配する必要はありません。
tkausl 14

これを読んでください:fabiensanglard.net/quake3/network.php-地震3ネットワークモデルのレビューには実装に関する議論が含まれています。
スティーブン14

どのようなゲームを構築しようとしていますか?ネットワークのセットアップは、作成しているゲームのタイプに大きく依存します。RTSは、ネットワーキングの点でFPSのようには動作しません。
AturSams 14

回答:


3

2つのスナップショットインスタンス(現在のインスタンスと最後に同期したインスタンス)を保持することにより、スナップショットデルタ(以前の同期状態への変更)を計算できます。

クライアント入力が到着すると、現在のスナップショットを変更します。次に、デルタをクライアントに送信するときに、最後に同期されたスナップショットを現在の1つのフィールドごとに(再帰的に)計算し、デルタを計算してシリアル化します。シリアル化では、クラスのスコープ内の各フィールドに一意のIDを割り当てることができます(グローバル状態スコープとは対照的)。クライアントとサーバーは、グローバル状態に対して同じデータ構造を共有する必要があります。これにより、クライアントは特定のIDが適用される対象を理解します。

次に、デルタが計算されると、現在の状態を複製して最後に同期された状態にするため、現在の状態と最後に同期された状態は同じですが、インスタンスが異なるため、現在の状態を変更し、他に影響を与えません。

このアプローチは、特にリフレクションの助けを借りて(このような贅沢がある場合)実装が簡単になりますが、リフレクション部分を非常に批判する場合でも(ほとんどのリフレクション呼び出しをキャッシュするデータスキーマを構築することで)遅くなる可能性があります。主に、潜在的に大きな状態の2つのコピーを比較する必要があるためです。もちろん、比較と言語の実装方法によって異なります。ハードコーディングされたコンパレータを使用したC ++では高速になりますが、それほど柔軟ではありません。グローバルステート構造の変更にはこのコンパレータの変更が必要であり、これらの変更はプロジェクトの初期段階で頻繁に行われます。

別のアプローチは、ダーティフラグを使用することです。クライアント入力が到着するたびに、それをグローバルステートの単一コピーに適用し、対応するフィールドにダーティのフラグを付けます。次に、クライアントを同期するときに、同じ一意のIDを使用してダーティフィールドを(再帰的に)シリアル化します。(軽微な)欠点は、厳密に必要なデータよりも多くのデータを送信する場合があることです。たとえばint field1、最初は0でしたが、1が割り当てられ(ダーティのフラグ)、その後0が再び割り当てられます(ダーティのままです)。利点は、巨大な階層データ構造を持つことです。デルタを計算するために完全に分析する必要はなく、ダーティパスのみです。

一般に、このタスクは非常に複雑になる可能性があり、最終的な解決策の柔軟性に依存します。例えば、Unity3D 5(今後)は属性を使用して、クライアントに自動同期するデータを指定し(非常に柔軟なアプローチ、フィールドに属性を追加する以外に何もする必要はありません)、次にコードを生成しますビルド後の手順。詳細はこちら。


2

まず、プロトコルに準拠した方法で関連データを表現する方法を知る必要があります。これは、ゲームに関連するデータに依存します。例としてRTSゲームを使用します。

ネットワーキングの目的で、ゲーム内のすべてのエンティティが列挙されます(ピックアップ、ユニット、建物、天然資源、破壊可能物など)。

プレイヤーは、それらに関連するデータを持っている必要があります(例えば、すべての目に見えるユニット):

  • 彼らは生きているか死んでいますか?
  • 彼らはどんなタイプですか?
  • 彼らはどれくらいの健康を残しましたか?
  • 現在位置、回転、速度(速度+方向)、近未来の経路...
  • アクティビティ:攻撃、歩行、構築、修復、癒しなど
  • バフ/デバフのステータス効果
  • そして、おそらくマナ、シールドなどのその他の統計はどうですか?

最初に、プレーヤーはゲームに入る前に完全な状態(または、そのプレーヤーに関連するすべての情報)を受け取る必要があります。

各ユニットには整数IDがあります。属性は列挙されているため、整数の識別子もあります。ユニットIDの長さは32ビットである必要はありません(質素でなければ)。非常に20ビットである可能性があります(属性に10ビットを残します)。ユニットのIDは一意である必要があります。ユニットがインスタンス化および/またはゲームワールドに追加されると、カウンターによって非常に適切に割り当てられます(建物とリソースは不動のユニットと見なされ、マップはが読み込まれます)。

サーバーは現在のグローバル状態を保存します。各プレーヤーの最新の更新状態はlist、最近の変更へのポインターで表されます(ポインターがまだそのプレーヤーに送信されていない後のすべての変更)。変更はlist、発生時に追加されます。サーバーが最後の更新の送信を完了すると、リストの反復処理を開始できます。サーバーはプレーヤーのポインターをリストに沿って末尾に移動し、途中ですべての変更を収集して、送信先のバッファーに配置しますプレイヤー(つまり、プロトコルの形式は次のようになります:unit_id; attr_id; new_value)新しいユニットも同様に変更と見なされ、すべての属性値とともに受信プレイヤーに送信されます。

ガベージコレクターで言語を使用していない場合は、遅れている遅延ポインターをセットアップし、リスト内の最も古いプレーヤーポインターに追いつき、途中でオブジェクトを解放する必要があります。どのプレーヤーが優先ヒープ内で最も古くなっているかを覚えておくか、レイジーポインターが等しくなるまで(つまり、プレーヤーポインターの1つと同じアイテムを指すように)単純に反復して解放します。

あなたが提起しなかったいくつかの質問と私は興味深いと思います:

  1. クライアントは、最初にすべてのデータを含むスナップショットを受信する必要がありますか?視界外のアイテムはどうですか?RTSゲームでの霧はどうですか?すべてのデータを送信すると、クライアントがハッキングされて、プレーヤーが使用できないデータを表示する可能性があります(他のセキュリティ対策に応じて)。関連データのみを送信すると、問題は解決します。
  2. すべての情報を送信するのではなく、変更を送信することが重要なのはいつですか?最新のマシンで利用可能な帯域幅を考慮して、すべての情報を送信するのではなく、「デルタ」を送信することで何か得られますか?
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.