私はJavaでゲームを作っていますが、うまくいっています。早い段階でマルチプレイヤーを実装したいので、ゲーム全体をマルチプレイヤーに移植するのではなく、さまざまな機能がたくさんある場合に構築します..クライアント/サーバーアプリケーションにしたいと思います。これで、マルチプレイヤーをどのように、または何を実装するかがわかりました。ソケットとすべてに関する Java チュートリアルを読んで、それらをテストし、(テスト プロジェクトで) 接続に成功しました。ここからどこへ行けばいいのかわからない。たとえば、さまざまなプレイヤーがマップ上のどこにいるか、またはプレイヤーがまったくいない場合でも、どのように転送するかわかりません..ライブラリを使用するか、自分で行うか、または何を行うべきかわかりません...
3 に答える
これはかなり幅広い質問であり、物事を行うには複数の方法がありますが、これが私の見解です。免責事項: 私は、モバイル マルチプレイヤー ゲーム会社のサーバー システム アーキテクトです。私は自分自身をこれらの専門家とは考えていませんが、仕事と趣味の両方である程度の経験があります (私は 2004 年になんと 255 人のプレイヤーをサポートした最初の「MMORPG」を書きました)。正しい方向に。ここでのほとんどの概念については、Google や Stackoveflow などを使用してさらに調査することをお勧めします。これは、ゲーム ネットワーキングに必要なものの「10000 フィート ビュー」にすぎません。
作成しているゲームの種類 (ファースト パーソン シューティング ゲームのようなリアルタイム ゲームとチェスのようなターン制ゲームを考えてください) に応じて、基盤となるトランスポート レイヤー プロトコルの選択が重要になります。Matzi が示唆したように、UDP を使用するとレイテンシが低くなります (ヘッダーが TCP よりも小さいため、パケットのオーバーヘッドが低くなります) が、宛先へのパケットの配信が保証されないという欠点があります。送信したデータが実際にクライアントに到達したかどうか、または複数のパケットを続けて送信した場合は、データが正しい順序で到着したかどうかを確認することはできません。到着したデータを個別のメッセージで確認することで「信頼できる UDP」プロトコルを実装できます (ただし、確認で UDP が使用されている場合は、それらも失われる可能性があります)。少なくとも部分的に) 低レイテンシーと低オーバーヘッドを失います。一方、TCP はデータの配信と順序が正しいことを保証しますが、パケットの確認応答とオーバーヘッドによりレイテンシが高くなります (TCP パケットのヘッダーは大きくなります)。UDP パケットは「別個のエンティティ」のようなものであると言えますが、TCP は継続的で中断のないストリームです (あるメッセージの終わりと別のメッセージの始まりを区別する何らかの方法が必要です)。
両方を使用するゲームがあります。プレーヤーの死亡など、絶対にクライアントに送信する必要がある重要なデータ用の別の TCP 接続と、プレーヤーの現在の位置などの「ファイア アンド フォーゲット」タイプのデータ用の別の UDP 接続 (位置が別のクライアントに到着せず、プレーヤーが移動している場合、データを再度送信する意味はあまりありません。データはおそらく既に古くなっており、しばらくして別の更新が行われるからです)。
トランスポートに UDP や TCP を選択した後でも、TCP/UDP パケットが移動するデータ (「ペイロード」) をエンコードおよびデコードするカスタム プロトコルが必要になる場合があります。ゲームの場合、当然の選択はバイナリ プロトコルです。(対 HTTP のようなテキストベースのプロトコル)。単純なバイナリ プロトコルでは、たとえば、メッセージに含まれる合計バイト数、データの種類、データ フィールドの長さ、およびフィールドの実際のデータをマークできます (メッセージごとのフィールド数について繰り返します)。これは少しトリッキーな場合があるため、少なくとも最初は、メッセージ オブジェクトをシリアル化および逆シリアル化するようなものを使用してから、既存のプロトコルを調べるか、独自のプロトコルをクックすることができます (実際にはそれほど難しくありません)。基本的なデータ型 (String、int、float など) のエンコードとデコードが機能し、一部のデータが移動するようになったら、独自の高レベル プロトコルを設計する必要があります。お互いに話すために使用します。これらのメッセージは、「プレーヤーがゲームに参加しました」、「プレーヤーがゲームを離れました」、「
リアルタイム ゲームでは、プレーヤーの位置を予測するなど、他にもいくつかの課題があります (クライアントが送信したデータは、別のプレーヤー クライアントに到着した時点で数百ミリ秒前になる可能性があることを思い出してください。そのため、どこに「推測」する必要があります)プレーヤーは到着時のものです)。「ゲームの推測航法」や「ゲームのネットワーク予測」などをグーグルで検索してみてください。また、Gamasutra にはかなり良い記事があります: Dead Reckoning: Latency Hiding for Networked Games、おそらく他にもたくさん見つかるでしょう。
もう 1 つ考慮する必要があるのは、サーバー側コードの同時実行性です。多くの人は、良好なパフォーマンスを達成するには Java NIO を使用する必要があり、接続ごとにスレッドを使用するのは良くないと言うでしょうが、実際には少なくとも Linux では Native Posix Thread Library (NPTL、ほとんどすべての最新の Linux ディストリビューションで使用されます)ボックス)、状況は逆です。参照用に、ここを参照してください: Java マルチスレッド サーバーの記述 - 古いものは新しいものです。何千人ものユーザーがいる 10,000 以上のスレッドを実行しているサーバーがあります (もちろん、いつでも、これらのスレッドの大部分はスリープ状態で、クライアント メッセージまたはクライアントに送信されるメッセージを待機しています)。
最後に、ゲームに必要な計算能力と帯域幅を測定する必要があります。このためには、特定の (サーバー?) ハードウェアがソフトウェアでどれだけの負荷を負うことができるか、およびゲームが引き起こすトラフィックの量を測定する必要があります。これは、サーバーでサポートできるクライアントの数と、必要なネットワーク接続の速度 (および 1 か月あたりのトラフィック割り当て量) を決定するために重要です。
これがあなたの質問のいくつかに答えるのに役立つことを願っています.
まず、マルチプレイヤーゲームはデータ転送にUDPを使用します。これには多くの理由があります。たとえば、ラグが小さいなどです。ゲームに集中的なアクションが含まれていて、迅速な反応が必要な場合は、UDPに基づいて何かを選択する必要があります。
おそらくウェブ上でゲームをするための解決策はありますが、独自の実装を書くこともそれほど難しくありません。それで問題が発生した場合は、ゲームの残りの部分で問題が発生する可能性があります。ネット上またはJava内にさえ、ゲーム指向ではないライブラリとソリューションがありますが、それらはほとんどの場合、ゲームのように高速なもののために設計されていません。たとえば、リモートプロシージャコールには、コストのかかるシリアル化が含まれ、実際に必要なものよりもはるかに大きなパッケージが生成される可能性があります。これらは便利なソリューションですが、ゲームを考慮するとパフォーマンスが低く、通常のビジネスアプリケーションではありません。
たとえば、20人のプレーヤーがいる場合、それぞれに座標、状態、そしてもちろん移動するオブジェクトがあります。ラグをあまり発生させないためには、1秒あたり少なくとも20回の更新が必要です。これは、大量のトラフィックを意味します。ユーザー入力のある20*20の着信メッセージ、および多くの情報を含む20*20の発信メッセージ。計算する。最適なパフォーマンスを得るには、すべてのプレーヤーとできるだけ多くのオブジェクトデータを1つのパッケージに圧縮する必要があります。これは、バイトストリームに非常に簡単にシリアル化できる小さなデータパッケージを作成する必要があり、実行可能な情報のみが含まれている必要があることを意味します。一部のデータが失われた場合でも問題はありませんが、重要な情報を処理して目的地に確実に届くようにする必要があります。たとえば、プレイヤーが自分の死についてのメッセージを見逃したくない場合です。
信頼性が高く使いやすいネットワーク「ライブラリ」をC#で作成しました。これは大きな作業ではありませんが、周りを見回して適切に構築することをお勧めします。これはこのトピックについての良い記事です、それを読んでください。外部ライブラリを使用する場合でも、それが何をしているのか、どのように使用するのかを把握しておくとよいでしょう。
VM 間の通信に関しては、 RMIほど簡単にはなりません。RMI を使用すると、まったく別のコンピューター上のオブジェクトのメソッドを呼び出すことができます。オブジェクト全体を引数および戻り値として使用できます。したがって、あなたの移動をサーバーに通知するのは簡単server.sendMove(someMoveObject, somePlayerObject, someOtherObject)
です。
出発点を探しているなら、これは良いものかもしれません。