数か月前、同僚と私は、Web上で動作するマルチプレイヤーリアルタイムゲームを作成することにしました。 サーバーにnode.jsを使用することにしました。 このソリューションは非常に説得力のある成功をもたらしました-私たちのサーバーは、プロセスの単一のドロップまたは再起動なしで数ヶ月間機能しました。
このプラットフォームについて多くの良いことを聞いて、実際に少し遊んでみたいと思ったため、node.jsでゲームを書くことにしました。 そして、それは驚くべきものでした-私たちはすぐに話題になりました。 node.jsには、まったく異なる問題を解決できる興味深いライブラリが数多くあります。 サーバー側にノードを使用することの副次的な利点は、実際にはJavaScriptです。これは非常に使いやすい言語です。 これにより、すべてのリアルタイムゲームで発生する問題に集中することができ、あまり動的でない言語を使用する場合のように、不必要な大騒ぎ、制限、コードのコンパイルの必要がなくなりました。
Node.jsは、ピーク時でも非常に軽量な言語であることが証明されました。 このゲームでは、node.jsプロセスは1つのスレッドのみを使用し、CPUの約3〜4%のみを消費すると同時に、それぞれ独自の衝突検出エンジンを備えたゲームの8〜10個のコピーを処理しました。
初期アプローチ
マルチプレイヤーリアルタイムゲームを作成する際の最初の(単純な)アプローチは次のようになります。
var info = {}; info.position = {x:10, y:15}; info.velocity = {x:0.5,y: 0.2}; info.currentWeapon = 1; info.radius = 10; this.netChannel.send( info );
サーバーは、受信したすべてのデータパケットを接続された各クライアントに転送します。
function broadcastInfo( sendingClient, messageInfo ) { for(var aClient in allClients) { if(aClient != sendingClient) { aClient.connection.send( messageInfo ); } } }
主な問題
このアプローチでは、ユーザーが渡したユーザーの位置情報を信頼できません。 ユーザーは、自分が必要な場所にいることを常に報告し、絶対的な正確さですべての敵に陥り、健康が十分にあることを報告します。
このアプローチの別の問題は、オブジェクトの動きを正しく表示できないことです。
この効果は、跳ねるボールと呼ばれることもあります。 オブジェクトの速度に基づいて、次のパケットが到着するまでオブジェクトの位置を推定し、さらに位置を「微調整」するなどの必要性と関連しています。...ボールが移動する放物線の上部にあるとき、速度はゼロです。 したがって、速度に基づいてその動きを予測すると、位置とまったく同じポイントが得られ、ボールは空中でホバリングし、突然急激に崩壊します。
最後に、もう1つの大きな問題はパケット損失です。 図の破線で示されたパケットの1つが受信者に到達しない場合、オブジェクトは間違ったパスをたどります。 そして、さらに悪化します。 もちろん、時間はミリ秒で測定され、これは世界の終わりではありません。 しかし、現実には、現実世界のオブジェクトは瞬時に移動できないため、非常に不自然に見えます。
別のアプローチは、クライアントサーバーモデルです
ゲームを開発する過程で、この問題を実際に解決する方法を見つけることにしました。 結局のところ、誰かが既にマルチプレイヤーリアルタイムゲームを行っているのです。 興味深い情報源、特に無料のソースコードQuakeworldとValveの従業員からの技術的な説明を見つけました。
このモデルには、ゲームをモデル化する単一の信頼できるサーバーがあります。 クライアントだけでなく、入力データのみを送信します。 たとえば、スペースバーが押されたという情報のみを送信し、サーバーはこれがゲームの意味を判断します。 このモデルのおかげで、以前の実装の多くの問題を取り除きました。
世界のレンダリング
私たちの神経系は遅延に適応できます。 同じ短い遅延時間を使用してオブジェクトを描画すると、人はそのオブジェクトに簡単に適応し、遅延をまったく認識しなくなります。 問題を解決するための鍵は、Nミリ秒前のように、過去形ですべてのクライアントの世界をレンダリングすることです。 数Nは任意に選択されます。 たとえば、75ミリ秒を使用しました。
基本手順
- 最初に、クライアントがサーバーから個別の間隔で世界の高精度の変更を受信するようにシステムを構成します。
- 世界の変化を配列に保存します。
- ステージ上でプレイヤーを描画するときが来たら、現在の時間から補間時間を引いた時間にこれを行います。
- この時点を呼び出します-レンダリング時間は、実際には現在の時間から75ミリ秒を引いた値に等しくなります。
- 各レンダリングで、レンダリング時間の間に2つの変化があります。
- 変更が見つかったら、線形補間関数を使用して、オブジェクトをパス上に正確に配置します。
- パケットが失われた場合、たとえば、パケット343を受信しなかった場合(図を参照)、受信した2つのパケット342と344の間の補間を使用できます。
RealtimeMultiplayerNodeJS
私たちはオープンソースを忠実に信じているため、開発の最後に、コードを少し整理し、オープンソースプロジェクトとしてレイアウトすることにしました。 良い名前を選ぶことに特に成功しなかったので、RealtimeMultiplayerNodeJSと名付けました。
RealtimeMultiplayerNodeJSは、HTML5とクライアントサーバーモデルを使用してマルチプレイヤーリアルタイムゲームを作成するために特別に設計されたフレームワークです。 このモデルでは、ユーザーは入力データのみを送信し、ゲーム自体はサーバー上でモデル化されます。 クライアントは、現在のレンダリング時間に基づいて、2つの状態の間で世界を補間します。
プロジェクトの使用方法
- リポジトリをダウンロード
- ターミナルで、サーバー「node js / DemoHelloWorld / server.js」を起動します
- ページ「/DemoHelloWorld.html」を開きます(websocketsを使用して接続できるように、ファイルはサーバーから見える必要があることに注意してください)
物理デモンストレーション
このデモは、すべてのシミュレーションがサーバー上で発生したときに、同期された物理プロセスのアイデアを示すように設計されています。 Box2D.jsを使用して世界を作成します。 また、複数のユーザーの対話の同期、およびサーバーにメッセージを送信し、その後のクライアント側での再解釈の例を示しています。
DemoHelloWorld
最も簡単だが最も興味深いデモ。 オブジェクトは左から右に移動します。
デモサークル
単純な衝突処理エンジンCircleCollisionのデモ。衝突に関する最も単純な情報を提供し、2つのオブジェクトが衝突したときにイベントを生成します。 このデモでは、接続ユーザーがキーボードを使用して制御する特別なタイプのオブジェクトの実装も示しています。
最後に、主なアイデアを強調するために-ASCIIテクニックを使用して作成されたエンティティ補間図:
(actual time) (snapshot interval) | | | _ (rendered time) | / \ | / vv / | ------|-----|----v|---|----|----|--v---------> 0.0sec 0.1sec 0.2sec 0.3sec time | | \ / \------\/----/ | | (interpolation time)