ネットワークゲームに関する記事まとめ

ツイッターの方でネットワークゲームに関する話題が上がったので、以前投稿したネットワークゲームに関する物をまとめてみました。これらの記事はXNA向けに書かれた物ですが、基本的な考え方やテクニックはどのプラットフォームでも同じなので参考になれば幸いです。 ネットワーク その1 レイテンシ ネットワーク その2 パケットロス ネットワーク その3 帯域 ネットワーク その4 帯域 ボイスチャットについて ネットワーク その5 圧縮 ネットワーク その6 量子化で圧縮 ネットワーク その7 ビットフィールドで圧縮 ネットワーク その8 算術符号化圧縮 ネットワーク その9 究極の圧縮方法 ネットワーク その10 オブジェクト所有権 また、AppHubの方にあるネットワーク関連のサンプルへのリンクもまとめてみました。 ネットワーク予測 ネットワーク アーキテクチャー: クライアント/サーバー ネットワーク ゲームの状態管理 ネットワーク アーキテクチャー: ピア ツー ピア 招待 ネットワーク ロビーおよびチャット用アイコン Net Rumble (英語版) HTTP マルチプレイヤー: Tic Tac Toe…

0

リモートコマンド

ねことプログラマーとXNAの友好な関係の為に デバッグコマンドはゲーム開発する上では欠かせないツールの一つですがXbox 360上で使用するにはUSBキーボードが必要になります。私の場合は新し物好きで定期的にキーボードを買い換えるので家の中には使っていないキーボードが幾つかあり、いならくなったキーボードを使っていました。 ですが、最近になって11年物のTVが写らなくなってしまい、ようやっとHDTVというものを購入しました。新しいTVの画面上で自分の作ったゲームが動いているのを見るのは楽しいので、リビングルームでソファに座りながらプログラムするという機会が増えました。 そこで、新たに起きた問題が発生しました。その問題を図式化したのが以下の図です。 ソファーに座っていると、ねこさん達が私の膝の上に乗ってきます。これから寒くなってくる時期には暖かいねこが膝の上に乗ってくるというのはありがたいものです。ですが、デバッグコマンドを使おうとするときに問題が発生します。デバッグコマンドを使用するときにはXbox 360に接続されたキーボードがあるところまで移動しないといけません。その為には膝の上のねこをどける必要があるのですが、せっかく寄ってきたねこさんを無下にする訳にもいきません。 USB延長ケーブルで手元にキーボードを持ってくるという方法もありますが、コントローラーも無線、Xbox 360へのゲームの転送もWiFi使っているので無線という環境なので、延長ケーブルを使うというのはスマートではありません。   リモートコマンド そこで新たに追加したデバッグコマンドがremoteコマンドです。remoteコマンドはWindowsとXbox 360上で同じゲームを実行している時にNetworkSession機能を使い、Windows上で動作しているゲームで入力したコマンドをXbox 360で実行するというものです。 下図はWindows上でremoteコマンドを実行した状態のものです。 リモートコマンドの実行が成功するとデバッグコマンドがリモートコマンドモードへと移行し、今まではCMD>と表示されていたものが[Client]と表示されます。この状態で入力されたコマンドはXbox 360側へと送られ実行され、実行された結果はWindows側へと送られます。リモートコマンド状態から抜け出すにはquitコマンドを使用します。 ゲームへの追加以下のようにコンポーネントを追加するだけです。Windwos Phoneには対応していないので、#ifdefで括っておくと良いでしょう。 #if WINDOWS || XBOX // リモートデバッグコマンド「remote」の追加 Components.Add(new RemoteDebugCommand(this)); #endif NetworkSessionを使っているのでGamerServicesを初期化する必要があるのですが、remoteコマンドはGamerServicesを使っていない場合は自動で初期化するので、GamerServicesを使っていないゲームでも特別なコードを書く必要がなく使えます。また、既にNetworkSessionを使っている場合にはゲーム側で作ったNetworkSessionをRemoteDebugCommand.NetworkSessionへ設定し、文字列データを受け取った場合にRemoteDebugCommand.ProcessRecievedPacketメソッドを呼び出すようにすることで共存できるようになっています。 ただし、最低でもローカルプロファイルがサインインしている必要があります。 ここで見逃しがちなのが、ローカルプロファイルの作るにはプロファイルの生成を選択した後の画面で下の方にスクロールさせた場所にあるリンクをクリックする必要があるということです。   3.1の頃からあった 実のことを言うと、remoteコマンドはデバッグサンプルの3.1版を公開したときに既にあった機能でしたが、いままで説明するのをすっかり忘れていました(汗)。そんな訳で、このリモートコマンド機能は前日に公開した4.0版のデバッグサンプルに既に含まれており、リモートコマンドも登録してあるので、そのまま使えるようになっています。   これで私は膝の上に乗っているねこさんを降ろすことなくデバッグコマンドが使えるようになりました。 と、いうのは半分冗談で、実はリモートコマンドにはもう一つの重要な役割があります。 つづく

0

招待サンプル

今日は招待サンプルを紹介します。 Xbox Live!の機能のひとつに、フレンドリスト内の友達と一緒にネットワークゲームをプレイしたいときに誘える招待機能があります。XNA GS 3.0ではこの招待機能がサポートされています。招待するケースとしては以下の二つのケースがあります。 同じゲームをプレイしている時(In-Title Invites) 違うゲームをプレイしている時(Cross-Title Invites) XNA GS 3.0上では、このどちらのケースでもNetworkSession.InviteAcceptedイベントが発生します。このイベントを受け取った時に、NetworkSession.JoinInviteメソッドを呼ぶことで招待された側が招待した側のセッションに接続できるようになっています。以下はイベントハンドラへの登録と、ハンドラ内での処理の例です。NetworkSession.InviteAccepted += InviteAcceptedEventHandler;void InviteAcceptedEventHandler(object sender, InviteAcceptedEventArgs e) { // 現在のセッションから抜ける if (networkSession != null) { networkSession.Dispose(); networkSession = null; } // 招待されたセッションに入る networkSession = NetworkSession.JoinInvited(maxLocalGamers); } 同じゲームをプレイしている場合は、通常のNetworkSessionの振る舞いと殆ど同じですが、違うゲームをプレイしている場合は振る舞いの仕方が変わってきます。ただし、これらの処理は全てOS側で行われるのでゲーム開発者は前述のInviteAcceptedイベントの実装をするだけです。 Xbox 360上で同じゲームが既にインストールしている場合は自動的にゲームが起動され、その直後にInviteAcceptedイベントが発生します。 クリエーターゲーム(開発中のゲームやピアレビュー中のゲーム)の場合は、招待した人が同じゲームを持っていない場合に、その旨を伝えるメッセージが表示されます。 コミュニティゲームの場合、ユーザーがそのゲームを持っていない場合はマーケットプレースに自動的に移動、該当するゲームをダウンロードするかのメニューが表示され、ダウンロード後に自動的にゲームが起動されます。 Windows上の場合、上記のように自動的なダウンロードや起動するといった機能がないので、ユーザーが手動で行う必要があります。 テスト時の注意 招待機能はXbox Live!の機能なので、ネットワークゲームのテストの様にシステムリンク(ローカル)を使ってテストできるのと違い、クリエータークラブ会員になっているゲーマータグが二つ必要なことに注意してください。

1

ネットワーク その10 オブジェクト所有権

クライアント/サーバーとピア・ツー・ピア型のどちらのネットワーク形態が優れているのかを議論するのが好きな人たちがいます。しかし、個人的にこの議論は間違ったものだと思います。誰が「どちらか一方のネットワーク形態を選ばなくてはいけない」と言ったんですが?私は両方の長所を活かしたハイブリット形式の大ファンです。 ネットワークプログラミングは妥協との戦いです。100%の正確さと、ラグがまったく無いという2つの状態を両立することは不可能で、トレードオフをしなければいけません。 時にはラグが多少大きくっなっても正確さをとる場合もあれば、逆に正確さを犠牲にして高いレスポンスを必要とする場合もあります。 100万ドルの問題:  どのマシンがどのデータを管理するか? ゲーム内のそれぞれのデータについてのコスト/実益の分析を以下の質問に答えることでできます。 そのデータは独立したものですか、もしくはそれ以外のデータと状態を一致させる必要がありますか? 全プレイヤーがそのデータのラグに対して同等に気にする必要がありますか、それともひとりのプレイヤーが他のプレイヤーよりも気にしなければいけませんか? そのデータのラグはどれだけ重要なものですか?ゲームデザインを変更することによって、その重要度を低くはできませんか? データ変化を予測することはできますか?間違った予測をした場合、問題になりますか、それともエラーをスムースに訂正することができますか? この質問の答えによって、どのようにデータを管理すればいいのかが判ります。 沢山のデータの整合性が必要な場合、それらは1台のマシンによって管理されるべき もしひとりのプレイヤーがデータのラグに対して他のプレイヤーより気にしなくてはいけない場合、そのデータはそのプレイヤーのローカルマシンによって管理されるべき ラグが大きな問題となる場合は、予測アルゴリズムを適用するべき 予測ミスが大きな問題となる場合は、予測アルゴリズムは適用すべきではない 1と2の質問はどのマシンがデータを管理するかの指標になり、3と4の予測アルゴリズムの適用についての質問は別々のものだということに注意   例えば: スペースシップの移動 スペースシップを操作しているプレイヤーにとってレイテンシは重要な問題。 それぞれのマシンでスペースシップの位置が多少ずれていても、大体の位置があっているのなら問題はない。最悪ケースとしては2つのスペースシップがぶつかったときに、それぞれのマシンでは多少違った方向に跳ね返るが、ゲーム的には問題はない。 多少の予測ミスは問題にならない。間違った位置から正しい位置へと、誰にも気づかれること無く補正することができる。 結論:それぞれのスペースシップはそれぞれのローカルマシンによって管理されるべき。予測アルゴリズムを適用する。   レースの勝敗 レイテンシは特に重要ではない。あなたがゴールラインを通過してから0.5秒後に結果が表示されても大丈夫(カッコイイアニメーションやカメラワークで、このレイテンシを隠すことができる)。 それぞれのマシンでレース結果が違うのはダメ。勝者は常にひとり。 間違った予測結果は大問題。「優勝おめでとう!!」と表示されて少し経ってから「やっぱり2位でした~」と表示されるのはひどすぎる。 結論:1台のマシンがレース結果を決めるべき。予測アルゴリズムは適用しない   死亡判定 殺されたプレイヤーにとってレイテンシは重要だが、他のプレイヤーにとっては特に重要ではない。 マシン毎に違った結果になるのは問題。もし私が死んでいるのなら、他のマシンでも死んでいなくてはならない。 予測ミスは許されない。「ぐはぁ、やられたー」と叫びながら派手なアニメーションが再生され、血飛沫を撒き散らしながら地面を転がった挙句に「ぐふぅ」と事切れた数秒後に何事もなかったのように生き返るのは問題。 結論:それぞれのマシンがローカルプレイヤーの死亡判定をするべき。他のマシンは死亡判定の予測をしてはいけない。 追記:ここでは視覚的な予測を適用することができる。例えば、私のマシンがヘッドショットだと判定するが、ヘッドショットされたプレイヤーの位置が100%確実ではないので死亡アニメーションを再生することはできない。その代わりに「ダメージを受けた」というアニメーションを再生することができ、視覚的なフィードバックを即座に得ることができる。もし、ヘッドショットを決めたプレイヤーから死亡か確定したという連絡が届いた場合は死亡アニメーションへスムースに変化させることができる。仮にこの予測が外れた場合でもダメージアニメーションをキャンセルして通常のアニメーションに戻すことができる。   ビークル(乗り物)に乗る 私は一度、キャラクターが徒歩で歩き回り、いろんなビークルに乗り降りすることができるゲームのプロトタイプ製作をしたことがありました。ビークルに乗っている間は「スペースシップの移動」の例で説明したのとまったく一緒です。ただビークルに乗ると言うのは違います。一度に複数のプレイヤーが運転することはできませんから! この問題を解決するために、まずどのマシンがどのオブジェクトに対して所有権を持っているのかを表すデータをつくりました。これで動的にビークルの所有権の変更ができます。次にどのマシンが所有権を持っているのかを決めなければいけません。 ビークルのステート(操作、物理シム、衝突判定等)は、そのビークルを運転しているプレイヤーがいるマシンによって管理されます。それ以外に、この所有権を誰が持つべきを決める管理マシンを決めました。もしビークルに誰も乗っていない場合は、管理しているビークルの数が最も少ないマシンに所有権があります。 ビークルのとなりに立ち「乗る」ボタンを押した時に以下の処理をします。 クライアントマシンは「乗せてください」というメッセージをビークル所有権管理マシンに送ります クライアントマシンではキャラクターがビークルに乗り込むアニメーションが再生されますが、まだ所有権がないので運転することはできません。 通常 ビークル所有権管理マシンは要求があったクライアントマシンに所有権を渡します ビークル所有権管理マシンからの「乗って良いよ」というメッセージはキャラクターんがビークルに乗り込むアニメーションが終わるまでに届き、ビークルを問題なく運転することができます。この乗り込むアニメーションはラグを隠蔽するわけです。 二人同時に乗り込もうとしたとき ビークル所有権管理マシンは「すまん、君は乗れん」というメッセージを一方に送ります 乗り込み要求を断られたキャラクターは乗り込みアニメーションをキャンセルしてビークルに乗り込む前の位置にもどされます。 先に乗り込み要求を申請したキャラクターはビークルを運転することができます。   ビークルを運転しているときはピア・ツー・ピア形式ですが、大事な決定をするのはクライアント/サーバー形式になっています。 アイディアとしては大事な決定を下す単一の管理マシンが全てのデータを管理する必要がないということです。マシンAはビークルの乗り降りを管理して、マシンBはパワーアップアイテムの管理、そしてマシンCはセッション終了時に誰が勝敗を決定するといった感じに管理するものを割り振ることができます。    …

1

ネットワーク その9 究極の圧縮方法

今まで紹介してきた狡猾な圧縮方法より効果的なデータ圧縮方法があります。それはデータ自体を送らないということです。 もちろん、まったくデータを送らないのでは相手側との同期ができません。でも、時には同期すること自体が重要ではない場合があります。 2つのルール ゲームプレイに関連するものは同期しなければならない 殆どの物はゲームプレイに関与しない   効果音やアニメーションは殆どの場合は同期する必要がありません。もし、ネットワークを介してキャラクターが前に走って移動する場合、それぞれりマシンではその情報を元に、走るアニメーションを再生させ、そのアニメーションに合わせて靴音を鳴らすことができます。靴音が鳴るタイミングが多少ずれていてもゲームプレイには関係ないのでネットワークを介して足音を同期させる必要はありません。 ケーススタディ:MotoGPでは5%の確立で相手を抜き去った場合、抜いた相手に向かって手を振り上げるアニメーションを再生します。クラッシュした場合、ライダーが吹き飛ぶ複数のアニメーションの中からランダムで再生され、ネットワークにはどのアニメーションを再生したかという情報は送られません。 あるプレイヤーから見ると手を振り上げたアニメーションをしているように見えますが、他のプレイヤー視点からはアニメーションが再生されていないという場合があります。クラッシュシーンでライダーの転がるアニメーションはそれぞれのプレイヤーによって違うアニメーションが再生されます。 これらをネットワークを介して同期するにはネットワーク帯域が足りませんでした。しかし、これらのアニメーションはゲームプレイには関係無いものなので、誰も同期していないということには気づきませんでした。 同じように、FPS等のゲームでは撃った弾の位置、壁に当たったときにできる弾痕の位置、飛び散るガラス片や薬莢の位置を同期する必要はありません。ここでネットワークに送る情報は単に「私はゲーム内で銃を撃っている」というブーリアン(bool)の情報だけです。このシンプルな情報を元に、各マシンではそれぞれにシミュレーションを行います。多少の違いが起きても、それらの平行世界で起きている事柄がほぼ同じである限りは問題がありません。   一度、この「データをネットワークに送らなくても良いんだ」という考え方が身につくと、いろんな事ができるようになります。 ケーススタディ:Moto GPで広告看板やパイロンはコース脇に配置されています。もしバイクがこれらのものと衝突した場合、バイクと一緒に吹き飛びます。もし、ゆっくりとした速度でぶつかった場合は看板やパイロンを押し動かすことができます。 殆どのプレイヤーはレースに勝つことが目的で真剣にレースに参加しますが、中にはレースの邪魔をしようとするグリファー(訳注:オンラインゲームで嫌がらせをするプレーヤーのこと、英語ではGriefer、griefには深い悲しみ、悲痛といった意味があります)がいます。グリファー達はコースを逆走したりして、どうやったらレースをメチャクチャにできるのかを競っています。 グリファー達は広告看板やパイロンをコースの中央に動かしてバリケードを築けることに気付きました。これで真剣にレースをしているライダー達がバリケードに衝突して派手にほこりを撒き散らしながら吹き飛ぶ筈です。 でも、実際にはグリファー達の思い通りにはいきませんでした。 広告看板やパイロンの位置情報はネットワークを介して送られてはいませんでした。単に充分なネットワーク帯域が無かったからです。 以下は実際にあったことです。 あるグリファーはバイクを前後に動かしながら、ゆっくりと障害物を押し進めます ピア・ツー・ピアを使っていたので、そのグリファーは自分のバイクの動きを完全にコントロールできます。 他のマシンでは予測アルゴリズムを使っているので、グリファーのバイクの座標は大体一緒ではありますが、完全に一緒ではありません。 グリファーのマシン上ではグリファーの思い通りに障害物はコースの真ん中へ移動します。 他のマシンでは最初の押しが少しだけ左にずれました。この段階では障害物の位置はグリファーが移動させた位置に近いですが一致している訳ではありません。更にグリファーが障害物を押し進めるうちにグリファーが実際に移動させた位置とはズレが生じ、最終的に他のマシン上では障害物はグリファーと当たらない位置に移動して、そのまま動かなくなります。それらの障害物の位置は同期されていないので、この矛盾は修正されません。 結果 グリファーのマシン上では立派なバリケードが完成、他のマシン上ではバリケードができていない レースを真剣にしている人達のマシン上ではバリケードが無いので問題なくレースを楽しむことができます。 一方、グリファーのマシン上では立派なバリケードがあるので他のプレイヤー達はグリファーの思惑通りにバリケードに衝突してライダーは派手に中に舞い、バイクは火花を飛ばしながら滑っていきます。そのしばらく後に新しい情報がネットワークから届きます。あれ?実際には衝突は起きていなかった?この時、吹き飛んだはずのプレイヤーはコース上に配置され、問題なくレースは続行されます。 100万人ものプレイヤー達が何時間もプレイしたにも関わらず、誰もこの矛盾には気づきませんでした。 グリファーは盛大なクラッシュシーンを見て喜び、 真剣にレースをしている人達は無事にレースを終えることができ、 みんないつまでも幸せにレースを楽しんだとさ、めでたしめでたし 原文:http://blogs.msdn.com/shawnhar/archive/2008/01/01/network-compression-just-say-no.aspx

1

ネットワーク その8 算術符号化圧縮

算術符号化は解りづらく、めったに使われないツールのひとつですが、時々その威力を発揮します。例えるならネットワークデータ圧縮における変なサイズの六角レンチです。 算術符号化はクールです。なぜなら、あまり知られていないし、一見すると動かないように思えるものだからです。パーティーで女の子に好印象を持たせるのに活躍します(訳注:そうか?) 以下のデータがあったとします。 enum Species // 種類 { Camel, // ラクダ Cat, // ねこ Caterpillar, // いもむし Cheetah, // チーター Chimpanzee, // チンパンジー Cobra, // コブラ Cormorant, // 鵜(う) Cougar, // クーガー Coyote, // コヨーテ Crab, // カニ Crocodile, // ワニ } Species animalA; Species animalB; bool whoWon; (そう、私たちは世界中の男の子が一度は思う「クーガーとワニはどっちが強いか?」という疑問に答えるためのゲームを作るんだ(訳注:ねこが最強))   ビットフィールド圧縮を使った場合、animalAとanimalBの変数には11種類の動物がいるので、その情報を格納するのにそれぞれ4ビットが必要になり、誰が勝ったのかという情報で1ビット必要になるので合計で8ビットを超えてしまいます。 でも、ちょっと待って animalAに格納する値の組み合わせは11種類、animalBも11種類、そしてwhoWonには2種類の値です。全ての組み合わせを考えると11× 11× 2の合計242通りになります。これならbyteに収まりそうです。…

3

ネットワーク その7 ビットフィールドで圧縮

ビットフィールドは古くから知られている素晴らしいデータパッキング手法です。C#プログラマーがビットフィールドを使う機会は非常に少ないですが、ネットワークパケットの圧縮にはもってこいなので、この機会に使ってみましょう。 バイトは8ビット、intは32ビット。でも、送るべきデータが8ビットや32ビットの倍数にならないときはどうします?例えば以下のようなデータを送るとします。 bool isAlive; // 生きているか? bool isFiring; // 撃っているか? enum Species // 種類 { Camel, // ラクダ Cat, // ねこ Caterpillar, // いもむし Cheetah, // チーター Chimpanzee, // チンパンジー Cobra, // コブラ Cormorant, // 鵜(う) Cougar, // クーガー Coyote, // コヨーテ Crab, // カニ Crocodile, // ワニ } packetWriter.Write(isAlive); packetWriter.Write(isFiring); packetWriter.Write((byte)species)   これで3バイトになりますが、実際にはそんなにいりません。ブーリアン型は1ビットしか必要としませんし、生物の種類も11種しか居ないので4ビットで足ります。 それぞれのフィールドにどれだけのビット数が必要かが判れば、ビットシフトすることによって複数のビット群を1つbyteやintにまとめることができます。使いやすいように、指定されたビット群をbyteやintといった値にまとめるメソッドを作るといいでしょう。…

2

ネットワーク その6 量子化で圧縮

ビット数の少ない方が多いものより消費するスペースは小さくなります。 もしint型の値が0~100までの範囲しか取らないと判っているのなら、そのまま4バイトのint型として送るより、byte型にキャストして送ることができます。 場合によっては値が表す範囲を値をずらすことによって減らすことができます。例えば、キャラクターの高さのデータを送る必要があり、高さはcmで表されるとします。このゲーム中のキャラクターの高さの範囲は、ドワーフ(100cm)から巨人(300cm)まであります。 300という値はbyteで表現できる範囲(0-255)を超えているので、キャストすることはできません。 しかし、100cm以下のキャラクターが存在しないと判っているのなら、100cmを基点とすることで値の取る範囲を小さくすることができます。PacketWriter.Write( (byte)( height – 100 ) );   受信側では値を使う前に100を足すだけです。これで、キャラクターの高さの範囲は0-200になり、byteにキャストすることができます。   他にもスケーリングすることで値の取る範囲を小さくすることができます。例えば、浮動小数点で表されるラジアン角度を送る場合があるとします。ラジアンで円を表す範囲は0~2πになります。この値を0~255の範囲に収まるようにスケーリングすれば、byteにキャストして送ることができます。 float rotationEncodeScale = 255.0f / MathHelper.TwoPi; PacketWriter.Write( (byte)( rotation * rotationEncodeScale ) );   受信側では逆数を掛けることで、元の値にします。 float rotationDecodeScale = MathHelper.TwoPi / 255.0f; float rotation = (float)PacketReader.ReadByte() * rotationDecodeScale;   この量子化では、精度が多少失われてしまいますが、通常は4バイトのデータを1/4に減らすことの方が価値があります。   訳注:原文のコードでは255ではなく256を使っていますが、この場合に変換できる値は上限値未満の時に動作します。もし、上限値を指定した場合、結果は256になり、byteにキャストすると結果は0になってしまいます。ですが、この例で扱っている値はラジアンなので円を表す場合、値が0とMathHelper.TwoPiの時は結果が同じになるので問題なく動作します。 同じ手法はシェーダープログラムなどで使うのですが、この場合に良く用いられるのは0~1までの値で1以下の値を取るので、この場合はスケールを255にしないと動作しません。 以上の理由から、ここではスケール値を255に変更しました。   原文:http://blogs.msdn.com/shawnhar/archive/2007/12/24/network-compression-quantization.aspx

1

ネットワーク その5 圧縮

限られたネットワーク帯域の中では、送信するデータを圧縮することは非常に重要なことです。 Zip等の一般的な圧縮アルゴリズムはネットワークゲーム向けではありません。これらの圧縮はある程度のデータ量がある場合は効率良い圧縮が期待できますが、ネットワークパケットのように小さいデータを圧縮するには不向きです。ここで必要なのは20バイトのデータを10バイトにするような圧縮方法です。 初心者がよく考える手法として、送信側で複数のパケットを続けてバイトストリームして一般的な圧縮アルゴリズムを使って圧縮し、圧縮されたデータをパケットに分割して送受信するというものがあります。確かに圧縮率は高くなるのですが、この手法には致命的名欠点があります。この手法では、圧縮したデータを展開するに全てのパケットが失われること無く、順番に配信される必要があります。この為にはSenDataOptions.ReliableInOrderを使う必要があり、レイテンシが増加する原因になってしまいます。 また、変化量(前の状態との差)を送ることで全体のデータを送るより少ないデータ量で済ますことができますが、この手法も一般的な圧縮アルゴリズムを使うのと一緒で、全てのデータが順番に送信されるという保障があるときにのみ使える手法です。   通常は古典的なビットフィールドをまとめたり、量子化を使った方が良い結果になります。これらの手法は4KBのRAMを積んだ8ビットマイコン時代に良く使われたものですが、ギガバイト単位のデータを扱える.Netの世界では忘れ去られた手法でもあります。 例えば、文字列を送るのでなく、整数のIDかenum値を送る。 もし、行列が回転と移動の組み合わせで、スケールや、せん断、射影をしないと判っているのなら行列データを送らない。変わりに12バイトのVector3であるMatrix.Translateと、16バイトのQuaternion.CreateFromRotationMatrix(matrix)を送ります。これで64バイトの行列データが28バイトに圧縮されたことになります。 以下、次回に続く。 原文http://blogs.msdn.com/shawnhar/archive/2007/12/22/network-compression.aspx

0

ネットワーク その4 帯域 ボイスチャットについて

XNAフレームワークはボイスチャットをサポートしており、ヘッドセットがある場合に自動でチャットができるようになっています。便利な機能ではありますが、使用中はより多くのネットワーク帯域が必要になることに注意が必要です。 音声データは500 B/s以下の帯域に圧縮され、ヘッドセットに向かって喋った時のみにデータ転送を行います。 デフォルトの状態では全てのプレイヤー同士で会話することができるようになっています。もし、ひとりが他の15人のプレイヤーに話しかけた場合、 500 * 15 = 7.3KB/s ひゃー!! 8KB/sが目標だったことを覚えていますか?これではゲームデータを送る前に殆どの帯域を使い切ってしまいます。   では、この厄介なボイスデータ帯域問題をどのようにして解決することができるのでしょうか? 同時にプレイできるプレイヤー数を少なくする もしくは、LocalNetworkGamer.EnableSendVoiceを状況に合わせて変更する 同じチームにのみ話せるようにする 自分の近くにいるプレイヤーに対してのみ話せるようにする。ただし、頻繁にEnableSendVoiceを変更するのは止めましょう。この値を変更する度にその情報をネットワーク上に転送しないといけないので、頻繁に変更してしまうと逆により多くの帯域を使ってしまうことになってしまいます。 MotoGPではゲーム用のデータ転送量の少ないロビー内では16人全員が自由に会話でき、ゲーム内では最も近い3人のプレイヤーと会話ができるようになっています。   原文:http://blogs.msdn.com/shawnhar/archive/2007/12/20/network-bandwidth-voice.aspx

2