非シューティングゲームの動きの予測


35

私は、中規模のマルチプレイヤー、約20〜30人のプレイヤーが一度に永続サーバーに接続する等尺性2Dゲームに取り組んでいます。適切な動き予測の実装を適切に行うのに苦労しました。

物理学/運動

このゲームには真の物理学の実装はありませんが、基本原理を使用して動きを実装します。入力を継続的にポーリングするのではなく、プレーヤーが制御しているキャラクターエンティティの状態を変更するために、状態の変更(つまり、マウスのダウン/アップ/移動イベント)が使用されます。プレイヤーの方向(つまり、北東)は一定の速度と組み合わされ、エンティティの速度である真の3Dベクトルに変換されます。

メインゲームループでは、「更新」が「描画」の前に呼び出されます。更新ロジックは、速度がゼロ以外のすべてのエンティティを追跡する「物理更新タスク」をトリガーし、非常に基本的な統合を使用してエンティティの位置を変更します。例:entity.Position + = entity.Velocity.Scale(ElapsedTime.Seconds)(ここで、「Seconds」は浮動小数点値ですが、ミリ秒の整数値に対して同じアプローチが機能します)。

重要な点は、動きに補間が使用されていないことです-初歩的な物理エンジンには「前の状態」または「現在の状態」という概念はなく、位置と速度のみがあります。

状態変更および更新パケット

プレイヤーが制御しているキャラクターエンティティの速度が変化すると、エンティティのアクションタイプ(スタンド、ウォーク、ラン)、方向(北東)、および現在位置を含む「アバターの移動」パケットがサーバーに送信されます。これは、3D一人称ゲームの仕組みとは異なります。3Dゲームでは、プレイヤーが動き回るにつれて、速度(方向)がフレームごとに変化します。すべての状態変更を送信すると、フレームごとにパケットが効率的に送信され、コストがかかりすぎます。代わりに、3Dゲームは状態の変化を無視し、一定の間隔(たとえば80〜150ミリ秒ごと)で「状態更新」パケットを送信するようです。

私のゲームでは速度と方向の更新の頻度がはるかに低いため、すべての状態変更を送信する必要はありません。すべての物理シミュレーションは同じ速度で行われ、確定的ですが、レイテンシは依然として問題です。そのため、通常の位置更新パケット(3Dゲームに似ています)を送信しますが、頻度はずっと低くなります-現在は250ミリ秒ごとですが、良い予測では500ミリ秒に簡単にブーストできると思います。最大の問題は、私が現在標準から逸脱していることです。他のすべてのドキュメント、ガイド、およびサンプルはオンラインで定期的に更新を送信し、2つの状態間を補間します。それは私のアーキテクチャと互換性がないようで、(非常に基本的な)「ネットワーク化された物理」アーキテクチャに近い、より良い動き予測アルゴリズムを考え出す必要があります。

サーバーはパケットを受信し、スクリプトに基づいてその移動タイプからプレーヤーの速度を決定します(プレーヤーは実行できますか?プレーヤーの実行速度を取得します)。速度が得られると、それを方向と組み合わせてベクトル(エンティティの速度)を取得します。いくつかのチート検出と基本的な検証が行われ、サーバー側のエンティティが現在の速度、方向、および位置で更新されます。基本的な調整も実行され、プレイヤーが移動要求でサーバーをあふれさせるのを防ぎます。

自身のエンティティを更新した後、サーバーは範囲内の他のすべてのプレーヤーに「アバター位置更新」パケットをブロードキャストします。位置更新パケットは、リモートクライアントのクライアント側の物理シミュレーション(ワールドステート)を更新し、予測と遅延補正を実行するために使用されます。

予測とラグ補償

前述のように、クライアントは自分の立場に対して権威があります。不正行為や異常の場合を除き、クライアントのアバターはサーバーによって再配置されることはありません。クライアントのアバターには外挿(「今すぐ移動して後で修正」)は必要ありません-プレイヤーが見るもの正しいです。ただし、移動しているすべてのリモートエンティティには、何らかの外挿または内挿が必要です。クライアントのローカルシミュレーション/物理エンジン内では、何らかの予測および/または遅延補償が明らかに必要です。

問題点

私はさまざまなアルゴリズムに苦労してきましたが、いくつかの質問と問題があります。

  1. 外挿、内挿、またはその両方を行う必要がありますか?私の「直感」は、速度に基づいた純粋な外挿を使用する必要があるということです。状態の変化はクライアントによって受信され、クライアントは遅延を補正する「予測された」速度を計算し、通常の物理システムが残りを行います。ただし、他のすべてのサンプルコードや記事とは相反するように感じられます。これらはすべて、多くの状態を格納し、物理エンジンなしで補間を実行するようです。

  2. パケットが到着したとき、パケットの位置を、一定時間(たとえば200ms)にわたってパケットの速度で補間しようとしました。次に、補間された位置と現在の「エラー」位置の差を取り、新しいベクトルを計算して、送信された速度ではなくエンティティに配置します。ただし、別のパケットがその時間間隔で到着するという前提があり、次のパケットがいつ到着するかを「推測」することは非常に困難です。特に、すべてのパケットが一定の間隔で到着するわけではないためです。概念に根本的な欠陥があるのか​​、それとも正しいのか、いくつかの修正/調整が必要なのか?

  3. リモートプレーヤーが停止するとどうなりますか?エンティティをすぐに停止できますが、再び移動するまで「間違った」スポットに配置されます。ベクトルを推定するか、補間しようとすると、以前の状態を保存しないため問題が発生します。物理エンジンには「位置Xに到達した後に停止する必要がある」と言う方法がありません。速度を理解するだけで、それ以上複雑なことはありません。「パケット移動状態」情報をエンティティまたは物理エンジンに追加するのは嫌です。これは、基本的な設計原理に違反し、ゲームエンジン全体でネットワークコードを流出させるためです。

  4. エンティティが衝突するとどうなりますか?3つのシナリオがあります-制御プレイヤーがローカルで衝突する、2つのエンティティが位置更新中にサーバーで衝突する、またはリモートエンティティ更新がローカルクライアントで衝突します。すべての場合において、衝突の処理方法は不確かです-不正行為は別として、両方の状態は「正しい」が、異なる期間にあります。リモートエンティティの場合、壁を歩いて描くのは理にかなっていないので、ローカルクライアントで衝突検出を実行し、「停止」させます。上記のポイント#2に基づいて、エンティティを「壁を通り抜けて」継続的に移動しようとする「修正されたベクトル」を計算することがあります。ポジション。ゲームはこれをどのように回避しますか?


1
3Dまたは2Dのゲームは、使用するサーバーの種類と何の関係がありますか?そして、なぜあなたのゲームで機能しないサーバーが機能しないのですか?
AttackingHobo

1
@Roy T.帯域幅のトレードオフ。帯域幅は、今日のコンピューターシステムで最も貴重なリソースです。
-FxIII

1
それは事実ではありません。オンラインゲームの大部分は応答時間に支配されています。たとえば、10Mbit回線(1.25MB / s)では、サーバーとクライアント間の遅延は20msで、1.25kbパケットの送信には20ms + 1msかかります。12.5kbパケットの送信には30msかかります。2倍の高速回線では、1.25 kbパケットは20ミリ秒+ 0.5ミリ秒、12キロバイトパケットでは20ミリ秒+ 5ミリ秒かかります。遅延は帯域幅ではなく制限要因です。とにかく、データの量はわかりませんが、50個のvector3(25x位置+ 25x回転)の送信は600バイトのみで、20msごとに送信すると30kb / sかかります。(+パケットのオーバーヘッド)。
ロイT.

2
Quakeエンジンには、最初のバージョン以降の予測があります。地震予知はそこと他のいくつかの場所で説明されています。見てみな。
user712092

1
この位置+ =速度*デルタ時間を各エンティティに対して並行して実行しますか(命令的に:コードにはエンティティの物理パラメータの2つの配列があり、1フレーム離れています。古いものをより新しく更新して交換します)?Thief 1エンジンのベースを作ったSean Barretによる反復には、いくつかの問題があります。
user712092

回答:


3

言うべき唯一のことは、この問題に関しては、2D、アイソメトリック、3D、すべて同じであるということです。3Dの多くの例を見て、瞬時速度で2Dオクタント制限入力システムのみを使用しているからといって、過去20年以上にわたって進化してきたネットワークの原則を捨てることができるわけではありません。

ゲームプレイが危険にさらされると、設計の原則は厳しくなります!

以前と現在を捨てることで、問題を解決する可能性のあるいくつかの情報を破棄しています。これらのデータにタイムスタンプと計算されたラグを追加して、外挿がそのプレーヤーの位置をより正確に予測し、内挿が時間の経過に伴う速度変化をより滑らかにできるようにします。

上記は、サーバーが制御入力ではなく、多くの状態情報を送信しているように見える大きな理由です。もう1つの大きな理由は、使用しているプロトコルに基づいています。受け入れられたパケット損失と順不同配信によるUDP 確実な配信と再試行を備えたTCP?任意のプロトコルを使用すると、奇妙な時間にパケットを取得したり、遅延したり、急いで積み上げたりして、アクティビティが急増します。これらの奇妙なパケットはすべて、クライアントが何が起こっているかを把握できるように、コンテキストに収まる必要があります。

最後に、入力が8方向に非常に制限されている場合でも、実際の変更はいつでも発生する可能性があります。250ミリ秒のサイクルを強制すると、素早いプレーヤーが欲求不満になります。30プレーヤーは、サーバーが処理するのに大きなものではありません。何千もの話をしているなら...それらのグループは複数のboxenに分割されているので、個々のサーバーは妥当な負荷のみを負担しています。

HavokやBulletのような物理エンジンのプロファイルを作成したことがありますか?これらは本当に最適化されており、非常に高速です。操作ABCが遅いと仮定し、それを必要としないものを最適化するというtrapに陥ることがあります。


賢明なアドバイスはこちら!全体像を簡単に見ることができません。この場合、TCPを使用しています。「8方向」の問題は、入力に関してはそれほど問題ではありません。補間と外挿の問題です。グラフィックスはこれらの角度に制限され、アニメーション化されたスプライトを使用します-プレイヤーが標準から離れすぎた異なる角度または速度で移動すると、ゲームプレイは「奇妙に見えます」。
ShadowChaser

1

あなたのサーバーは本質的に「レフェリー」ですか?この場合、クライアントのすべてが決定論的である必要があると思います。各クライアントのすべてが常に同じ結果になることを確認する必要があります。

あなたの最初の質問については、ローカルプレイヤーが他のプレイヤーの方向を受信すると、時間の経過とともに動きを減速させて衝突を適用できることを除いて、プレイヤーが次にどの方向を向くか、特に8方向の環境。

各プレーヤーの「実際の位置」の更新を受信するとき(サーバー上でずらして試すことができます)、はい、プレーヤーの位置と方向を補間する必要があります。「推測された」位置が非常に間違っている場合(つまり、最後の方向パケットが送信された直後にプレーヤーが完全に方向を変えた場合)、大きなギャップが生じます。これは、プレイヤーが位置をジャンプするか、次の推測位置に補間できることを意味します。これにより、時間の経過とともに滑らかな補間が提供されます。

エンティティが衝突する場合、決定論的システムを作成できれば、各プレイヤーは衝突をローカルでシミュレートでき、結果は現実からあまり遠くないはずです。各ローカルマシンは、両方のプレイヤーの衝突をシミュレートする必要があります。その場合、最終状態が非ブロッキングで受け入れられることを確認してください。

サーバー側では、レフリーサーバーは単純な計算を実行して、たとえば簡単なアンチチートメカニズムとして使用するために短時間でプレーヤーの速度をチェックすることができます。一度に1を超える各プレーヤーの監視をループする場合、チート検出はスケーラブルになりますが、詐欺師を見つけるのに時間がかかるだけです。


おかげで-特にサーバー側では、私が必要とするものにかなり近いように思えます。興味深い点の1つは、プレイヤーは8方向にロックされていますが、内部の動きは3Dベクトルであるということです。私はこれについてもう少し考えましたが、補間がまったく実装されていないという事実に苦労していると思います-私は単に非常に基本的な統合を使用し、速度に基づいて位置を更新します各更新をベクトル化します。
ShadowChaser

それを補間または予測と組み合わせる方法がわかりません。パケットで送信された更新された位置を取得し、それを固定期間(たとえば200ms)で統合し、200msでそのポイントに到達するために必要なベクトル(速度)を決定しようとしました。つまり、クライアント側でのプレーヤーの現在の誤った位置に関係なく、200ミリ秒で同じ「推定正しい位置」に到達する必要があります。結局、私はキャラクターを狂った方向に送り出しました。200ミリ秒が次のパケットまでの時間であるはずだと思いますが、これは推定できません。
ShadowChaser

間違った位置を推測されたt + 1の正しい位置に統合する前に、最初にtの正しい位置をt + 1に統合することを確認しましたか?
ジョナサンコネル

はい-元の統合に正しい位置を使用していたことを再確認しました。もともとそれはバグでしたが、それを修正しても目立った改善は見られませんでした。私の疑いは「+1」です-パケット間の時間に非常に依存する必要があります。2つの問題があります。定期的な(250ミリ秒)更新に加えて状態の変更を送信し、それらがいつ発生するか予測できません。また、特定の間隔でロックすることを嫌がります。これは、プレーヤーから遠く離れているエンティティに対して、サーバーが送信する更新を少なくすることが理にかなっているためです。パケット間の時間は変化する可能性があります。
ShadowChaser

1
固定された種類のタイムステップを含めることは、おそらく良い考えではありません。しかし、8方向の動きの不規則性を予測することは(不可能ではないにしても)非常に難しいのではないかと心配しています。それでも、クライアントの平均レイテンシを使用してt + 1を予測し、他のプレイヤーを常に新しい位置に「テレポート」するしきい値を超えることができる場合があります。
ジョナサンコネル

0

状態変更メッセージに速度を含め、それを使用して動きを予測することはできませんか?たとえば、速度が変化したというメッセージが表示されるまで速度が変化しないと仮定しますか?あなたはすでにポジションを送信していると思いますので、何かが「オーバーシュート」した場合、次のアップデートからとにかく正しいポジションになります。その後、更新中に最後のメッセージの速度を使用して既に位置を移動し、メッセージが新しい位置で受信されるたびに位置を上書きできます。これは、位置は変わらないが、ベロシティはメッセージを送信する必要がある場合(それがゲームで有効なケースである場合)、それは帯域幅の使用にほとんど影響しないことを意味します。

補間はここでは重要ではありません。たとえば、今後何かが起こるかどうか、それがあるかどうか、使用している方法などがわかっている場合です。おそらく外挿と混同されていますか?(私が説明しているのは、1つのシンプルなアプローチです)


-1

最初の質問は次のとおりです。サーバーに権限があるモデルを使用することの何が問題になっていますか?環境が2Dであるか3Dであるかが重要なのはなぜですか?サーバーが信頼できる場合、チート保護がはるかに簡単になります。

私が見たサンプルのほとんどは、動きの予測をエンティティ自体に密接に結合しています。たとえば、現在の状態とともに以前の状態を保存します。それを避けて、エンティティを「現在の状態」のみに保ちたいと思います。これを処理するより良い方法はありますか?

予測を実行するときは、クライアントでいくつかの状態(または少なくともデルタ)を維持する必要があります。これにより、サーバーから信頼できる状態/デルタを受信したときに、クライアントの状態と比較でき、必要なものを作成できます。修正。アイデアは、必要な修正の量を最小限に抑えるために、可能な限り決定論的に保つことです。前の状態を維持しないと、サーバーで何か別のことが起こったかどうかを知ることができません。

プレーヤーが停止するとどうなりますか?正しい位置に補間することはできません。彼らの位置があまりにも前方にある場合、彼らは後方または別の奇妙な方向に歩く必要があるかもしれないからです。

なぜ補間する必要があるのですか?権限のあるサーバーは、誤った動きを上書きする必要があります。

エンティティが衝突するとどうなりますか?現在のプレイヤーが何かと衝突した場合、答えは簡単です-プレイヤーの動きを止めるだけです。しかし、2つのエンティティがサーバー上の同じスペースを占有するとどうなりますか?ローカル予測により、リモートエンティティがプレーヤーまたは別のエンティティと衝突する場合はどうなりますか?それらも停止しますか?プレイヤーが移動した壁の前に予測を貼り付けるという不幸があった場合、予測は補正できず、エラーが高くなるとエンティティは新しい位置にスナップします。

これらは、サーバーとクライアントの間に競合が発生する状況であり、サーバーがエラーを修正できるように、クライアントの状態を維持する必要がある理由です。

迅速な回答で申し訳ありませんが、私は真っ向からやらなければなりません。この記事を読んでください、それは射手について言及していますが、リアルタイムのネットワーキングを必要とするどんなゲームでも動作するはずです。


いくつかの回答:*サーバーに権限がある場合、サーバーはすべての移動エンティティを追跡し、定期的にその位置を更新します。言い換えれば、物理エンジンを実行する必要があります-高価になる可能性があります。スケーラビリティは私の主要な設計目標です。*クライアント側で補間する必要があります。そうしないと、クライアントに送信されるすべてのサーバー更新によってエンティティがジャンプします。現在、私の補間は物理エンジンで行われています-速度を設定するだけです。状態またはデルタはありません。
ShadowChaser

Glennのすべての記事を読みましたが、彼はコメントで、それらはシューティングゲーム(つまり、更新頻度が高い)にのみ向けられていると述べています。彼の記事のいくつかは、私が目指している実装である信頼できるクライアントについて述べています。サーバーで補間/物理演算を行いたくありませんが、それが本当に唯一の方法である場合は気を変えたいと思います:)
ShadowChaser

1
-1。あなたが書いたものは、そのトピックに漠然と触れているだけです。あいまいです。回答は、本質的に「この長い記事を読んでいる」場合には、標準よりも低いと感じますが、手元の記事からの有用な情報は含まれません。
AttackingHobo

1
@AttackingHoboあなたに同意する必要があります。私は急いでいると言いましたが、それは言い訳にはなりません。時間がない場合は、そのままにしておく方がよかったでしょう。学んだ教訓。
ギャン別名ゲイリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.