<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>ひにけにXNA : ネットワーク</title><link>http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx</link><description>Tags: ネットワーク</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>招待サンプル</title><link>http://blogs.msdn.com/ito/archive/2008/11/25/invites-sample.aspx</link><pubDate>Wed, 26 Nov 2008 04:38:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:9143077</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/ito/comments/9143077.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=9143077</wfw:commentRss><description>&lt;P&gt;今日は&lt;A target=_blank href="http://creators.xna.com/en-US/sample/invites" mce_href="http://creators.xna.com/en-US/sample/invites"&gt;招待サンプル&lt;/A&gt;を紹介します。&lt;/P&gt;
&lt;P&gt;Xbox Live!の機能のひとつに、フレンドリスト内の友達と一緒にネットワークゲームをプレイしたいときに誘える招待機能があります。XNA GS 3.0ではこの招待機能がサポートされています。招待するケースとしては以下の二つのケースがあります。&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;同じゲームをプレイしている時(In-Title Invites)&lt;/LI&gt;
&lt;LI&gt;違うゲームをプレイしている時(Cross-Title Invites)&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;XNA GS 3.0上では、このどちらのケースでも&lt;STRONG&gt;NetworkSession.InviteAccepted&lt;/STRONG&gt;イベントが発生します。このイベントを受け取った時に、&lt;STRONG&gt;NetworkSession.JoinInvite&lt;/STRONG&gt;メソッドを呼ぶことで招待された側が招待した側のセッションに接続できるようになっています。以下はイベントハンドラへの登録と、ハンドラ内での処理の例です。&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: #2b91af"&gt;NetworkSession&lt;/SPAN&gt;.InviteAccepted += InviteAcceptedEventHandler;&lt;BR&gt;&lt;/PRE&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: blue"&gt;void &lt;/SPAN&gt;InviteAcceptedEventHandler(&lt;SPAN style="COLOR: blue"&gt;object &lt;/SPAN&gt;sender, &lt;SPAN style="COLOR: #2b91af"&gt;InviteAcceptedEventArgs &lt;/SPAN&gt;e)
{
    &lt;SPAN style="COLOR: green"&gt;// 現在のセッションから抜ける
    &lt;/SPAN&gt;&lt;SPAN style="COLOR: blue"&gt;if &lt;/SPAN&gt;(networkSession != &lt;SPAN style="COLOR: blue"&gt;null&lt;/SPAN&gt;)
    {
        networkSession.Dispose();
        networkSession = &lt;SPAN style="COLOR: blue"&gt;null&lt;/SPAN&gt;;
    }

    &lt;SPAN style="COLOR: green"&gt;// 招待されたセッションに入る
    &lt;/SPAN&gt;networkSession = &lt;SPAN style="COLOR: #2b91af"&gt;NetworkSession&lt;/SPAN&gt;.JoinInvited(maxLocalGamers);
}&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;同じゲームをプレイしている場合は、通常のNetworkSessionの振る舞いと殆ど同じですが、違うゲームをプレイしている場合は振る舞いの仕方が変わってきます。ただし、これらの処理は全てOS側で行われるのでゲーム開発者は前述のInviteAcceptedイベントの実装をするだけです。&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Xbox 360上で同じゲームが既にインストールしている場合は自動的にゲームが起動され、その直後にInviteAcceptedイベントが発生します。&lt;/LI&gt;
&lt;LI&gt;クリエーターゲーム(開発中のゲームやピアレビュー中のゲーム)の場合は、招待した人が同じゲームを持っていない場合に、その旨を伝えるメッセージが表示されます。&lt;/LI&gt;
&lt;LI&gt;コミュニティゲームの場合、ユーザーがそのゲームを持っていない場合はマーケットプレースに自動的に移動、該当するゲームをダウンロードするかのメニューが表示され、ダウンロード後に自動的にゲームが起動されます。&lt;/LI&gt;
&lt;LI&gt;Windows上の場合、上記のように自動的なダウンロードや起動するといった機能がないので、ユーザーが手動で行う必要があります。&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;テスト時の注意&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;招待機能はXbox Live!の機能なので、ネットワークゲームのテストの様にシステムリンク(ローカル)を使ってテストできるのと違い、クリエータークラブ会員になっているゲーマータグが二つ必要なことに注意してください。&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9143077" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/_B230FC30E0308B957A76_/default.aspx">ゲーム開発</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+3.0/default.aspx">XNA 3.0</category></item><item><title>ネットワーク　その１０　オブジェクト所有権</title><link>http://blogs.msdn.com/ito/archive/2008/02/12/network-10-object-ownership.aspx</link><pubDate>Wed, 13 Feb 2008 05:48:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7660918</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/ito/comments/7660918.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7660918</wfw:commentRss><description>&lt;P&gt;&lt;A href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Architectures_3A00_--Client_2F00_Server.aspx" target=_blank mce_href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Architectures_3A00_--Client_2F00_Server.aspx"&gt;クライアント/サーバー&lt;/A&gt;と&lt;A href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Architectures_3A00_--Peer_2D00_to_2D00_Peer.aspx" target=_blank mce_href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Architectures_3A00_--Peer_2D00_to_2D00_Peer.aspx"&gt;ピア・ツー・ピア&lt;/A&gt;型のどちらのネットワーク形態が優れているのかを議論するのが好きな人たちがいます。しかし、個人的にこの議論は間違ったものだと思います。誰が&lt;STRONG&gt;「どちらか一方のネットワーク形態を選ばなくてはいけない」&lt;/STRONG&gt;と言ったんですが？私は両方の長所を活かしたハイブリット形式の大ファンです。&lt;/P&gt;
&lt;P&gt;ネットワークプログラミングは妥協との戦いです。100%の正確さと、ラグがまったく無いという２つの状態を両立することは不可能で、トレードオフをしなければいけません。&lt;/P&gt;
&lt;P&gt;時にはラグが多少大きくっなっても正確さをとる場合もあれば、逆に正確さを犠牲にして高いレスポンスを必要とする場合もあります。&lt;/P&gt;
&lt;P&gt;１００万ドルの問題:　　どのマシンがどのデータを管理するか？&lt;/P&gt;
&lt;P&gt;ゲーム内のそれぞれのデータについてのコスト/実益の分析を以下の質問に答えることでできます。&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;そのデータは独立したものですか、もしくはそれ以外のデータと状態を一致させる必要がありますか？&lt;/LI&gt;
&lt;LI&gt;全プレイヤーがそのデータのラグに対して同等に気にする必要がありますか、それともひとりのプレイヤーが他のプレイヤーよりも気にしなければいけませんか？&lt;/LI&gt;
&lt;LI&gt;そのデータのラグはどれだけ重要なものですか？ゲームデザインを変更することによって、その重要度を低くはできませんか？&lt;/LI&gt;
&lt;LI&gt;データ変化を予測することはできますか？間違った予測をした場合、問題になりますか、それともエラーをスムースに訂正することができますか？&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;この質問の答えによって、どのようにデータを管理すればいいのかが判ります。&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;沢山のデータの整合性が必要な場合、それらは１台のマシンによって管理されるべき&lt;/LI&gt;
&lt;LI&gt;もしひとりのプレイヤーがデータのラグに対して他のプレイヤーより気にしなくてはいけない場合、そのデータはそのプレイヤーのローカルマシンによって管理されるべき&lt;/LI&gt;
&lt;LI&gt;ラグが大きな問題となる場合は、予測アルゴリズムを適用するべき&lt;/LI&gt;
&lt;LI&gt;予測ミスが大きな問題となる場合は、予測アルゴリズムは適用すべきではない&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;１と２の質問はどのマシンがデータを管理するかの指標になり、３と４の予測アルゴリズムの適用についての質問は別々のものだということに注意&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;例えば:&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;スペースシップの移動&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;スペースシップを操作しているプレイヤーにとってレイテンシは重要な問題。&lt;/LI&gt;
&lt;LI&gt;それぞれのマシンでスペースシップの位置が多少ずれていても、大体の位置があっているのなら問題はない。最悪ケースとしては２つのスペースシップがぶつかったときに、それぞれのマシンでは多少違った方向に跳ね返るが、ゲーム的には問題はない。&lt;/LI&gt;
&lt;LI&gt;多少の予測ミスは問題にならない。間違った位置から正しい位置へと、誰にも気づかれること無く補正することができる。&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;FONT color=#ff0000&gt;結論:それぞれのスペースシップはそれぞれのローカルマシンによって管理されるべき。予測アルゴリズムを適用する。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#ff0000&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;レースの勝敗&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;レイテンシは特に重要ではない。あなたがゴールラインを通過してから０．５秒後に結果が表示されても大丈夫(カッコイイアニメーションやカメラワークで、このレイテンシを隠すことができる)。&lt;/LI&gt;
&lt;LI&gt;それぞれのマシンでレース結果が違うのはダメ。勝者は常にひとり。&lt;/LI&gt;
&lt;LI&gt;間違った予測結果は大問題。「優勝おめでとう！！」と表示されて少し経ってから「やっぱり２位でした～」と表示されるのはひどすぎる。&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;FONT color=#ff0000&gt;結論:１台のマシンがレース結果を決めるべき。予測アルゴリズムは適用しない&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#ff0000&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;死亡判定&lt;/STRONG&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;殺されたプレイヤーにとってレイテンシは重要だが、他のプレイヤーにとっては特に重要ではない。&lt;/LI&gt;
&lt;LI&gt;マシン毎に違った結果になるのは問題。もし私が死んでいるのなら、他のマシンでも死んでいなくてはならない。&lt;/LI&gt;
&lt;LI&gt;予測ミスは許されない。「ぐはぁ、やられたー」と叫びながら派手なアニメーションが再生され、血飛沫を撒き散らしながら地面を転がった挙句に「ぐふぅ」と事切れた数秒後に何事もなかったのように生き返るのは問題。&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;FONT color=#ff0000&gt;結論:それぞれのマシンがローカルプレイヤーの死亡判定をするべき。他のマシンは死亡判定の予測をしてはいけない。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#ff0000&gt;追記:ここでは視覚的な予測を適用することができる。例えば、私のマシンがヘッドショットだと判定するが、ヘッドショットされたプレイヤーの位置が１００％確実ではないので死亡アニメーションを再生することはできない。その代わりに「ダメージを受けた」というアニメーションを再生することができ、視覚的なフィードバックを即座に得ることができる。もし、ヘッドショットを決めたプレイヤーから死亡か確定したという連絡が届いた場合は死亡アニメーションへスムースに変化させることができる。仮にこの予測が外れた場合でもダメージアニメーションをキャンセルして通常のアニメーションに戻すことができる。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#ff0000&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;ビークル(乗り物)に乗る&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;私は一度、キャラクターが徒歩で歩き回り、いろんなビークルに乗り降りすることができるゲームのプロトタイプ製作をしたことがありました。ビークルに乗っている間は「スペースシップの移動」の例で説明したのとまったく一緒です。ただビークルに乗ると言うのは違います。一度に複数のプレイヤーが運転することはできませんから！&lt;/P&gt;
&lt;P&gt;この問題を解決するために、まずどのマシンがどのオブジェクトに対して所有権を持っているのかを表すデータをつくりました。これで動的にビークルの所有権の変更ができます。次にどのマシンが所有権を持っているのかを決めなければいけません。&lt;/P&gt;
&lt;P&gt;ビークルのステート(操作、物理シム、衝突判定等)は、そのビークルを運転しているプレイヤーがいるマシンによって管理されます。それ以外に、この所有権を誰が持つべきを決める管理マシンを決めました。もしビークルに誰も乗っていない場合は、管理しているビークルの数が最も少ないマシンに所有権があります。&lt;/P&gt;
&lt;P&gt;ビークルのとなりに立ち「乗る」ボタンを押した時に以下の処理をします。&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;クライアントマシンは「乗せてください」というメッセージをビークル所有権管理マシンに送ります&lt;/LI&gt;
&lt;LI&gt;クライアントマシンではキャラクターがビークルに乗り込むアニメーションが再生されますが、まだ所有権がないので運転することはできません。&lt;/LI&gt;
&lt;LI&gt;通常&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;ビークル所有権管理マシンは要求があったクライアントマシンに所有権を渡します&lt;/LI&gt;
&lt;LI&gt;ビークル所有権管理マシンからの「乗って良いよ」というメッセージはキャラクターんがビークルに乗り込むアニメーションが終わるまでに届き、ビークルを問題なく運転することができます。この乗り込むアニメーションはラグを隠蔽するわけです。&lt;/LI&gt;&lt;/UL&gt;
&lt;LI&gt;二人同時に乗り込もうとしたとき&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;ビークル所有権管理マシンは「すまん、君は乗れん」というメッセージを一方に送ります&lt;/LI&gt;
&lt;LI&gt;乗り込み要求を断られたキャラクターは乗り込みアニメーションをキャンセルしてビークルに乗り込む前の位置にもどされます。&lt;/LI&gt;
&lt;LI&gt;先に乗り込み要求を申請したキャラクターはビークルを運転することができます。&lt;/LI&gt;&lt;/UL&gt;&lt;/UL&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;ビークルを運転しているときはピア・ツー・ピア形式ですが、大事な決定をするのはクライアント/サーバー形式になっています。&lt;/P&gt;
&lt;P&gt;アイディアとしては大事な決定を下す単一の管理マシンが全てのデータを管理する必要がないということです。マシンAはビークルの乗り降りを管理して、マシンBはパワーアップアイテムの管理、そしてマシンCはセッション終了時に誰が勝敗を決定するといった感じに管理するものを割り振ることができます。&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;これで私がネットワークに関して言わなければならないことの全てです。&lt;/P&gt;
&lt;P&gt;THE END&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;原文:&lt;BR&gt;&lt;A title=http://blogs.msdn.com/shawnhar/archive/2008/01/03/network-object-ownership.aspx href="http://blogs.msdn.com/shawnhar/archive/2008/01/03/network-object-ownership.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2008/01/03/network-object-ownership.aspx"&gt;http://blogs.msdn.com/shawnhar/archive/2008/01/03/network-object-ownership.aspx&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7660918" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+_D530EC30FC30E030EF30FC30AF30_/default.aspx">XNA フレームワーク</category><category domain="http://blogs.msdn.com/ito/archive/tags/_B230FC30E0308B957A76_/default.aspx">ゲーム開発</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワーク　その９　究極の圧縮方法</title><link>http://blogs.msdn.com/ito/archive/2008/02/11/network-09-just-say-no.aspx</link><pubDate>Tue, 12 Feb 2008 07:12:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7630825</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/ito/comments/7630825.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7630825</wfw:commentRss><description>&lt;P&gt;今まで紹介してきた狡猾な圧縮方法より効果的なデータ圧縮方法があります。それはデータ自体を送らないということです。&lt;/P&gt;
&lt;P&gt;もちろん、まったくデータを送らないのでは相手側との同期ができません。でも、時には同期すること自体が重要ではない場合があります。&lt;/P&gt;
&lt;P&gt;２つのルール&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;ゲームプレイに関連するものは同期しなければならない&lt;/LI&gt;
&lt;LI&gt;殆どの物はゲームプレイに関与しない&lt;/LI&gt;&lt;/UL&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;効果音やアニメーションは殆どの場合は同期する必要がありません。もし、ネットワークを介してキャラクターが前に走って移動する場合、それぞれりマシンではその情報を元に、走るアニメーションを再生させ、そのアニメーションに合わせて靴音を鳴らすことができます。靴音が鳴るタイミングが多少ずれていてもゲームプレイには関係ないのでネットワークを介して足音を同期させる必要はありません。&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;ケーススタディ:&lt;/FONT&gt;&lt;A href="http://www.talula.demon.co.uk/motogp_online/index.html" target=_blank mce_href="http://www.talula.demon.co.uk/motogp_online/index.html"&gt;&lt;FONT color=#008000&gt;MotoGP&lt;/FONT&gt;&lt;/A&gt;&lt;FONT color=#008000&gt;では５%の確立で相手を抜き去った場合、抜いた相手に向かって手を振り上げるアニメーションを再生します。クラッシュした場合、ライダーが吹き飛ぶ複数のアニメーションの中からランダムで再生され、ネットワークにはどのアニメーションを再生したかという情報は送られません。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;あるプレイヤーから見ると手を振り上げたアニメーションをしているように見えますが、他のプレイヤー視点からはアニメーションが再生されていないという場合があります。クラッシュシーンでライダーの転がるアニメーションはそれぞれのプレイヤーによって違うアニメーションが再生されます。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;これらをネットワークを介して同期するにはネットワーク帯域が足りませんでした。しかし、これらのアニメーションはゲームプレイには関係無いものなので、誰も同期していないということには気づきませんでした。&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;同じように、FPS等のゲームでは撃った弾の位置、壁に当たったときにできる弾痕の位置、飛び散るガラス片や薬莢の位置を同期する必要はありません。ここでネットワークに送る情報は単に&lt;STRONG&gt;「私はゲーム内で銃を撃っている」&lt;/STRONG&gt;というブーリアン(bool)の情報だけです。このシンプルな情報を元に、各マシンではそれぞれにシミュレーションを行います。多少の違いが起きても、それらの平行世界で起きている事柄がほぼ同じである限りは問題がありません。&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;一度、この&lt;STRONG&gt;「データをネットワークに送らなくても良いんだ」&lt;/STRONG&gt;という考え方が身につくと、いろんな事ができるようになります。&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;ケーススタディ:Moto GPで広告看板やパイロンはコース脇に配置されています。もしバイクがこれらのものと衝突した場合、バイクと一緒に吹き飛びます。もし、ゆっくりとした速度でぶつかった場合は看板やパイロンを押し動かすことができます。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;殆どのプレイヤーはレースに勝つことが目的で真剣にレースに参加しますが、中にはレースの邪魔をしようとするグリファー(訳注:オンラインゲームで嫌がらせをするプレーヤーのこと、英語ではGriefer、griefには深い悲しみ、悲痛といった意味があります)がいます。グリファー達はコースを逆走したりして、どうやったらレースをメチャクチャにできるのかを競っています。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;グリファー達は広告看板やパイロンをコースの中央に動かしてバリケードを築けることに気付きました。これで真剣にレースをしているライダー達がバリケードに衝突して派手にほこりを撒き散らしながら吹き飛ぶ筈です。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;でも、実際にはグリファー達の思い通りにはいきませんでした。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;広告看板やパイロンの位置情報はネットワークを介して送られてはいませんでした。単に充分なネットワーク帯域が無かったからです。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;以下は実際にあったことです。&lt;/FONT&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;FONT color=#008000&gt;あるグリファーはバイクを前後に動かしながら、ゆっくりと障害物を押し進めます&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT color=#008000&gt;ピア・ツー・ピアを使っていたので、そのグリファーは自分のバイクの動きを完全にコントロールできます。&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT color=#008000&gt;他のマシンでは予測アルゴリズムを使っているので、グリファーのバイクの座標は大体一緒ではありますが、完全に一緒ではありません。&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT color=#008000&gt;グリファーのマシン上ではグリファーの思い通りに障害物はコースの真ん中へ移動します。&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT color=#008000&gt;他のマシンでは最初の押しが少しだけ左にずれました。この段階では障害物の位置はグリファーが移動させた位置に近いですが一致している訳ではありません。更にグリファーが障害物を押し進めるうちにグリファーが実際に移動させた位置とはズレが生じ、最終的に他のマシン上では障害物はグリファーと当たらない位置に移動して、そのまま動かなくなります。それらの障害物の位置は同期されていないので、この矛盾は修正されません。&lt;/FONT&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;結果&lt;/FONT&gt;&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;FONT color=#008000&gt;グリファーのマシン上では立派なバリケードが完成、他のマシン上ではバリケードができていない&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT color=#008000&gt;レースを真剣にしている人達のマシン上ではバリケードが無いので問題なくレースを楽しむことができます。&lt;/FONT&gt;&lt;/LI&gt;
&lt;LI&gt;&lt;FONT color=#008000&gt;一方、グリファーのマシン上では立派なバリケードがあるので他のプレイヤー達はグリファーの思惑通りにバリケードに衝突してライダーは派手に中に舞い、バイクは火花を飛ばしながら滑っていきます。そのしばらく後に新しい情報がネットワークから届きます。あれ？実際には衝突は起きていなかった？この時、吹き飛んだはずのプレイヤーはコース上に配置され、問題なくレースは続行されます。&lt;/FONT&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;１００万人ものプレイヤー達が何時間もプレイしたにも関わらず、誰もこの矛盾には気づきませんでした。&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;グリファーは盛大なクラッシュシーンを見て喜び、&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;真剣にレースをしている人達は無事にレースを終えることができ、&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT color=#008000&gt;みんないつまでも幸せにレースを楽しんだとさ、めでたしめでたし&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;原文:&lt;BR&gt;&lt;A title=http://blogs.msdn.com/shawnhar/archive/2008/01/01/network-compression-just-say-no.aspx href="http://blogs.msdn.com/shawnhar/archive/2008/01/01/network-compression-just-say-no.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2008/01/01/network-compression-just-say-no.aspx"&gt;http://blogs.msdn.com/shawnhar/archive/2008/01/01/network-compression-just-say-no.aspx&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7630825" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/_B230FC30E0308B957A76_/default.aspx">ゲーム開発</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワーク　その８　算術符号化圧縮</title><link>http://blogs.msdn.com/ito/archive/2008/02/08/network-arithmetic-encoding.aspx</link><pubDate>Sat, 09 Feb 2008 07:40:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7553084</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>3</slash:comments><comments>http://blogs.msdn.com/ito/comments/7553084.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7553084</wfw:commentRss><description>&lt;p&gt;算術符号化は解りづらく、めったに使われないツールのひとつですが、時々その威力を発揮します。例えるならネットワークデータ圧縮における変なサイズの六角レンチです。&lt;/p&gt;  &lt;p&gt;算術符号化はクールです。なぜなら、あまり知られていないし、一見すると動かないように思えるものだからです。パーティーで女の子に好印象を持たせるのに活躍します(訳注:そうか？)&lt;/p&gt;  &lt;p&gt;以下のデータがあったとします。&lt;/p&gt;  &lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;enum&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Species&lt;/span&gt;    &lt;span style="color: rgb(0,128,0)"&gt;// 種類
&lt;/span&gt;    {
        Camel,          &lt;span style="color: rgb(0,128,0)"&gt;// ラクダ
&lt;/span&gt;        Cat,            &lt;span style="color: rgb(0,128,0)"&gt;// ねこ
&lt;/span&gt;        Caterpillar,    &lt;span style="color: rgb(0,128,0)"&gt;// いもむし
&lt;/span&gt;        Cheetah,        &lt;span style="color: rgb(0,128,0)"&gt;// チーター
&lt;/span&gt;        Chimpanzee,     &lt;span style="color: rgb(0,128,0)"&gt;// チンパンジー
&lt;/span&gt;        Cobra,          &lt;span style="color: rgb(0,128,0)"&gt;// コブラ
&lt;/span&gt;        Cormorant,      &lt;span style="color: rgb(0,128,0)"&gt;// 鵜(う)
&lt;/span&gt;        Cougar,         &lt;span style="color: rgb(0,128,0)"&gt;// クーガー
&lt;/span&gt;        Coyote,         &lt;span style="color: rgb(0,128,0)"&gt;// コヨーテ
&lt;/span&gt;        Crab,           &lt;span style="color: rgb(0,128,0)"&gt;// カニ
&lt;/span&gt;        Crocodile,      &lt;span style="color: rgb(0,128,0)"&gt;// ワニ
&lt;/span&gt;    }

    &lt;span style="color: rgb(0,0,255)"&gt;Species&lt;/span&gt; animalA;
    &lt;span style="color: rgb(0,0,255)"&gt;Species&lt;/span&gt; animalB;
    &lt;span style="color: rgb(0,0,255)"&gt;bool&lt;/span&gt; whoWon;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;&lt;em&gt;(そう、私たちは世界中の男の子が一度は思う「クーガーとワニはどっちが強いか？」という疑問に答えるためのゲームを作るんだ(訳注:ねこが最強))&lt;/em&gt;&lt;/p&gt;

&lt;p mce_keep="true"&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;ビットフィールド圧縮を使った場合、animalAとanimalBの変数には１１種類の動物がいるので、その情報を格納するのにそれぞれ４ビットが必要になり、誰が勝ったのかという情報で１ビット必要になるので合計で８ビットを超えてしまいます。&lt;/p&gt;

&lt;p&gt;でも、ちょっと待って&lt;/p&gt;

&lt;p&gt;animalAに格納する値の組み合わせは１１種類、animalBも１１種類、そしてwhoWonには２種類の値です。全ての組み合わせを考えると１１&amp;#215; １１&amp;#215; ２の合計２４２通りになります。これならbyteに収まりそうです。&lt;/p&gt;

&lt;p&gt;ここでは４ビットを動物の種類に割り当てていますが実際は１１種類必要なところに１６種類を表すだけのスペースを用意してしまうというのはビットフィールド圧縮の問題です。この中途半端な組み合わせをコンパクトにする方法があれば良いと思いませんか？&lt;/p&gt;

&lt;p&gt;前の記事のビットフィールド圧縮の部分を、乗算、除算、そして剰余算に変更することで解決することができます。&lt;/p&gt;

&lt;p mce_keep="true"&gt;&amp;#160;&lt;/p&gt;

&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; ArithmeticEncode( &lt;span style="color: rgb(0,0,255)"&gt;ref&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; encoded, &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; valueRange, &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; Value )
    {
        encoded *= valueRange;
        encoded += value;
    }

    &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; encoded = 0;
    ArithmeticEncode( &lt;span style="color: rgb(0,0,255)"&gt;ref&lt;/span&gt; encoded, 11, (&lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt;)animalA );
    ArithmeticEncode( &lt;span style="color: rgb(0,0,255)"&gt;ref&lt;/span&gt; encoded, 11, (&lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt;)animalB );
    ArithmeticEncode( &lt;span style="color: rgb(0,0,255)"&gt;ref&lt;/span&gt; encoded, 2, whoWon? 1: 0 );
    packetWriter.Write((&lt;span style="color: rgb(0,0,255)"&gt;byte&lt;/span&gt;)encoded);&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p mce_keep="true"&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;そして、読み込み側は、ビットフィールドの時と同じようにパラメーターを逆順に読み込みます。&lt;/p&gt;

&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; ArithmeticDecode( &lt;span style="color: rgb(0,0,255)"&gt;ref&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; encoded, &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; valueRange )
    {
        &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; value = encoded % valueRange;
        encoded /= valueRange;
        &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; value;
    }

    &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; encoded = packetReader.ReadByte();
    whoWon = ArithmeticDecode( &lt;span style="color: rgb(0,0,255)"&gt;ref&lt;/span&gt; encoded, 2 ) != 0;
    animalB = (&lt;span style="color: rgb(43,145,175)"&gt;Species&lt;/span&gt;)ArithmeticDecode( &lt;span style="color: rgb(0,0,255)"&gt;ref&lt;/span&gt; encoded, 11 );
    animalA = (&lt;span style="color: rgb(43,145,175)"&gt;Species&lt;/span&gt;)ArithmeticDecode( &lt;span style="color: rgb(0,0,255)"&gt;ref&lt;/span&gt; encoded, 11 );&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p mce_keep="true"&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;どう？クールでしょ？&lt;/p&gt;

&lt;p mce_keep="true"&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;原文:
  &lt;br /&gt;&lt;a title="http://blogs.msdn.com/shawnhar/archive/2007/12/30/network-compression-arithmetic-encoding.aspx" href="http://blogs.msdn.com/shawnhar/archive/2007/12/30/network-compression-arithmetic-encoding.aspx" target="_blank"&gt;http://blogs.msdn.com/shawnhar/archive/2007/12/30/network-compression-arithmetic-encoding.aspx&lt;/a&gt;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7553084" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+_D530EC30FC30E030EF30FC30AF30_/default.aspx">XNA フレームワーク</category><category domain="http://blogs.msdn.com/ito/archive/tags/_B230FC30E0308B957A76_/default.aspx">ゲーム開発</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワーク　その７　ビットフィールドで圧縮</title><link>http://blogs.msdn.com/ito/archive/2008/02/07/network-07-bitfields.aspx</link><pubDate>Fri, 08 Feb 2008 06:44:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7533126</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/ito/comments/7533126.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7533126</wfw:commentRss><description>&lt;P&gt;ビットフィールドは古くから知られている素晴らしいデータパッキング手法です。C#プログラマーがビットフィールドを使う機会は非常に少ないですが、ネットワークパケットの圧縮にはもってこいなので、この機会に使ってみましょう。&lt;/P&gt;
&lt;P&gt;バイトは８ビット、intは３２ビット。でも、送るべきデータが８ビットや３２ビットの倍数にならないときはどうします？例えば以下のようなデータを送るとします。&lt;/P&gt;&lt;PRE class=code&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;bool&lt;/SPAN&gt; isAlive;       &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// 生きているか？
&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;bool&lt;/SPAN&gt; isFiring;      &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// 撃っているか？

&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;enum&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;Species&lt;/SPAN&gt;    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// 種類
&lt;/SPAN&gt;    {
        Camel,          &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// ラクダ
&lt;/SPAN&gt;        Cat,            &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// ねこ
&lt;/SPAN&gt;        Caterpillar,    &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// いもむし
&lt;/SPAN&gt;        Cheetah,        &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// チーター
&lt;/SPAN&gt;        Chimpanzee,     &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// チンパンジー
&lt;/SPAN&gt;        Cobra,          &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// コブラ
&lt;/SPAN&gt;        Cormorant,      &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// 鵜(う)
&lt;/SPAN&gt;        Cougar,         &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// クーガー
&lt;/SPAN&gt;        Coyote,         &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// コヨーテ
&lt;/SPAN&gt;        Crab,           &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// カニ
&lt;/SPAN&gt;        Crocodile,      &lt;SPAN style="COLOR: rgb(0,128,0)"&gt;// ワニ
&lt;/SPAN&gt;    }

    packetWriter.Write(isAlive);
    packetWriter.Write(isFiring);
    packetWriter.Write((&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;byte&lt;/SPAN&gt;)species)
&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;これで３バイトになりますが、実際にはそんなにいりません。ブーリアン型は１ビットしか必要としませんし、生物の種類も１１種しか居ないので４ビットで足ります。&lt;/P&gt;
&lt;P&gt;それぞれのフィールドにどれだけのビット数が必要かが判れば、ビットシフトすることによって複数のビット群を１つbyteやintにまとめることができます。使いやすいように、指定されたビット群をbyteやintといった値にまとめるメソッドを作るといいでしょう。&lt;/P&gt;
&lt;P&gt;ここでは、先の例では３バイトを送っていたところを１バイトにまとめて送ることができます(まだ２ビット余ってる)&lt;/P&gt;&lt;PRE class=code&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;void&lt;/SPAN&gt; AddToBitfield( &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ref&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; bitfield, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; bitCount, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; value )
    {
        bitfield &amp;lt;&amp;lt;= bitCount;
        bitfield |= value;
    }

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; bitfield = 0;
    AddToBitfield( &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ref&lt;/SPAN&gt; bitfield, 1, isAlive? 1: 0 );
    AddToBitfield( &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ref&lt;/SPAN&gt; bitfield, 1, isFiring? 1: 0 );
    AddToBitfield( &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ref&lt;/SPAN&gt; bitfield, 4, (&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt;)species );

    packetWriter.Write((&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;byte&lt;/SPAN&gt;)bitfield);
&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;AddToBitfieldメソッドは２つのことをします&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;現在のビットフィールドを左シフト(「&amp;lt;&amp;lt;」演算子を使う)して、新しいフィールドを確保&lt;/LI&gt;
&lt;LI&gt;新しいビット群を設定(「|」演算子を使う)&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;読み込むためには、逆のプロセスを行います。&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE class=code&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; ReadFromBitfield( &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ref&lt;/SPAN&gt; &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; bitfield, &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; bitCount )
    {
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; value = bitfield &amp;amp; ((1 &amp;lt;&amp;lt; bitCount) - 1 );
        bitfield &amp;gt;&amp;gt;= bitCount;
        &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;return&lt;/SPAN&gt; value;
    }

    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;int&lt;/SPAN&gt; bitfield = &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;PacketReader&lt;/SPAN&gt;.ReadByte();
    species = (&lt;SPAN style="COLOR: rgb(43,145,175)"&gt;Species&lt;/SPAN&gt;)ReadFromBitfield( &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ref&lt;/SPAN&gt; bitfield, 4 );
    isFiring = ReadFromBitfield( &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ref&lt;/SPAN&gt; bitfield, 1 ) != 0;
    isAlive = ReadFromBitfield( &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;ref&lt;/SPAN&gt; bitfield, 1 ) != 0;

&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P&gt;書き込んだ順番とは逆に読み込んでいることに注意してください。&lt;/P&gt;
&lt;P&gt;ReadFromBtfieldメソッドはAddToBitfieldと逆のことをします。&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;ビットフィールドの下位ビットからビット群を取り出す(「&amp;amp;」演算子を使う)&lt;/LI&gt;
&lt;LI&gt;残りのビットフィールドを右にシフトする(「&amp;gt;&amp;gt;」演算子を使う)&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;ビットフィールドはenumやブーリアンをまとめるのに有効ですが、他の数値データも前回紹介した量子化の手法と組み合わせることで格納することができます。&lt;/P&gt;
&lt;P&gt;原文:&lt;BR&gt;&lt;A class="" href="http://blogs.msdn.com/shawnhar/archive/2007/12/28/network-compression-bitfields.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/28/network-compression-bitfields.aspx"&gt;http://blogs.msdn.com/shawnhar/archive/2007/12/28/network-compression-bitfields.aspx&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7533126" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+_D530EC30FC30E030EF30FC30AF30_/default.aspx">XNA フレームワーク</category><category domain="http://blogs.msdn.com/ito/archive/tags/_B230FC30E0308B957A76_/default.aspx">ゲーム開発</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワーク　その６　量子化で圧縮</title><link>http://blogs.msdn.com/ito/archive/2008/01/31/network-06-quantization.aspx</link><pubDate>Fri, 01 Feb 2008 07:15:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7366427</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/ito/comments/7366427.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7366427</wfw:commentRss><description>&lt;P&gt;ビット数の少ない方が多いものより消費するスペースは小さくなります。&lt;/P&gt;
&lt;P&gt;もしint型の値が0～100までの範囲しか取らないと判っているのなら、そのまま４バイトのint型として送るより、byte型にキャストして送ることができます。&lt;/P&gt;
&lt;P&gt;場合によっては値が表す範囲を値をずらすことによって減らすことができます。例えば、キャラクターの高さのデータを送る必要があり、高さはcmで表されるとします。このゲーム中のキャラクターの高さの範囲は、ドワーフ(100cm)から巨人(300cm)まであります。&lt;/P&gt;
&lt;P&gt;300という値はbyteで表現できる範囲(0-255)を超えているので、キャストすることはできません。&lt;/P&gt;
&lt;P&gt;しかし、100cm以下のキャラクターが存在しないと判っているのなら、100cmを基点とすることで値の取る範囲を小さくすることができます。&lt;/P&gt;&lt;PRE class=code&gt;&lt;SPAN style="COLOR: rgb(43,145,175)"&gt;PacketWriter&lt;/SPAN&gt;.Write( (&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;byte&lt;/SPAN&gt;)( height - 100 ) );&lt;/PRE&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;受信側では値を使う前に100を足すだけです。これで、キャラクターの高さの範囲は0-200になり、byteにキャストすることができます。&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;他にもスケーリングすることで値の取る範囲を小さくすることができます。例えば、浮動小数点で表されるラジアン角度を送る場合があるとします。ラジアンで円を表す範囲は０～２πになります。この値を０～２５５の範囲に収まるようにスケーリングすれば、byteにキャストして送ることができます。&lt;/P&gt;&lt;PRE class=code&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;float&lt;/SPAN&gt; rotationEncodeScale = 255.0f / &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;MathHelper&lt;/SPAN&gt;.TwoPi;
    &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;PacketWriter&lt;/SPAN&gt;.Write( (&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;byte&lt;/SPAN&gt;)( rotation * rotationEncodeScale ) );
&lt;/PRE&gt;
&lt;P&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;受信側では逆数を掛けることで、元の値にします。&lt;/P&gt;&lt;PRE class=code&gt;    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;float&lt;/SPAN&gt; rotationDecodeScale = &lt;SPAN style="COLOR: rgb(43,145,175)"&gt;MathHelper&lt;/SPAN&gt;.TwoPi / 255.0f;
    &lt;SPAN style="COLOR: rgb(0,0,255)"&gt;float&lt;/SPAN&gt; rotation = (&lt;SPAN style="COLOR: rgb(0,0,255)"&gt;float&lt;/SPAN&gt;)&lt;SPAN style="COLOR: rgb(43,145,175)"&gt;PacketReader&lt;/SPAN&gt;.ReadByte() * rotationDecodeScale;
&lt;/PRE&gt;
&lt;P&gt;&lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;この量子化では、精度が多少失われてしまいますが、通常は４バイトのデータを1/4に減らすことの方が価値があります。&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;訳注:&lt;BR&gt;原文のコードでは255ではなく256を使っていますが、この場合に変換できる値は上限値&lt;STRONG&gt;未満&lt;/STRONG&gt;の時に動作します。もし、上限値を指定した場合、結果は256になり、byteにキャストすると結果は０になってしまいます。ですが、この例で扱っている値はラジアンなので円を表す場合、値が０とMathHelper.TwoPiの時は結果が同じになるので問題なく動作します。&lt;/P&gt;
&lt;P&gt;同じ手法はシェーダープログラムなどで使うのですが、この場合に良く用いられるのは０～１までの値で１&lt;STRONG&gt;以下&lt;/STRONG&gt;の値を取るので、この場合はスケールを255にしないと動作しません。&lt;/P&gt;
&lt;P&gt;以上の理由から、ここではスケール値を255に変更しました。&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;原文:&lt;BR&gt;&lt;A title=http://blogs.msdn.com/shawnhar/archive/2007/12/24/network-compression-quantization.aspx href="http://blogs.msdn.com/shawnhar/archive/2007/12/24/network-compression-quantization.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/24/network-compression-quantization.aspx"&gt;http://blogs.msdn.com/shawnhar/archive/2007/12/24/network-compression-quantization.aspx&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7366427" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+_D530EC30FC30E030EF30FC30AF30_/default.aspx">XNA フレームワーク</category><category domain="http://blogs.msdn.com/ito/archive/tags/_B230FC30E0308B957A76_/default.aspx">ゲーム開発</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワーク　その５　圧縮</title><link>http://blogs.msdn.com/ito/archive/2008/01/29/network-05-compression.aspx</link><pubDate>Wed, 30 Jan 2008 05:38:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7317744</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/ito/comments/7317744.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7317744</wfw:commentRss><description>&lt;P&gt;限られたネットワーク帯域の中では、送信するデータを圧縮することは非常に重要なことです。&lt;/P&gt;
&lt;P&gt;Zip等の一般的な圧縮アルゴリズムはネットワークゲーム向けではありません。これらの圧縮はある程度のデータ量がある場合は効率良い圧縮が期待できますが、ネットワークパケットのように小さいデータを圧縮するには不向きです。ここで必要なのは２０バイトのデータを１０バイトにするような圧縮方法です。&lt;/P&gt;
&lt;P&gt;初心者がよく考える手法として、送信側で複数のパケットを続けてバイトストリームして一般的な圧縮アルゴリズムを使って圧縮し、圧縮されたデータをパケットに分割して送受信するというものがあります。確かに圧縮率は高くなるのですが、この手法には致命的名欠点があります。この手法では、圧縮したデータを展開するに全てのパケットが失われること無く、順番に配信される必要があります。この為にはSenDataOptions.ReliableInOrderを使う必要があり、レイテンシが増加する原因になってしまいます。&lt;/P&gt;
&lt;P&gt;また、変化量(前の状態との差)を送ることで全体のデータを送るより少ないデータ量で済ますことができますが、この手法も一般的な圧縮アルゴリズムを使うのと一緒で、全てのデータが順番に送信されるという保障があるときにのみ使える手法です。&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;通常は古典的なビットフィールドをまとめたり、量子化を使った方が良い結果になります。これらの手法は4KBのRAMを積んだ8ビットマイコン時代に良く使われたものですが、ギガバイト単位のデータを扱える.Netの世界では忘れ去られた手法でもあります。&lt;/P&gt;
&lt;P&gt;例えば、文字列を送るのでなく、整数のIDかenum値を送る。&lt;/P&gt;
&lt;P&gt;もし、行列が回転と移動の組み合わせで、スケールや、せん断、射影をしないと判っているのなら行列データを送らない。変わりに１２バイトのVector3であるMatrix.Translateと、１６バイトのQuaternion.CreateFromRotationMatrix(matrix)を送ります。これで６４バイトの行列データが２８バイトに圧縮されたことになります。&lt;/P&gt;
&lt;P&gt;以下、次回に続く。&lt;/P&gt;
&lt;P&gt;原文&lt;BR&gt;&lt;A title=http://blogs.msdn.com/shawnhar/archive/2007/12/22/network-compression.aspx href="http://blogs.msdn.com/shawnhar/archive/2007/12/22/network-compression.aspx" mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/22/network-compression.aspx"&gt;http://blogs.msdn.com/shawnhar/archive/2007/12/22/network-compression.aspx&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7317744" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/_B230FC30E0308B957A76_/default.aspx">ゲーム開発</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワーク　その４　帯域　ボイスチャットについて</title><link>http://blogs.msdn.com/ito/archive/2008/01/25/network-04-voice.aspx</link><pubDate>Fri, 25 Jan 2008 22:43:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7245455</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/ito/comments/7245455.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7245455</wfw:commentRss><description>&lt;P&gt;XNAフレームワークはボイスチャットをサポートしており、ヘッドセットがある場合に自動でチャットができるようになっています。便利な機能ではありますが、使用中はより多くのネットワーク帯域が必要になることに注意が必要です。&lt;/P&gt;
&lt;P&gt;音声データは500 B/s以下の帯域に圧縮され、ヘッドセットに向かって喋った時のみにデータ転送を行います。&lt;/P&gt;
&lt;P&gt;デフォルトの状態では全てのプレイヤー同士で会話することができるようになっています。もし、ひとりが他の15人のプレイヤーに話しかけた場合、&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;500 * 15 = 7.3KB/s&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;ひゃー!!　8KB/sが目標だったことを覚えていますか？これではゲームデータを送る前に殆どの帯域を使い切ってしまいます。&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;では、この厄介なボイスデータ帯域問題をどのようにして解決することができるのでしょうか？&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;同時にプレイできるプレイヤー数を少なくする 
&lt;LI&gt;もしくは、LocalNetworkGamer.EnableSendVoiceを状況に合わせて変更する 
&lt;UL&gt;
&lt;LI&gt;同じチームにのみ話せるようにする 
&lt;LI&gt;自分の近くにいるプレイヤーに対してのみ話せるようにする。ただし、頻繁にEnableSendVoiceを変更するのは止めましょう。この値を変更する度にその情報をネットワーク上に転送しないといけないので、頻繁に変更してしまうと逆により多くの帯域を使ってしまうことになってしまいます。 
&lt;LI&gt;&lt;A href="http://www.talula.demon.co.uk/motogp_online/index.html" target=_blank mce_href="http://www.talula.demon.co.uk/motogp_online/index.html"&gt;MotoGP&lt;/A&gt;ではゲーム用のデータ転送量の少ないロビー内では16人全員が自由に会話でき、ゲーム内では最も近い３人のプレイヤーと会話ができるようになっています。&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;原文:&lt;BR&gt;&lt;A title=http://blogs.msdn.com/shawnhar/archive/2007/12/20/network-bandwidth-voice.aspx href="http://blogs.msdn.com/shawnhar/archive/2007/12/20/network-bandwidth-voice.aspx" mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/20/network-bandwidth-voice.aspx"&gt;http://blogs.msdn.com/shawnhar/archive/2007/12/20/network-bandwidth-voice.aspx&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7245455" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+_D530EC30FC30E030EF30FC30AF30_/default.aspx">XNA フレームワーク</category><category domain="http://blogs.msdn.com/ito/archive/tags/_B230FC30E0308B957A76_/default.aspx">ゲーム開発</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワーク　その3　帯域</title><link>http://blogs.msdn.com/ito/archive/2008/01/23/network-03-packet-header.aspx</link><pubDate>Thu, 24 Jan 2008 03:53:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7213696</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/ito/comments/7213696.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7213696</wfw:commentRss><description>&lt;P&gt;ネットワーク帯域とは、どれだけの量のデータを送受信できるかを表します。データ量が上限に近づくほどにパケットロスの量が増え、この上限を超えたデータ量を送ろうとした場合、結果的にセッションから切断されることになってしまいます。&lt;/P&gt;
&lt;P&gt;XNA Frameworkでは帯域をバイト/秒で表します。まぎらわしいことにネットワークベンダーはビット/秒で表すのが好きで(多分、より数字を大きく見せるため)、さらにまぎらわしいことにどちらの表記もbpsやkbpsを使うことです。この表記が出てきた場合にはバイト/秒なのかビット/秒のどちらのことを表しているのか注意する必要があります。(訳注:私は個人的にバイト/秒は大文字のB/s、ビット/秒はをbit/sと区別して表記するようにしています)&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;より多くのユーザーに遊んで貰うために色々な家庭のインターネット接続を考慮してXboxのゲームでは最小で上り、下り8KB/sの帯域で動作することが求められます。&lt;/P&gt;
&lt;P&gt;ネットワーク帯域と言うと、ゲーム内で送受信するデータ量x接続するマシン数と考えがちですが、ここで忘れてはいけないのはデータパケットのヘッダー部分のサイズです。&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;20バイトのIPヘッダー&lt;/LI&gt;
&lt;LI&gt;8バイトのUDPヘッダー&lt;/LI&gt;
&lt;LI&gt;～22バイトのLIVEやXNA Frameworkで使われる部分(NAT Traversal、暗号化、Reliable/Ordered転送用の情報)&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;これで～50バイトのパケットヘッダーサイズになります。&lt;/P&gt;
&lt;P&gt;例えばプレーヤー毎にBoolean値を、60フレーム/秒の間隔で転送すると:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;1バイトのペイロードデータ&lt;/LI&gt;
&lt;LI&gt;+50バイトのヘッダー = 51バイト&lt;/LI&gt;
&lt;LI&gt;*60パケット/秒 = 3060バイト&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;ホァ！Boolean値一個のデータ転送だけでも3KB/sになってしまいます(ゴールは8KB/sということを思い出してください)。98%の帯域がパケットヘッダーで無駄に消費されてしまいます。&lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;では、どうやってこの厄介なヘッダー問題を回避することができるのでしょうか？&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;データ転送頻度を下げる&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;通常のゲームでは毎フレーム毎にパケットを送るのではなく、1秒間に10～20パケットの割合で送っています&lt;/LI&gt;
&lt;LI&gt;この転送頻度は動的に変化させることができます。例えば遠くのプレイヤーにはより低頻度でパケットを送ります&lt;/LI&gt;
&lt;LI&gt;&lt;A href="http://www.talula.demon.co.uk/motogp_online/index.html" target=_blank mce_href="http://www.talula.demon.co.uk/motogp_online/index.html"&gt;MotoGP&lt;/A&gt;ではXbox LIVE上で最大16人同時プレイができます。これだけの人数になると1秒間に4パケットが上限でした。この為により効果的な予測アルゴリズムを使って低レートでも滑らかに動作させる必要がありました。&lt;/LI&gt;&lt;/UL&gt;
&lt;LI&gt;パケットをまとめる&lt;/LI&gt;
&lt;UL&gt;
&lt;LI&gt;沢山の小さいパケットを1つの大きなパケットにすることで、パケットヘッダーのサイズの無駄を少なくすることができます。&lt;/LI&gt;
&lt;LI&gt;通知メッセージを即座に送るのではなく、複数のフレームを待たせることによってパケットをまとめる&lt;/LI&gt;
&lt;LI&gt;もし同じフレーム内で複数のパケットを同じ相手に送った場合、次にNetworkSession.Updateを呼んだときに自動的まとめてくれます。&lt;/LI&gt;
&lt;LI&gt;XNA Frameworkは自動的にボイスデータやrealiable UDPの確認などを既にあるパケットにまとめるので、これらのデータは独立したヘッダーを必要としません。&lt;/LI&gt;&lt;/UL&gt;&lt;/UL&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;原文&lt;BR&gt;&lt;A class="" title=http://blogs.msdn.com/shawnhar/archive/2007/12/18/network-bandwidth-packet-headers.aspx href="http://blogs.msdn.com/shawnhar/archive/2007/12/18/network-bandwidth-packet-headers.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/18/network-bandwidth-packet-headers.aspx"&gt;http://blogs.msdn.com/shawnhar/archive/2007/12/18/network-bandwidth-packet-headers.aspx&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7213696" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+_D530EC30FC30E030EF30FC30AF30_/default.aspx">XNA フレームワーク</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワーク　その2　パケットロス</title><link>http://blogs.msdn.com/ito/archive/2008/01/09/network-02-packet-loss.aspx</link><pubDate>Thu, 10 Jan 2008 06:24:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7048726</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/ito/comments/7048726.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7048726</wfw:commentRss><description>&lt;P&gt;ネットワークは信頼できません。 
&lt;P&gt;データーパケットを送ったときにはいろいろな事がおこります。 
&lt;OL&gt;
&lt;LI&gt;相手側に届くかもしれない&lt;/LI&gt;
&lt;LI&gt;届かないかもしれない&lt;/LI&gt;
&lt;LI&gt;届いたとしても、送った順番とは違う順番で届くかもしれない&lt;/LI&gt;
&lt;LI&gt;届いたとしても、その内容が壊れてるかもしれないし、誰かによって変更されているかもしれない&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;4つ目はXNAフレームワークベースのゲームでは問題になりません。XNAフレームワークではLiveのライブラリを使っているので送られるパケットは全て自動的に暗号化されているので、データ自体が改ざんされたり、壊れたりということを気にする必要はありません。&lt;/P&gt;
&lt;P&gt;2と3の問題については&lt;STRONG&gt;SendDataOptions.Reliable&lt;/STRONG&gt;と&lt;STRONG&gt;SendDataOptions.InOrder&lt;/STRONG&gt;フラグを指定することで解決できます。なぜフラグ指定をしないといけないのか疑問に思う人もいるかもしれません。常に&lt;STRONG&gt;SendDataOptions.ReliableInOrder&lt;/STRONG&gt;で送ればいいじゃないか、それ以前になぜ他のネットワークアプリケーションの用にTCPを使わないの？&lt;/P&gt;
&lt;P&gt;その理由はゲームでは常にシミュレーション結果をリアルタイムに更新する必要があるからです。TCPやSendDataOptions.ReliableInOrderを指定した場合、以下のような動作をします。&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;送信側はデータパケットをネットワークに送る&lt;/LI&gt;
&lt;LI&gt;送信側はデータパケットのコピーを内部の送信中キューに保存する&lt;/LI&gt;
&lt;LI&gt;パケットが届いた時に、受信側は「データが届いたよ」という確認メッセージを返信をする&lt;/LI&gt;
&lt;LI&gt;送信者は受信側からの確認メッセージが届いたら、そのパケットを送信キューから取り除く&lt;/LI&gt;
&lt;LI&gt;一定時間、確認メッセージが届かなかった場合は、もう一度同じパケットを送信しなおす&lt;/LI&gt;
&lt;LI&gt;もしパケットが送信順に届かなかった場合、例えばBとCのパケットがAを受信する前に届いたら、受信側はBとCのパケットをキューに保存する&lt;/LI&gt;
&lt;LI&gt;送信順序を保つために、受信側はAが届くまでの間待たなければいけない&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;この仕組みはウェブページのHTMLデータ等をダウンロードする時などには有効な方法です。何故なら、AのパケットにはHTMLのヘッダーやレイアウト部分を含んでいるわけですが、その情報無しに残りのデータを表示するのは意味がありません。&lt;/P&gt;
&lt;P&gt;ゲームの場合を考えてみましょう。例えばプレイヤーの座標をゲームループ内で送る場合は&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Aの時間の時のプレイヤー座標を送る&lt;/LI&gt;
&lt;LI&gt;Bの時間の時のプレイヤー座標を送る&lt;/LI&gt;
&lt;LI&gt;Cの時間の時のプレイヤー座標を送る&lt;/LI&gt;
&lt;LI&gt;AとBの座標情報が失われる&lt;/LI&gt;
&lt;LI&gt;Cの座標が届く&lt;/LI&gt;
&lt;LI&gt;受信側はCの情報が届いているのにも関わらず、AとBの座標が再び送信するまでの間、待つことになる&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;なんて間抜けな…。もし受信者がCの時点での座標を知っているのなら、それより過去の時間であるAとBを気にするのは無意味なことです。この状況ではAとBの情報は無視して、最新の座標に更新するのがより自然な方法です。&lt;/P&gt;
&lt;P&gt;SendDataOptions.ReliableInOrderを指定したときにパケットロスした場合、レイテンシは増加します。このフラグを指定しない場合にパケットロスが発生した瞬間だけガクっとしますが、その後は問題なくゲームを続けることができます。しかし、フラグを指定した場合、たった一つのパケットロスでさえ、それに続くパケット全てが失ったパケットが再び届くまでの時間分だけ遅延するこになります。&lt;/P&gt;
&lt;P&gt;SendDataOptions.ReliableかSendDataOptions.InOrderのどちらかを指定したときには問題にならないことに注意してください。この問題は両方のフラグを指定した(TCPの振舞いと同じ)時のみに発生します。&lt;/P&gt;
&lt;P&gt;転送を確実に行いたいが、パケットの送信順を気にする必要が無い(SendDataOptions.Reliableを指定)場合、パケットAがロスしてBが届いた時はゲーム側では単にBを受信したことになり、Aのパケットは送信しなおされますが続くほかのパケットの遅延には影響ありません。&lt;/P&gt;
&lt;P&gt;パケットの送信順は気にするが、パケットロスは気にする必要が無い(SendDataOptions.InOrderを指定)場合、それぞれのパケットには番号がつけられ、番号が古いものが、新しいものより後に届いた場合は古いものは無視されます。この方法が最もレイテンシが少ない方法です。&lt;/P&gt;
&lt;P&gt;ショーン(Shawn)曰く&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;SendDataOptions.InOrderをできるだけ多く使う&lt;/LI&gt;
&lt;LI&gt;SendDataOptions.Reliableは必要なときに使う&lt;/LI&gt;
&lt;LI&gt;SendDataOptions.ReliableInOrderはできるだけ少なく&lt;/LI&gt;&lt;/UL&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;原文&lt;BR&gt;&lt;A title=http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-packet-loss.aspx href="http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-packet-loss.aspx" mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-packet-loss.aspx"&gt;http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-packet-loss.aspx&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7048726" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+_D530EC30FC30E030EF30FC30AF30_/default.aspx">XNA フレームワーク</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワーク　その1　レイテンシ</title><link>http://blogs.msdn.com/ito/archive/2008/01/07/network-01-latency.aspx</link><pubDate>Tue, 08 Jan 2008 05:44:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:7022986</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/ito/comments/7022986.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=7022986</wfw:commentRss><description>&lt;P&gt;&lt;EM&gt;「さて」とセイウチはいった「ネットワークの話をしましょう」&lt;/EM&gt;&lt;/P&gt;
&lt;P&gt;(訳注:元ネタはルイス・キャロルの&lt;A href="http://blogs.yahoo.co.jp/fminorop34/52838986.html" target=_blank mce_href="http://blogs.yahoo.co.jp/fminorop34/52838986.html"&gt;セイウチと大工&lt;/A&gt;から)&lt;/P&gt;
&lt;P&gt;ネットワークゲームプログラマーには以下の三つの不死の敵がいます&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;レイテンシ &lt;/STRONG&gt;データが相手に届くまでに掛かる遅延時間&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;パケットロス &lt;/STRONG&gt;データが相手に届かない現象&lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;帯域 &lt;/STRONG&gt;送ることのできるデータ量の上限&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;以上の三つについて順に話しましょう。&lt;/P&gt;
&lt;P&gt;レイテンシは物理的理由によって決まります。SFの世界では何十年もの昔に実現しているのに、物理学者は未だに光速を超える手段を発見していません。ですから、秒速３０万キロ/秒を超える速度でデータを送ることができません。これは物理学者のせいです(そんなに難しいことなのか？)&lt;/P&gt;
&lt;P&gt;十分に速いだろって？&lt;/P&gt;
&lt;P&gt;僕はシアトルに住んでいて、同僚のイーライ(Eli)はニューヨークに住んでいた。その距離はおよそ4,000Km、光の速度で13ミリ秒掛かる。&lt;/P&gt;
&lt;P&gt;僕は以前イギリスに住んでいたけど、イギリスからシアトルまでは7,800Km、光の速度で26ミリ秒掛かることになる。&lt;/P&gt;
&lt;P&gt;60フレーム/秒のゲームの場合、各フレームは16ミリ秒毎に更新されるので、僕がイギリスの友達と遊んでる場合は2フレーム近い差(ラグ)が生じてしまう。&lt;/P&gt;
&lt;P&gt;ちょっと待ってくれ、話はそれだけではないんだ&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;ネットワークデータは真空中を通らない。光の速度といえば普通は真空中での光の速度を言うけど、実際のデータは光ケーブルや銅線ケーブルを通るので６割程度の速度に落ちてしまう&lt;/LI&gt;
&lt;LI&gt;ネットワークデータは一本線で繋がっている訳ではない。両サイドにモデムがあり、それぞれ10ミリ秒程度のレイテンシがある。また、友人は私と同じISPやスイッチボード上にいる訳ではないので実際にはルーターを介してデータ送られる。それぞれのルーターでは5～50ミリ秒程のレイテンシがある&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;最悪ケースは？&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Xboxのゲームでは最大200ミリ秒(0.2秒)の遅延が合っても動作するよう奨励されている&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;どうやったら、家で再現できるの？&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;NetworkSession.SimulateLatency = TimeSpan.FromMilliseconds(200)&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;どうすればいいの？&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;0.2秒の遅延があっても問題のないゲームデザインをする。例えばポーカーゲームなんかの場合は0.2秒程度の遅れは無視してもかまわないよね&lt;/LI&gt;
&lt;LI&gt;予測アルゴリズムを使ってラグを隠蔽する。レイテンシがあるお陰でリモートオブジェクトのその瞬間のゲームステートを知ることは不可能。でも0.2秒前の情報、例えば移動速度、加速しているのか、減速しているのか、旋回しているのかという情報を元に0.2秒後の状態を予測することはできる。 &lt;BR&gt;その予測が間違っていた場合でも、予測した座標から実際の座標へスムーズに補間することによってガクガク動くのを防ぐことができる。予測の90%くらいがあっていればラグが気にならなくなるね。 &lt;BR&gt;ラグを完全に消すことは物理学的に不可能なので、あくまで目的はラグが分からないように隠すということ&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;ここまでがShawn &lt;A href="http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-latency.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-latency.aspx"&gt;Hargreaves氏の投稿&lt;/A&gt;の翻訳です。&lt;/P&gt;
&lt;P&gt;ちなみに日本国内の場合は距離的問題は少ないと思います。例えば東京～札幌間の距離は850Kmで3ミリ秒となりますが、記事の中の通り、問題になるのはルーター間でレイテンシが大きな問題となるでしょう。&lt;/P&gt;
&lt;P&gt;予測アルゴリズムですが、以下のサンプルが参考になると思います。&lt;/P&gt;
&lt;P&gt;&lt;A title=http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Prediction-Sample.aspx href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Prediction-Sample.aspx" mce_href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Prediction-Sample.aspx"&gt;http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Prediction-Sample.aspx&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;追記:&lt;/P&gt;
&lt;P&gt;ゲーム中に表示されるPing値と、この記事で言及しているレイテンシとは違うものです。&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;レイテンシは片道時間　A地点からB地点にデータが届くまで掛かる時間&lt;/LI&gt;
&lt;LI&gt;Pingは往復時間&amp;nbsp; A地点からB地点まで、そしてB地点からA地点までデータを送り返すのに掛かる時間&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;行きと帰りが同じ速度と仮定すると0.2秒というレイテンシは0.4秒のPing値に相当することに注意してください。&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=7022986" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/_B230FC30E0308B957A76_/default.aspx">ゲーム開発</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item><item><title>ネットワークサンプルの配信</title><link>http://blogs.msdn.com/ito/archive/2007/12/20/network-program-samples.aspx</link><pubDate>Fri, 21 Dec 2007 05:23:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:6823222</guid><dc:creator>Yuichi Ito</dc:creator><slash:comments>0</slash:comments><comments>http://blogs.msdn.com/ito/comments/6823222.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ito/commentrss.aspx?PostID=6823222</wfw:commentRss><description>&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;XNA 2.0リリースと同時に配信されたネットワーク対応のスターターキットに&lt;A href="http://creators.xna.com/Education/StarterKits.aspx" target=_blank mce_href="http://creators.xna.com/Education/StarterKits.aspx"&gt;Net Rumble&lt;/A&gt;がありますが、もっとシンプルなサンプルが欲しいという人の為に4つのネットワークサンプルコードが配信されました。&lt;/P&gt;
&lt;P&gt;&lt;IMG alt="" src="http://creators.xna.com/themes/default/images/common/ClientServer.png" align=left mce_src="http://creators.xna.com/themes/default/images/common/ClientServer.png"&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Architectures_3A00_--Client_2F00_Server.aspx" target=_blank mce_href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Architectures_3A00_--Client_2F00_Server.aspx"&gt;Network Architectures: Client/Sever&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;XNA 2.0上でのシンプルなサーバー/クライアント型のネットワークゲームのサンプルです。このサンプルではクライアントは単に入力情報をサーバーに送るのと、レンダリングするだけの処理をしています。サーバー側はクライアントから受け取った情報を元にシミュレーションを実行し、その結果をクライアントに送るようになっています。&lt;/P&gt;
&lt;P&gt;&lt;IMG alt="" src="http://creators.xna.com/themes/default/images/common/PeerToPeer.png" align=left mce_src="http://creators.xna.com/themes/default/images/common/PeerToPeer.png"&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Architectures_3A00_--Peer_2D00_to_2D00_Peer.aspx" target=_blank mce_href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Architectures_3A00_--Peer_2D00_to_2D00_Peer.aspx"&gt;Network Architectures: Peer-to-Peer&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;上記のサーバー/クライアント形式に対して、このサンプルではピア・ツー・ピア形式のプログラムになっています。それぞれが自分の戦車のコントロールとシュミレーションを行い、その結果をセッションに参加している全てのプレイヤーに送るようになっています。そして、その結果を受け取った側は、その情報を元に処理を行います。&lt;/P&gt;
&lt;P&gt;&lt;IMG alt="" src="http://creators.xna.com/themes/default/images/common/NetworkStateManagement.png" align=left mce_src="http://creators.xna.com/themes/default/images/common/NetworkStateManagement.png"&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Game-State-Management.aspx" target=_blank mce_href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Game-State-Management.aspx"&gt;Network Game State Management&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;このサンプルではネットワークゲームを作る場合のセッション参加のための必要なシンプルなメニューを用いて行います。 &lt;BR&gt;シングルプレイヤー、LIVE、またはシステムリンクの選択に始まり、新規セッションの追加、動作中のセッションへの参加などの処理をしています。また、ネットワークに起因するエラーハンドリングの処理も行っているので参考になります。&lt;/P&gt;
&lt;P&gt;&lt;IMG alt="" src="http://creators.xna.com/themes/default/images/common/NetworkPrediction.png" align=left mce_src="http://creators.xna.com/themes/default/images/common/NetworkPrediction.png"&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Prediction-Sample.aspx" target=_blank mce_href="http://creators.xna.com/Headlines/developmentaspx/archive/2007/01/01/Network-Prediction-Sample.aspx"&gt;Network Prediction Sample&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;上で紹介したクライアント/サーバーとピア・ツー・ピアのサンプルでは1/60秒毎にデータを送っています。これはLAN環境では問題なく動作しますが、インターネットを介して遊ぶ場合には遅延や帯域の問題があります。そこで、このサンプルではピア・ツー・ピアのサンプルを元にしてインターネットを介しても動作するプログラムに改善しています。戦車の動きはパケットロスや遅延があった場合でも滑らかに動くように予測処理がなされています。またLAN環境でもインターネット環境をシミュレートするために&lt;STRONG&gt;NetworkSession.SimulateLatency&lt;/STRONG&gt;や&lt;STRONG&gt;NetworkSession.SimulatedPacketLoss&lt;/STRONG&gt;を使っています。&lt;/P&gt;
&lt;P&gt;これらのサンプルの他にも英文ですが、Shawn Hargreaves氏のサイトではネットワークゲームに関する有用な投稿が複数あります。&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-latency.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-latency.aspx"&gt;Network Latency&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-packet-loss.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-packet-loss.aspx"&gt;Network Packet loss&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/shawnhar/archive/2007/12/18/network-bandwidth-packet-headers.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/18/network-bandwidth-packet-headers.aspx"&gt;Network bandwidth: packet headers&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/shawnhar/archive/2007/12/20/network-bandwidth-voice.aspx" target=_blank mce_href="http://blogs.msdn.com/shawnhar/archive/2007/12/20/network-bandwidth-voice.aspx"&gt;Network bandwidth: voice&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;ひにけにの方でも、これらの投稿を日本語訳(意訳？)していこうと思います。&lt;/P&gt;
&lt;P&gt;それ以外にもXNAチームメンバのブログを日本語で紹介して欲しいという要望があればコメント欄でリクエストを受け付けます。&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6823222" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ito/archive/tags/_397DCB4E_/default.aspx">紹介</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+_D530EC30FC30E030EF30FC30AF30_/default.aspx">XNA フレームワーク</category><category domain="http://blogs.msdn.com/ito/archive/tags/XNA+2.0/default.aspx">XNA 2.0</category><category domain="http://blogs.msdn.com/ito/archive/tags/_CD30C330C830EF30FC30AF30_/default.aspx">ネットワーク</category></item></channel></rss>