高速ゲームのマルチプレイヤー(パートI、II)





  1. パートI、II(独裁的なサーバーを持つシングルプレーヤー)
  2. パートIII(敵の出現)
  3. パートIV(ヘッドショット!)




記事「 Fast-Paced Multiplayer(Part I):Introduction」の翻訳に注目してください。



ゲーム開発自体は簡単な作業ではありません。 しかし、マルチプレイヤーゲームでは、解決する必要があるまったく新しい問題が発生します。 私たちの問題には、人間性と物理法則という2つの理由しかありません。 物理法則は、相対性理論の分野から問題をもたらし、人間の性質上、クライアントからのメッセージを信頼することはできません。



マルチプレイヤーゲームの背後にある基本的なアイデアに既に精通している場合は、最初の部分をスキップして2番目の部分に進んでください。



パートI



不正行為の問題



頭痛はすべて不正行為から始まります。



シングルプレイヤーゲームを開発している場合、ユーザーがチートを選択しても気にしないでください-彼の行動は彼にのみ影響します。 はい、おそらくプレイヤーはあなたが彼に与えたいと思った経験を得られないかもしれませんが、結局彼はゲームを買って、彼が望むようにそれを使うことができます。



しかし、マルチプレイヤーゲームでは、すべてが完全に異なります。 どんな競争ゲームでも、詐欺師はゲームを単純化するだけでなく、他の人の経験を悪化させます。 不正行為者がプレイヤーをゲームから遠ざけるので、開発者としてこれを防ぐ必要があります。



不正行為を防ぐためにできることはたくさんあります。 しかし、最も重要な原則(そしておそらく最も深い原則)は非常に単純です: プレイヤーを信頼しないでください 。 常に最悪の事態を予想する-プレイヤーあなたをだまそうとすること。



独裁的なサーバーと素朴なクライアント



この原理により、一見シンプルなソリューションに導かれます-ゲームロジックはすべて、メインサーバー上で制御され、クライアントはサーバーの現在の状態のみを表示し、コマンド(キーストロークなど)を送信します。 これは、世界をシミュレートできる唯一のサーバーであるため、通常、 権威主義サーバーと呼ばれます



もちろん、サーバーはハッキングされる可能性がありますが、このトピックはこのシリーズの記事の範囲を超えています。 ただし、独裁的なサーバーを使用すると、さまざまなチートが防止されます。 たとえば、プレーヤーの生活水準を信頼することはできません。 ハッキングされたクライアントはローカル情報を変更して、プレイヤーのライフが100,000%であることを報告できますが、サーバーはライフの10%しか存在しないこと認識しており、プレイヤーが攻撃された場合、クライアントがそれについてどう考えても死にます。



また、彼が世界での自分の位置について報告するとき、プレーヤーを信じることは不可能です。 信頼できる場合、ハッキングされたクライアントはサーバーに次のことを伝えることができます。



-私は(10、10)にいます

少し後で:



-私は(20、10)にいます



この場合、おそらく彼は壁を「通り抜けた」か、本来よりも速く動きます。



そして、これが正しいパラダイムです。 サーバーは、プレーヤーが位置(10、10)にあること知っています。 クライアントは、「1ユニットを右に移動したい」と言っています。 サーバーはプレーヤーの位置を(11、10)に更新し、必要なすべてのチェックを行ってから、プレーヤーに応答します:「あなたは(11、10)にいます」:











要約すると、ゲームの状態はサーバーによってのみ制御されます。 クライアントはアクションをサーバーに送信し、サーバーは定期的にステータスを更新してクライアントに送信し、クライアントに表示します。



ネットワークを扱う



素朴なクライアントは、スローターンベースのゲーム(戦略またはポーカー)に最適です。 また、情報がほぼ瞬時に送信されるローカル接続でもうまく機能します。 しかし、インターネット上の高速ゲームにはまったく不適切です。



物理学について話しましょう。 サンフランシスコにいて、ニューヨークのサーバーに接続しているとします。 これは約4,000キロメートルです。 光の速度より速く移動できるものはないため、最良の場合、信号は13ミリ秒に達します。 しかし、あなたがそのような良好な接続を持つことはほとんどありません。 現実の世界では、情報は直接的な方法ではなく、光の速度ではありません。

それで、50ミリ秒かかると仮定しましょう。 そして、これは事実上最良のシナリオです。 しかし、東京のサーバーに接続するとどうなりますか? しかし、通信回線が過負荷になった場合はどうでしょうか? このような場合、遅延は0.5秒に達します。



例に戻りましょう。 クライアントにメッセージを送信させます。



-右矢印をクリックしました。



サーバーは50ミリ秒後にリクエストを受信し、すぐに更新された状態を送り返します。



-あなたは(11、10)にいます



このメッセージは、さらに50ミリ秒後にユーザーに届きます。



プレイヤーの視点から、彼は矢印をクリックしてから、0.1秒間何も起こりませんでした。その後、キャラクターは最終的に1ユニット右に移動しました。 チームとその結果との間のこの遅れは取るに足らないように見えるかもしれませんが、顕著です。 そしてもちろん、0.5秒の遅れは目立つだけでなく、ゲームを完全にプレイ不能にするでしょう。







要約する



オンラインゲームは信じられないほど楽しいですが、まったく新しいクラスの挑戦と障害をもたらします。 独裁的なアーキテクチャは詐欺師には適していますが、素朴な実装ではゲームがユーザーに反応しなくなります。



将来的には、権威主義的なサーバーに基づいたシステムを作成する可能性を探りますが、プレーヤーの遅延は最小限に抑えられ、シングルプレーヤーのゲームと区別できなくなります。




パートII



はじめに



最初の部分では、権威主義的なサーバーと、サーバーにコマンドを送信し、応答に含まれる更新された状態を表示する単純なクライアントを備えたクライアントサーバーモデルを調べました。

この概念の素朴な実装は、チームと反応の間の顕著な遅延につながります。 たとえば、プレイヤーが左矢印を押すと、キャラクターは0.5秒で動き始めます。 これは、コマンドがサーバーに到達する必要があり、この後のコマンドの結果がクライアントに到達する必要があるためです。







遅延が10分の1秒に達する可能性のあるインターネットでは、最高のゲームプレイは応答せず、最悪の場合はプレイできなくなります。 このパートでは、この問題を軽減する方法を見つけるか、まったく解決します。



クライアント側の予測



一部のプレーヤーは不正行為を試みますが、ほとんどの場合、サーバーは正しいリクエストを受信します。 これは、受信した入力が正しくなり、ゲームが期待どおりに更新されることを意味します。 つまり、キャラクターがオン(10、10)で、右に移動するコマンドを送信すると、彼はオン(11、10)になります。



ゲームが十分に決定的である場合 (つまり、結果はチームと以前の状態によって決定される場合)、これを使用できます。



100ミリ秒の遅延があり、キャラクターの移動時間が100ミリ秒であるとします。 単純な実装を使用する場合、アクション時間は200ミリ秒になります。







チームが実行されると仮定すると、クライアントはゲームワールドの状態を予測できます。ゲームワールドは決定論的であるため、多くの場合、予測は正しくなります。



そのため、チームを送信して新しいゲーム状態がレンダリングされるのを待つのではなく、チームを送信して、チームが既に完了したかのように結果のレンダリングを開始できます。 そしてもちろん、サーバーからの結果、つまりゲームの「実際の」状態を待つ必要があります。これはほとんどの場合、ローカル状態と一致します。







プレイヤーのアクションと画面上の結果の間にはまったく遅延がなく、サーバーは依然として権威的です(ハッキングされたクライアントが誤ったコマンドを送信し始めた場合、画面上に何でもレンダリングできますが、これは彼らが見るサーバー上のゲームの状態には影響しません他のプレイヤー)。



同期の問題



前の例では、すべてが完全に機能するように数字を慎重に選択しました。 スクリプトを少し変更しましょう。 遅延は250ミリ秒で、ユニットごとの動きのアニメーションは100ミリ秒続きます。 そして、プレーヤーが右矢印をすばやくダブルクリックできるようにします。



現在のアプローチを使用すると、これが起こります。







新しい状態が発生したときに、t = 250 msで興味深い問題が発生しました。 クライアントはx = 12を予測しましたが、サーバーはx = 11と言います。サーバーは権威主義なので、クライアントはキャラクターをx = 11に戻す必要があります。しかし、t = 350で、サーバーはx = 12と言うので、キャラクターは再びジャンプします、しかし、今回は前進します。



プレイヤーの視点から、彼は右矢印を2回押したため、キャラクターは2ユニット右に移動し、50ミリ秒そこに立ち、1ユニット左にジャンプし、100ミリ秒そこに立ち、1ユニット右にジャンプしました。 もちろん、これはまったく受け入れられません。







サーバーネゴシエーション



この問題を解決する鍵は、クライアントが現在のゲームの世界を見ていることを理解することにあります。 サーバーが更新を送信する頃には、まだコマンドの一部を受信して​​いませんでした。



しかし、この問題を回避することはそれほど難しくありません。 クライアントからの各リクエストに彼の番号を追加しましょう。 サーバーは、応答時に、最後に処理されたリクエストの番号を追加します。 そして、サーバーに送信されたすべてのコマンドのコピーをクライアントに保持しましょう。







したがって、t = 250では、クライアントは「x = 11、最後のコマンド#1」を受け取ります。 クライアントは、#1までのすべてのコマンドを削除しますが、サーバーがまだ知らない#2のコピーを残します。 サーバーから受信した状態(x = 11)を適用し、サーバーにまだ表示されていない入力を適用します。 この場合、#2は「右側に1ユニット」です。 最終結果はx = 12で、これは真です。



次に、t = 350で、サーバーから新しい状態が発生します:“ x = 12、last command#2”。 クライアントは、#2までのコマンドのすべてのコピーを削除し、状態x = 12(何も変更されていない)を適用します。 未処理のコマンドはもうないので、これはすべて正しい結果で終了します。







まとめ



動きの例を調べましたが、ほとんどすべてに同じ原理が当てはまります。 たとえば、敵のキャラクターを攻撃するとき、血とダメージを反映した数字をすぐに表示できます。



ただし、サーバーが更新された状態を送信するまで、キャラクターの寿命を実際に更新しないでください。



ロールバックするのは必ずしも簡単ではないゲーム状態の複雑さのため、たとえ彼の生活水準が0未満に下がったとしても、キャラクターを殺す価値はないかもしれません。 攻撃の直前に他のプレイヤーが治癒したが、サーバーがまだ報告していない場合はどうなりますか?



ご注意 perev。 キャラクターをすぐに殺しますが、永続性(状態をロールバックする機能)を提供します。 これにより、サーバーとクライアントの両方で実行される移植可能なコードを簡単に作成できます。 いずれにせよ、後で見るように、あなたはそれをしなければなりません。



これはすべて興味深い結論につながります-世界が絶対に決定的であり、不正行為者がいない場合でも、クライアントによって予測された状態とサーバーから送信された状態が一致しない可能性がまだあります。 これは1人のプレーヤーには不可能ですが、複数のプレーヤーが同時にプレイするような問題を再現するのは非常に簡単です。 これは次の記事のトピックになります。



要約する



権威主義的なサーバーを使用する場合、プレーヤー応答性の錯覚を与える必要がありますが、実際にはサーバーが実際に入力を処理するのを待っています。 これを行うために、クライアントはすべてのチームの結果をシミュレートします。 サーバーから更新が到着すると、サーバーの現在の状態と処理されていないコマンドに基づいて状態が更新されます。



All Articles