トップページ > 過去ログ > 記事閲覧
通信について
名前:埴輪 日時: 2010/09/22 22:51

DXライブラリを使ってアクションゲームを 作ろうと考えています。 通信対戦ができるようにしたいと考えています。 チャットソフト程度ならメッセージの送受信時のみの通信ですが、 思いつく通信方法として、毎フレームごとに自機の位置などのステータス や行動のデータを送る、という方法ですが、 これだと、受信側でバッファから読み込んだデータが最新のものでなくなる (古いものから読んでしまう)などの問題点が考えられます。 アクションゲームなのでリアルタイム性が要求されるので致命的です。 どのように実装すればいいでしょうか? アドバイスお願いします。 ※DXライブラリの通信関連関数は使ったことがあるので、それらの使い方自体はわかるのですが・・・

Page: 1 |

Re: 通信について ( No.1 )
名前:管理人 日時:2010/09/25 20:21

私が以前ちょっと試したリアルタイム性の高いアクションゲームのオンライン対戦プログラムでの通信は ゲームが開始してから経過したフレーム数+そのときのプレイヤーのキー入力情報 の情報を毎フレーム「現在のフレーム+それ以前の60フレーム分」を通信相手とお互いに送信しあって、 相手のキー入力情報が届いてから初めてゲーム内時間を進める、という処理を行ったところ、それっぽく動きました ただ、ゲーム内時間は相手のキー入力情報が届くまで進めませんが、こちらのキー入力は相手に 送るためにガンガン先行して採取するので、相手からキー入力情報が届くまでの時間が長くなれば なるほど「自分が入力したキー入力が画面に反映されるまでの時間」が長くなります あと、DXライブラリのリファレンスに載っている通信機能はTCP/IPという通信プロトコルを 使用しているのですが、リアルタイム性の高いゲームではTCP/IPは遅くて実用に耐えないので、 UDPという通信プロトコルを使用します、こちらは ・相手に届かないかもしれない    送信したデータが途中で行方不明になって相手に届かないことがあります( TCP/IP では自動的に再送信してくれます ) ・送った順番で相手に届くとは限らない    1・2・3という順番でデータを送信しても、相手には2・1・3という順番で届く可能性があります    なので、送信データはどの順番で届いても処理に問題が発生しないような内容にしておく必要があります ・一度に送信できるデータサイズは 65507byte    ただ、データのサイズが大きいと送信時間が長くなるので、1フレーム辺りの送信データのサイズは    数百バイトに抑えた方が良いです という欠点がありますが、高速です DXライブラリのリファレンスには載っていませんが、以下のようなUDP通信用の関数があります // UDPを使用した通信を行うソケットハンドルを作成する( RecvPort を -1 にすると送信専用のソケットハンドルになります ) int MakeUDPSocket( int RecvPort ) ; // UDPを使用した通信を行うソケットハンドルを削除する int DeleteUDPSocket( int NetUDPHandle ) ; // UDPを使用した通信で指定のIPにデータを送信する、Length は最大65507、SendPort を -1 にすると MakeUDPSocket に RecvPort で渡したポートが使用されます // 戻り値 0以上;送信できたデータサイズ -1:エラー -2:送信データが大きすぎる -3:送信準備ができていない int NetWorkSendUDP( int NetUDPHandle, IPDATA SendIP, int SendPort, void *Buffer, int Length ) ; // UDPを使用した通信でデータを受信する、Peek に TRUE を渡すと受信に成功してもデータを受信キューから削除しません // // 引数補足 // // RecvIP : 送信してきた相手のIPを保存する IPDATA 構造体のアドレス( NULL でも大丈夫 ) // RecvPort : 受信に使用したポート番号( NULL でも大丈夫 ) // // 戻り値 0以上:受信したデータのサイズ -1:エラー -2:バッファのサイズが足りない -3:受信データがない int NetWorkRecvUDP( int NetUDPHandle, IPDATA *RecvIP, int *RecvPort, void *Buffer, int Length, int Peek ) ; // UDPを使用した通信で新たな受信データが存在するかどうかを調べる // 戻り値 -1:エラー TRUE:受信データあり FALSE:受信データなし int CheckNetWorkRecvUDP( int NetUDPHandle ) ; UDPはTCPと違い「相手に接続する」という過程がありません、なので、「接続を切る」という関数もなく データを送信するときも NetWorkSendUDP で相手のIPアドレスとポート番号をその都度、直接指定します ( 受信するときは相手に MakeUDPSocket の RecvPort で指定したポートに NetWorkSendUDP してもらい、 それを NetWorkRecvUDP で受信します ) 例えばポート番号 9876 に送信されたUDPパケットをひたすら受信して、受信したデータの総量を表示するだけのプログラムは 次のようになります #include "DxLib.h" BYTE Data[ 65500 ] ; int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { int UDPNetHandle ; int RecvSize, TotalRecvSize ; ChangeWindowMode( TRUE ) ; if( DxLib_Init() < 0 ) return -1 ; // 受信用UDPソケットハンドルの作成 UDPNetHandle = MakeUDPSocket( 9876 ) ; SetDrawScreen( DX_SCREEN_BACK ) ; // パケット受信 TotalRecvSize = 0 ; while( ProcessMessage() == 0 ) { ClearDrawScreen() ; // データを受信 RecvSize = NetWorkRecvUDP( UDPNetHandle, NULL, NULL, Data, sizeof( Data ), FALSE ); if( RecvSize >= 0 ) { TotalRecvSize += RecvSize; } DrawFormatString( 0, 0, GetColor( 255,255,255 ), "TotalRecvSize:%d", TotalRecvSize ) ; ScreenFlip() ; } DxLib_End() ; return -1 ; } アクションゲームの通信ではUDPは必須、ということと、それを使ってそれっぽい処理を実現することはできましたが、 完成形のものを作った経験は無いのでそれが正しい方法かはわかりません また、UDPに関しては上記以外にも注意することがありますので、よろしければ「UDP WinSock」などのキーワードで 検索して調べてみてください( WinSock は Windows上で TCP/IP通信や UDP通信を行うための機能で、DXライブラリも WinSock を使用して通信機能を作成しています )
Re: 通信について ( No.2 )
名前:三郎 日時:2010/09/26 20:59

私はWinのプログラムもゲームのプログラムも殆ど全く作ったことがなく分かりませんが参考にならないでしょうが思いを書いてみます。 DOSの時代にパソコン通信でモデムを使いATコマンドで通信した経験が少しあります。 この時はデータの早い遅いよりも確実にデータを受信したかどうかが一番重要でした。 囲碁将棋等の通信プログラム、組み込み系のA/D変換データの受信などに FIFO : (Fist In , Fist Out)で処理していた。 この方式はデータの取りこぼしが無いようにオーバフローが発生しないようになったいた筈。 この他にリング方式というのもあったがこれはオーバフローの危険があったような気がします。 ゲームの場合、データの確実性よりもスピードが優先されるなら状況は全く違いますね。 聞いた話ですが、LIFO:(Last In, First Out)と呼ばれる方式もあるとか、、、。
Re: 通信について ( No.3 )
名前:埴輪 日時:2010/10/03 13:04

確かに囲碁将棋等でしたらデータ送受信の確実性が重要ですね。 ある程度組めてきたらUDP、試してみます。 そのときはよろしくお願いします。

Page: 1 |