私は、socket.ioを使用して最初のオンラインゲームを作成しています。これをagar.ioやdiep.ioのようなリアルタイムマルチプレーヤーゲームにしたいと考えています。
しかし、すべてのコンピューターを同じ速度で動作させる方法を理解しようとする問題に遭遇しました。
私はモデルについて3つのアイデアを持っていますが、どれも正しくないようで、通常のビデオゲームでそれがどのように行われるのか疑問に思っています。 (あなたは私の考えを読むのをスキップすることができます;それらは私が抱えている問題を見る方法をあなたに与えるだけです。)
サーバーは、クライアントが自分で実行し、更新をサーバーに渡して、残りのクライアントにブロードキャストすることを許可します。これには、一部のコンピューターが他のコンピューターよりも高速に実行され、更新が速くなり、画面上をより速く移動できるという問題があります。
更新するタイミングをサーバーにクライアントに通知させます。その後、最後のクライアントが応答するまで待つか(1人のコンピューターの速度が遅い場合はひどい考え)、最初のクライアントが応答するまで待つ(もう一度、各フレームの前に通信を待つ)か、できるだけ早く送信します(これは1)と同じ問題が発生するようです。
ゲームの開始時に、サーバーにクライアントに更新の速さを伝えさせます。これは、クライアントがその期間の移動を制限する責任があることを意味します。たとえば、誰かがなんとかしてその期間内にボタンを2回押した場合、ボタンを押すイベントは1つしか送信されません。これには、一部のアクション(ダブルボタンを押すなど)が無視され、対話がサーバーのクロックと一致しない可能性があるクライアントのクロックに依存するという問題があります。次に、サーバーは各クライアントを追跡し、更新が正しい時間に送信されていることを確認する必要があります。
私は 一部の調査 を実行しましたが、私が読んだ記事では、クライアントが他のクライアントよりも速く更新を送信する場合の対処法について具体的に取り上げていないようです。
私の特定のケースでは、キーボードの速度が速い(他のコンピューターよりも多くのキーボードの更新を送信するコンピューターを使用している)人々を扱っています。
プログラマは通常これをどのように扱いますか?
あなたの3番目のアイデアは、この種の問題に対する業界のソリューションとして私が考えるものに最も近いようです。
あなたが説明しているものは、一般に Ticks と呼ばれます。各ティックで、固定数のアクションが各クライアントに対して連続して処理されます。多くの場合、ゲームサーバーは対応可能な場合にいくつかの並列アクションを実行しますが、これははるかに複雑な問題です。
ティックはおそらく1/N秒の形式になります。Nは1秒あたりのティック数、またはTickrateです。このティックレートは、ユースケースに応じて、非常に頻繁または非常にまれです。私の個人的な提案は、60ティック/秒を超えるティックレートを回避することです。あなたはおそらく:)
アクションはアトミックである必要があります。たとえば、slither.ioでは、ヒットしたプレーヤーが既に移動していない限り、移動などのアクションでチェーンの切断などの処理がすぐに行われることはありません。これは、ピクセルのレベルで何かにとっては些細なことのように思えるかもしれませんが、タイルベースの動きを扱っている場合、それははるかに明白になり、公平性を保証します。プレーヤーAがタイルX、Yに移動し、プレーヤーBが現在そのタイル上にある場合、ティックの終わりまでにプレーヤーBがそのタイル上にあることを確認して、それらの間でアクションを実行する必要があります。
さらに、サーバー側で個別に行われない限り、クライアント側で計算を行わないようにします。これは、物理学によって複雑で費用がかかります(多くのゲームでは、このため、他の多くのアクションやイベントよりも物理学のティックレートが低くなっています)
参考までに、ゲームサーバーとマルチプレイヤーネットワーキングについての理解を深めるために こちらは適切なリンクです .
最後に、公平性がサーバーを台無しにしないようにしましょう。ゲームを不当にするエクスプロイトがある場合は、それらを修正します。それが少し有利なより良いコンピュータの問題であるならば、私はそれがそれほど大きな問題ではないかもしれないと言います。
次のシステムは、すべてのクライアントとサーバーがいつでもほぼ同じゲーム状態を共有するようにします。
クライアントとサーバーの両方でゲームの状態を把握します。
クライアントがコマンド(マウス、キーボードなどの入力)を使用しようとしたときに、それが有効かどうか、ゲームの状態を調べます。
の場合は、送信側クライアントで実行せずに、コマンドをサーバーに送信します。
サーバーがコマンドを受け取ったら、それが有効かどうか、ゲームの状態を調べます。
そうである場合は、サーバーでの実行が終了する予定の正確な将来の日付ですべてのクライアントにコマンドを送り返し、コマンドをクライアントに送信するための最小時間と等しい遅延の後に、コマンドによって要求されたアクションを実行します。 。次に、日付を記録します。これは、将来の予測に役立ちます。時間の変動が大きすぎる場合は、ゲームシステムの時間をより確定的にします。
クライアントがサーバーからコマンドを受け取ったら、ゲームの状態を調べて、コマンドが要求するアクションをすぐに実行し、現在の日付を見て、受け取った日付の予測と比較します。有効でない場合、クライアントは同期していません。 (同様の接続を持つすべてのクライアントが同時に受信します)
日付が以前の場合は、前のステップで問題が発生した場合は修正してください。日付が何もしないで少し後の場合、日付がかなり後である場合、クライアントは同期していません。つまり、クライアントはあまりにも遅れています。
クライアントが同期していない場合は、サーバーのゲーム状態全体のコピーを要求し、それを使用します。それが頻繁に発生する場合は、コマンドを送信するよりもコストがかかるため、修正してください。
クライアント上で、画面上にレンダリングするのは、他に何もすることがなくなったときだけです。単純なシナリオでは、render関数は現在のゲームの状態のみを入力として受け取ります。
その上で、予測システム、グループ化コマンド、差分のみのレンダリングなどを使用して、多くを最適化できます...
検証は異なる必要があります。たとえば、コマンドリクエスト/時間単位を制限するのはサーバー次第です。
これがあなたが使っているライブラリや環境の問題なのかどうかはわかりませんが、まったく間違っていると思います。
マルチプレイヤーゲームの大多数では、実際の計算を行っているのはサーバーだけです。クライアントはダムのみですIO実際のパフォーマンスの問題のみが3Dグラフィックスを描画しているマシンです。その場合、ビジュアルにのみ影響するため、クライアントが20または200 FPSで実行できるかどうかは問題ではありません。つまり、クライアントはサーバーが計算するものを「予測」しようとするかもしれませんが、それはゲームプレイの感覚を滑らかにするためだけであり、ゲームプレイ自体には実際の影響はありません。
キーボードの速度が速い
それが何を意味するのかもわかりません。ほとんどの人はローエンドキーボードの速度に追いつくことさえできないので、それはプレーヤーのパフォーマンスにどのように影響しますか?
そうでない場合、質問はかなり広範に見えます。代わりに、あなたが持っていないかもしれない「一般的な」問題を作成しようとするのではなく、単一の実際の問題に焦点を当てるべきです。