リアルタイム戦略ゲームのネットワーキング


16

私は自分が取っているコンピューターサイエンスコースのリアルタイム戦略ゲームを開発しています。それのより難しい側面の1つは、クライアントとサーバーのネットワーキングと同期であるようです。このトピック(1500人の射手を含む)を読みましたが、他のモデル(たとえばLAN経由)とは対照的に、クライアント/サーバーアプローチを採用することにしました。

このリアルタイム戦略ゲームにはいくつかの問題があります。ありがたいことに、プレーヤーが行うすべてのアクションは決定論的です。ただし、スケジュールされた間隔で発生するイベントがあります。たとえば、ゲームはタイルで構成されており、プレイヤーがタイルを取得すると、そのタイルの値である「エネルギーレベル」は取得後1秒ごとに増加します。これは、私のユースケースを正当化する非常に簡単な説明です。

現在、サーバーにパケットを送信して応答を待つシンクライアントを実行しています。ただし、いくつかの問題があります。

プレイヤー間のゲームがエンドゲームに発展すると、毎秒50を超えるイベントが発生することが多く(前述のスケジュールされたイベントにより、蓄積されます)、同期エラーが発生し始めます。私の最大の問題は、クライアント間の状態のわずかな逸脱でさえ、クライアントが異なる決定を意味する可能性があり、それが完全に別個のゲームに雪だるま式になるということです。もう1つの問題(現時点ではそれほど重要ではありません)は、待ち時間があり、結果を確認するために移動してから数ミリ秒、さらには数秒待つ必要があることです。

これをエンドユーザーにとってより簡単に、より速く、より楽しくするためにどのような戦略とアルゴリズムを使用できるのだろうかと考えています。これは、1秒あたりのイベント数が多く、ゲームごとに複数のプレイヤーがいることを考えると、特に興味深いものです。

TL; DRが1秒あたり50イベントを超えるRTSを作成している場合、クライアントを同期するにはどうすればよいですか?


Eve-onlineの機能を実装し、すべての処理を適切に処理できるように時間を「遅くする」ことができます。
ライアンアーブ

3
以下は、Planetary Annihilationのクライアント/サーバーモデルへの必須リンクです。forrestthewoods.ghost.io / これは、彼らにとって非常にうまく機能していると思われるロックステップモデルの代替です。
ダロンF

タイルごとに更新するのではなく、取得したすべてのタイルに単一の更新を送信するか、Ilmariが答えたように、非プレーヤーアクションを分散化することにより、イベントの数を減らすことを検討してください。
リリエンタール

回答:


12

リアルタイムサウンドで毎秒50イベントを同期するというあなたの目標は、現実的ではないように思えます。1500アーチャーの記事で説明されているロックステップアプローチがよく話題になっているのはこのためです!

一文で:遅すぎるネットワークで短すぎる時間に多すぎるアイテムを同期する唯一の方法は、遅すぎるネットワークで短すぎる時間で多すぎるアイテムを同期せず、代わりにすべてのクライアントで確定的に状態を進行させ、最低限必要なもの(ユーザー入力)。


6

プレーヤーが行うすべてのアクションは決定的ですが、スケジュールされた間隔で発生するイベントがあります

問題があると思います。ゲームには1つのタイムラインのみを含める必要があります(ゲームプレイに影響するもののため)。特定の物事は毎秒 Xの割合で成長すると言います。1秒にいくつのゲームステップがあるかを調べ、それをYゲームステップあたり Xのレートに変換します。その後、ゲームの速度が低下する場合でも、すべてが確定的です。

ゲームをリアルタイムで独立して実行することには、他の利点もあります。

  • 可能な限り高速で実行することでベンチマークできます
  • 前述のように、ゲームの速度を落とし、つかの間のイベントを表示することでデバッグできます。
  • ゲームは確定的であり、マルチプレイヤーにとって非常に重要です。

また、50を超えるイベントがある場合、または最大数秒の遅延がある場合に問題が発生することにも言及しました。これは、1500人の射手で説明されているシナリオよりも規模がはるかに小さいため、ゲームのプロファイルを作成し、スローダウンがどこで発生するかを確認してください。


1
+1:フレームベースは時間ベースではなく正しい選択です。もちろん、1秒あたりNフレームを維持しようとすることもできます。わずかなヒッチは、完全な同期解除よりも優れています。
PatrickB

@PatrickB:多くのゲームは、ビデオフレームに関連付けられていない「シミュレートされた」時間を使用しているようです。World of Warcraftは100msごとにマナなどを更新するだけで、Dwarf Fortressはビデオフレームごとに10ティックにデフォルト設定されます。
Mooingダック

@Mooing Duck:私のコメントはRTSに特化したものでした。小さなエラーを後で許容して修正できるもの(MMORPG、FPSなど)の場合、連続値を使用することは問題ないだけでなく、重要です。しかし、複数のマシン間で同期する必要がある決定論的シミュレーションは?フレームにこだわる。
PatrickB

4

まず、スケジュールされたイベントの問題を解決するには、イベントが発生したときではなく、最初にスケジュールされたときにイベントをブロードキャストします。それは代わりに「タイルのエネルギーをインクリメント(送信のうち、XY)」というメッセージ秒ごと、単に「(タイルのエネルギーをインクリメント言って単一のメッセージを送信するのxyのそれがいっぱいになるまで、1秒に1回)、またはまでを中断」。その後、各クライアントはローカルで更新をスケジュールします。

実際、この原則をさらに進めて、プレーヤーのアクションのみを送信できます。他のすべては、各クライアント(および必要に応じてサーバー)によってローカルで計算できます。

(もちろん、偶然の非同期化を検出するために、ゲームの状態のチェックサムを時々送信する必要があります。例えば、サーバーの権限のあるコピーからすべてのゲームデータをクライアントに再送信することにより、 。しかし、これはまれなイベントであり、テスト中またはまれな誤動作中にのみ発生します。


次に、クライアントの同期を維持するために、ゲームが決定論的であることを確認します。 他の回答はすでにこれに関する良いアドバイスを提供していますが、何をすべきかの簡単な要約を含めてみましょう。

  • ゲームを内部的にターンベースにし、各ターンまたは「ティック」は、たとえば1/50秒かかります。(実際、おそらく1/10秒以上のターンで逃げることができます。)1ターン中に発生するプレーヤーのアクションは、同時として扱われる必要があります。少なくともサーバーからクライアントへのすべてのメッセージには、各クライアントがどのイベントが発生するかを知るために、ターン番号でタグを付ける必要があります。

    ゲームはクライアント/サーバーアーキテクチャを使用しているため、各ターン中に発生することの最終的なアービターとしてサーバーを動作させることができます。これにより、いくつかのことが簡素化されます。ただし、クライアントはサーバーから自身のアクションも再確認する必要があることを注意してください:クライアントが「ユニットXを1タイル左に移動します」というメッセージを送信し、サーバーの応答がユニットXの移動について何も言わない場合、クライアントは発生しなかったと想定し、おそらく再生を開始した可能性のある予測動作アニメーションをキャンセルする可能性があります。

  • 同じ順番で発生する「同時」イベントの一貫した順序を定義して、各クライアントが同じ順序でそれらを実行するようにします。この順序は、確定的であり、すべてのクライアント(およびサーバー)で同じである限り、どのような順序でもかまいません。

    たとえば、最初にすべてのリソースをインクリメントし(1つのタイルのリソースの増加が別のタイルのリソースの増加と干渉できない場合、一度にすべて実行できます)、次に各プレーヤーのユニットを所定の順序で移動してから、NPCユニットを移動できます。プレイヤーに公平を期すために、ターン間でユニットの移動順序を変えて、各プレイヤーが同じ頻度で最初に移動できるようにすることができます。これは、確定的に(たとえば、ターン番号に基づいて)行われている限り、問題ありません。

  • 浮動小数点演算を使用している場合は、厳密なIEEEモードで使用していることを確認してください。これにより事態は少し遅くなりますが、それはクライアント間の一貫性のために支払う小さな代償です。また、通信中に偶発的な丸めが発生しないようにします(たとえば、クライアントが丸められた値をサーバーに送信しますが、内部的に丸められていない値を使用します)。上記のように、念のため、非同期を検出して回復するプロトコルを用意することもお勧めします。


1
また、RNGを同期して開始し、サーバーから指示された場合にのみ、同期されたRNGからプルします。Starcraft1には長い間バグがあり、リプレイ中にRNGシードが保存されなかったため、リプレイは実際のゲームから徐々に逸脱していました。
Mooingダック

1
@MooingDuck:良い点。実際、RNGの非同期化がすぐに検出されるように、現在のRNGシードを毎ターン送信することをお勧めします。また、UIコードにランダム性が必要な場合は、ゲームロジックに使用されるのと同じRNGインスタンスからプルしないください
イルマリカロネン

3

ゲームロジックをリアルタイムから完全に独立させ、基本的にターンベースにする必要があります。そうすれば、「タイルのエネルギー変化が起こる」ターンを正確に知ることができます。あなたの場合、各ターンはちょうど50分の1秒です。

そうすれば、プレイヤーの入力のみを心配する必要があり、他のすべてはゲームのロジックによって管理され、すべてのクライアントで完全に同一になります。ネットラグや非常に複雑な計算のためにゲームが一時的に停止した場合でも、イベントはすべてのユーザーに対して同期して発生します。


1

まず、計算にIEEE-754を厳密に使用するように指定しない限り、PCのfloat / double mathは決定論的ではないことを理解する必要があります(遅くなります)

次に、これは私がそれを実装する方法です:クライアントはサーバーに接続し、時間を同期します(pingの遅延に注意してください!)(長いゲームプレイでは、タイムスタンプ/ターンを再同期する必要がある場合があります)

現在、クライアントがアクションを実行するたびに、タイムスタンプ/ターンが含まれ、不正なタイムスタンプ/ターンを拒否するのはサーバー次第です。その後、サーバーはアクションをクライアントに送り返し、ターンが「クローズ」されるたびに(別名、サーバーは古いターン/タイムスタンプを受け入れないため)、サーバーはクライアントにアクションを送信して終了します。

クライアントには2つの「ワールド」があります。1つはエンドターンと同期しており、もう1つはエンドターンから計算され、現在のクライアントターン/タイムスタンプまでキューに到着したアクションを合計します。

サーバーは少し古いアクションを受け入れるため、クライアントは独自のアクションをキューに直接追加できます。そのため、少なくとも独自のアクションでは、ネットワーク上のラウンドトリップ時間が非表示になります。

最後に、MTUパケットを埋めることができるように、より多くのアクションをキューに入れて、プロトコルのオーバーヘッドを減らします。サーバー上でそれを行うことをお勧めします。そのため、すべての終了ターンイベントにはキューでのアクションが含まれます。

私はリアルタイムシューティングゲームでこのアルゴリズムを使用し、正常に動作します(クライアントが独自のアクションを追跡する場合としない場合がありますが、サーバーpingは20/50ミリ秒と低くなります)。ドリフト値を修正するためのクライアントマップ」パケット。


一般に、浮動小数点演算の問題は回避できます。RTSでは、通常、整数/固定小数点を使用してシミュレーションと移動を簡単に実行でき、ゲームの動作に影響を与えない表示レイヤーにのみ浮動小数点を使用できます。
ペティス

整数では、八角形のボードでない限り、水平タイルを作成するのは困難です。固定小数点のハードウェアアクセラレーションがないため、フロートieee754
Lesto
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.