格ゲーのネット対戦について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
プラム
記事: 164
登録日時: 6年前
住所: 東海地方

格ゲーのネット対戦について

#1

投稿記事 by プラム » 3年前

現在、C言語とDxLibで東方の格ゲーを作っている者です。

ネット対戦を実装してみたのですが、なかなかうまくいかなくて困っています。
一応つながることはつながるんですが・・・クライアント側には正常に描画されるんですが、ホスト側には、自分の機体のみが表示され、いろいろと荒ぶります。 ←これは事故解決しました、もう一つのクライアントに渡しているexeファイルをReleaseでビルドされたものを渡していたつもりなのですが、何かの間違いでDebugのexeファイルをを渡していたみたいです。

なんですが。通信は上手くいったんですが、かなりタイムラグがひどいです。クライアントでキーを押してから、ホストに反映されるまでにおよそ3秒くらいかかります。何故なんでしょうか・・・・なにか良い対処法はないんですかね・・・・

クライアントのコード

コード:

void Link_S_Init(){								//クライアントの初期化

	DeleteUDPSocket( NetUDPHandle ) ;
	NetUDPHandle = MakeUDPSocket( 25953 ) ;
	state=0;
	Game_Init();
	mynum=1;

}

void Link_S_Update(){							//クライアントの更新
	
	if(state==0){

		DrawFormatString(50,0,GetColor(255,255,255),"IPアドレスを入力してね");
		
		ScreenFlip(); //裏画面情報を表画面に描画

		char StrBuf[ 81 ] , StrBuf2[ 81 ] ;

		// IPの入力を行う
		KeyInputSingleCharString( 0 , 300 , 80 , StrBuf , FALSE ) ;

		// ピリオドが3つあるか調べる
		j = 0 ;
		for( i = 0 ; i < 80 ; i ++ )
		{
			if( StrBuf[ i ] == '.' ) j ++ ;
		}
				
		// もし3つピリオドがなかった場合は入力のし直し
		if( j == 3 )
		{

			// 文字列からIPを抜き出す
			j = 0 ;
				k = 0 ;
				i = 0 ;
			while( !ProcessMessage() )
			{
				if( StrBuf[ i ] == '.' || StrBuf[ i ] == '\0' )
				{
					StrBuf2[ j ] = '\0' ;
					switch( k )
					{
					case 0 :IP.d1 = atoi( StrBuf2 ) ; break ;
					case 1 :IP.d2 = atoi( StrBuf2 ) ; break ;
					case 2 :IP.d3 = atoi( StrBuf2 ) ; break ;
					case 3 :IP.d4 = atoi( StrBuf2 ) ; break ;
					}
					k ++ ;
					if( k == 4 ) break ;

					j = 0 ;
				}
				else
				{
					StrBuf2[ j ] = StrBuf[ i ] ;
					j ++ ;
				}
				i ++ ;
			}
			
			state=1;

		}
	}
	
	if(state==1){

		NetWorkSendUDP( NetUDPHandle,IP,25953,&netS, sizeof(netS) ) ;

		// 新しい接続があった場合はループを出る
		if( CheckNetWorkRecvUDP( NetUDPHandle ) == TRUE ) {
			state=2;
		}
	
	}

	if(state==2){

		
		if(Key(KEY_INPUT_ESCAPE)==1){

			Next_Scene(eScene_Menu);

		}

		NetWorkRecvUDP( NetUDPHandle, NULL, NULL, &netJ, sizeof(netJ), FALSE ) ;

		for(int i=0;i<COF_NUM_KEY;i++){
	
			netS.COF[i]=Cof(pl[0].Profile,i);
			netS.COFDown[i]=CofDown(pl[0].Profile,i);
			
		}   
		 
		NetWorkSendUDP( NetUDPHandle,IP,25953,&netS, sizeof(netS)) ;
		
		allcount++;

		for(int i=0;i<4;i++){
			if(pl[i].frag==true){
		
				if(i==mynum){
					Player_Update( i);
					/*
					pl[i].x=netJ.pl[i].x;
					pl[i].y=netJ.pl[i].y;
					pl[i].num=netJ.pl[i].num;
					pl[i].Dash=netJ.pl[i].Dash;
					pl[i].Fx=netJ.pl[i].Fx;
					pl[i].Fy=netJ.pl[i].Fy;
					pl[i].TargetAngle=netJ.pl[i].TargetAngle;
					pl[i].stoptime=netJ.pl[i].stoptime;
					pl[i].Gard=netJ.pl[i].Gard;
					pl[i].GardP=netJ.pl[i].GardP;
					pl[i].Jump=netJ.pl[i].Jump;
					pl[i].AttackStatus=netJ.pl[i].AttackStatus;
					pl[i].GardP=netJ.pl[i].GardP;
					pl[i].Draw=netJ.pl[i].Draw;
					pl[i].vx=netJ.pl[i].vx;
					pl[i].vy=netJ.pl[i].vy;
					pl[i].frag=netJ.pl[i].frag;
				*/
				}else{
				
					Player_Update_Net( i,netJ.COF,netJ.COFDown);
					/*	
					pl[i].x=netJ.pl[i].x;
					pl[i].y=netJ.pl[i].y;
					pl[i].num=netJ.pl[i].num;
					pl[i].Dash=netJ.pl[i].Dash;
					pl[i].Fx=netJ.pl[i].Fx;
					pl[i].Fy=netJ.pl[i].Fy;
					pl[i].TargetAngle=netJ.pl[i].TargetAngle;
					pl[i].stoptime=netJ.pl[i].stoptime;
					pl[i].Gard=netJ.pl[i].Gard;
					pl[i].GardP=netJ.pl[i].GardP;
					pl[i].Jump=netJ.pl[i].Jump;
					pl[i].AttackStatus=netJ.pl[i].AttackStatus;
					pl[i].GardP=netJ.pl[i].GardP;
					pl[i].Draw=netJ.pl[i].Draw;
					pl[i].vx=netJ.pl[i].vx;
					pl[i].vy=netJ.pl[i].vy;
					pl[i].frag=netJ.pl[i].frag;
					*/
				}
				
				if(pl[i].num<=0){
			
					pl[i].frag=0;
			
				}
			
			}
		}
		
		allcount++;

		Shot_Update();

		Effect_Update();

		ChangeLightTypeDir( VGet( 0 ,-2600 , 3400 ) ) ;
	
		float x,z,y;

		x=(pl[0].x-pl[1].x);

		y=(pl[0].y-pl[1].y);
		
		if(x>0){
		x=-x;
		}

		if(y>0){
		y=-y;
		}

		z=x+y;

		if(z>0){
			z=-z;
		}


		SetCameraPositionAndTarget_UpVecY( VGet( (pl[0].x+pl[1].x)/2, ((pl[0].y+pl[1].y)/2)+20, z-30 ), VGet( (pl[0].x+pl[1].x)/2, ((pl[0].y+pl[1].y)/2)+10, 0.0f ) ) ;

//		SetCameraPositionAndTarget_UpVecY( VGet( (pl[0].x+pl[1].x)/2, ((pl[0].y+pl[1].y)/2)+20, -120 ), VGet( (pl[0].x+pl[1].x)/2, ((pl[0].y+pl[1].y)/2)+10, 0.0f ) ) ;

	//  SetCameraPositionAndTarget_UpVecY( VGet( 0, 20, -120 ), VGet( 50, 20, 0.0f ) ) ;
	
	}


}


void Link_S_Draw(){     //クライアントの描画
	
	if(state==0){
		DrawFormatString(50,0,GetColor(255,255,255),"IPアドレスを入力してね");
	
	}
	if(state==1){
		DrawFormatString(50,0,GetColor(255,255,255),"接続まちなう~");
	
	}
	
	if(state==2){

		// 3Dモデルのスケールをx軸方向に2倍にする
		MV1SetScale( stg.ModelHandleA, VGet( 1.5f, 1.5f, 1.5f ) ) ;
		MV1SetPosition( stg.ModelHandleA, VGet(0,-185,-420) ) ;
		MV1SetRotationXYZ( stg.ModelHandleA, VGet( 0.0f,0.0f, 0.0f ) ) ;

		// ステージモデルの描画
		MV1DrawModel( stg.ModelHandleB ) ;
	
		// ステージモデルの描画
		MV1DrawModel( stg.ModelHandleA ) ;
	
		for(int i=0; i<4;i++){
	
			// 指定位置にモデルを配置    
	
			MV1SetPosition( pl[i].ModelHandle, VGet( pl[i].x, pl[i].y, 0 ) ) ;
	

			if(pl[i].frag==true&&pl[i].Draw==true){
				// 3Dモデルの描画
				MV1DrawModel( pl[i].ModelHandle ) ;
		
	
			if(pl[i].status==4&&pl[i].Gard==0){
			
				SetDrawBlendMode( DX_BLENDMODE_ADD , 255 ) ;

				DrawBillboard3D( VGet( pl[i].x, pl[i].y+10,0 ), 0.5f, 0.5f, pl[i].GardP/8, 0, imgrs[0], true ) ;

				SetDrawBlendMode( DX_BLENDMODE_NOBLEND , 0 ) ;

			}

			SetDrawBlendMode( DX_BLENDMODE_ALPHA, 160 );

			VECTOR Screen = ConvWorldPosToScreenPos( VGet(pl[i].x,pl[i].y+25,0) ) ;
		
			DrawBox(Screen.x-15,Screen.y-5,Screen.x+120,Screen.y+30,GetColor(0,0,0),true);

			SetDrawBlendMode( DX_BLENDMODE_NOBLEND, 0 );

			DrawFormatString(Screen.x-10,Screen.y,GetColor(255,255,255),profile[pl[i].Profile].name);

		
			}
		}
	
		Shot_Draw();
	
		Effect_Draw();
	
		DrawBox(0,0,300,100,GetColor(255,255,255),true);

		DrawFormatString(0,0,0,"pl1,x:%f,y:%f",pl[0].x,pl[0].y);

		DrawFormatString(0,20,0,"pl2,x:%f,y:%f",pl[1].x,pl[1].y);
	
		DrawFormatString(0,40,0,"pl1 vy%f,y%f,",pl[0].vy,pl[0].y);
	
		DrawFormatString(0,60,0,"pl1 vy%f,y%f,",pl[1].vy,pl[1].y);



		DrawBox(150,300,300,400,GetColor(255,255,255),true);

		DrawBox(350,300,500,400,GetColor(255,255,255),true);

		DrawFormatString(150,300,0,"%d%",pl[0].Percentage);

		DrawFormatString(350,300,0,"%d%",pl[1].Percentage);

		DrawFormatString(150,325,0,"残機:%d",pl[0].num);

		DrawFormatString(350,325,0,"残機:%d",pl[1].num);


		DrawFormatString(150,350,0,profile[pl[0].Profile].name);
	
		DrawFormatString(350,350,0,profile[pl[1].Profile].name);
	
		if(ba<=1&&allcount<127){

			SetDrawBlendMode( DX_BLENDMODE_ALPHA, allcount*2 );

			DrawBox(0,0,640,480,GetColor(0,0,0),true);

			SetDrawBlendMode( DX_BLENDMODE_NOBLEND, 0 );
		
		
		}
		
		if( CheckNetWorkRecvUDP( NetUDPHandle ) == FALSE ) {

			DrawBox(50,50,100,200,GetColor(255,255,255),true);
			DrawFormatString(80,80,0,"相手からのデータ応答がありません",pl[1].num);
			

		}
	}
}[\code]

ホストのコード
[code]

void Link_J_Init(){ //ホストの初期化

	DeleteUDPSocket( NetUDPHandle ) ;

	NetUDPHandle = MakeUDPSocket( 25953 ) ;
	
	state=0;

	Game_Init();

	mynum=0;

}

int winpl;

int ba;

int iroha;


void Link_J_Update(){	//ホスト側の通信


	if(state==0){

		// 新しい接続があった場合はループを出る
		if( CheckNetWorkRecvUDP( NetUDPHandle ) == TRUE ) {

			state=1;
			NetWorkRecvUDP( NetUDPHandle, &IP, NULL, &netS, sizeof(netS), FALSE ) ;
			NetWorkSendUDP( NetUDPHandle,IP,25953,&netJ, sizeof(netJ)) ;

		}

	}

	if(state==1){
		
		
		if(Key(KEY_INPUT_ESCAPE)==1){

			Next_Scene(eScene_Menu);

		}
		NetWorkRecvUDP( NetUDPHandle, NULL, NULL, &netS, sizeof(netS), FALSE ) ;
	
		for(int i=0;i<4;i++){
			if(pl[i].frag==true){
	
				if(i==mynum){
					Player_Update( i);
				}else{
					Player_Update_Net( i,netS.COF,netS.COFDown);
				
				}
				if(pl[i].num<=0){
			
					pl[i].frag=0;
			
				}
			
			}
			
			netJ.pl[i]=pl[i];

		}
		
		for(int i=0;i<COF_NUM_KEY;i++){
	
			netJ.COF[i]=Cof(pl[0].Profile,i);
			netJ.COFDown[i]=CofDown(pl[0].Profile,i);

			
		}   
		 
		NetWorkSendUDP( NetUDPHandle,IP,25953,&netJ, sizeof(netJ)) ;
		
		allcount++;

		Shot_Update();

		Effect_Update();

		ChangeLightTypeDir( VGet( 0 ,-2600 , 3400 ) ) ;
	
	}
	
}

void Link_J_Draw(){						//ホストの描画
	
	if(state==0){
		DrawFormatString(50,0,GetColor(255,255,255),"接続待ちをしています。");
	
	}
	
	if(state==1){

		MV1SetScale( stg.ModelHandleA, VGet( 1.5f, 1.5f, 1.5f ) ) ;
		MV1SetPosition( stg.ModelHandleA, VGet(0,-185,-420) ) ;
		MV1SetRotationXYZ( stg.ModelHandleA, VGet( 0.0f,0.0f, 0.0f ) ) ;

		// ステージモデルの描画
		MV1DrawModel( stg.ModelHandleB ) ;
	
		// ステージモデルの描画
		MV1DrawModel( stg.ModelHandleA ) ;
	
		for(int i=0; i<4;i++){
	
			// 指定位置にモデルを配置    
	
			MV1SetPosition( pl[i].ModelHandle, VGet( pl[i].x, pl[i].y, 0 ) ) ;
	

			if(pl[i].frag==true&&pl[i].Draw==true){
				// 3Dモデルの描画
				MV1DrawModel( pl[i].ModelHandle ) ;
		
	
			if(pl[i].status==4&&pl[i].Gard==0){
			
				SetDrawBlendMode( DX_BLENDMODE_ADD , 255 ) ;

				DrawBillboard3D( VGet( pl[i].x, pl[i].y+10,0 ), 0.5f, 0.5f, pl[i].GardP/8, 0, imgrs[0], true ) ;

				SetDrawBlendMode( DX_BLENDMODE_NOBLEND , 0 ) ;

			}

			SetDrawBlendMode( DX_BLENDMODE_ALPHA, 160 );

			VECTOR Screen = ConvWorldPosToScreenPos( VGet(pl[i].x,pl[i].y+25,0) ) ;
		
			DrawBox(Screen.x-15,Screen.y-5,Screen.x+120,Screen.y+30,GetColor(0,0,0),true);

			SetDrawBlendMode( DX_BLENDMODE_NOBLEND, 0 );

			DrawFormatString(Screen.x-10,Screen.y,GetColor(255,255,255),profile[pl[i].Profile].name);

		
			}
		}
	
		Shot_Draw();
	
		Effect_Draw();
	
		DrawBox(0,0,300,100,GetColor(255,255,255),true);

		DrawFormatString(0,0,0,"pl1,x:%f,y:%f",pl[0].x,pl[0].y);

		DrawFormatString(0,20,0,"pl2,x:%f,y:%f",pl[1].x,pl[1].y);
	
		DrawFormatString(0,40,0,"pl1 vy%f,y%f,",pl[0].vy,pl[0].y);

		DrawFormatString(0,60,0,"pl1 vy%f,y%f,",pl[1].vy,pl[1].y);

	

		DrawBox(150,300,300,400,GetColor(255,255,255),true);

		DrawBox(350,300,500,400,GetColor(255,255,255),true);

		DrawFormatString(150,300,0,"%d%",pl[0].Percentage);

		DrawFormatString(350,300,0,"%d%",pl[1].Percentage);

		DrawFormatString(150,325,0,"残機:%d",pl[0].num);

		DrawFormatString(350,325,0,"残機:%d",pl[1].num);


		DrawFormatString(150,350,0,profile[pl[0].Profile].name);
	
		DrawFormatString(350,350,0,profile[pl[1].Profile].name);
	
		if(ba<=1&&allcount<127){

			SetDrawBlendMode( DX_BLENDMODE_ALPHA, allcount*2 );

			DrawBox(0,0,640,480,GetColor(0,0,0),true);

			SetDrawBlendMode( DX_BLENDMODE_NOBLEND, 0 );
		
		
		}
		
		if( CheckNetWorkRecvUDP( NetUDPHandle ) == FALSE ) {

			DrawBox(50,50,100,200,GetColor(255,255,255),true);
			DrawFormatString(80,80,0,"相手からのデータ応答がありません",pl[1].num);
			

		}

	}

}
構造体など

コード:


//プレイヤーに関する構造体
typedef struct{

	int COF[COF_NUM_KEY];
	int COFDown[COF_NUM_KEY];
	pl_st pl[4];
	
}net_J_st;

//プレイヤーに関する構造体
typedef struct{

	int COF[COF_NUM_KEY];
	int COFDown[COF_NUM_KEY];
	
}net_S_st;

int state;
int NetUDPHandle ;				// 接続相手のネットワークハンドル
int i,j,k;
IPDATA IP;        // 送信用IPアドレスデータ

//net_J_st netJ_test;
net_S_st netS;
net_J_st netJ;

最後に編集したユーザー プラム on 2016年8月07日(日) 18:04 [ 編集 1 回目 ]

アバター
MoNoQLoREATOR
記事: 284
登録日時: 9年前
住所: 東京

Re: 格ゲーのネット対戦について

#2

投稿記事 by MoNoQLoREATOR » 3年前

とりあえず全てのソースコードを公開しましょう。そうすれば回答が集まりやすくなるはずです。
GitHubとか、firestrageとかにプロジェクトごとアップするのがおすすめです。

アバター
プラム
記事: 164
登録日時: 6年前
住所: 東海地方

Re: 格ゲーのネット対戦について

#3

投稿記事 by プラム » 3年前

すいません、事故解決しました。No:1を編集しました。
あと、Projectごと上げるのはちょっと・・・あとから必要になれば上げますね・・・

あと、接続は上手くいったんですがタイムラグが尋常じゃないくらいやばいです。何故なのでしょうか。。。
何か良い対処法はないでしょうか・・・

アバター
Dixq (管理人)
管理人
記事: 1661
登録日時: 9年前
住所: 北海道札幌市
連絡を取る:

Re: 格ゲーのネット対戦について

#4

投稿記事 by Dixq (管理人) » 3年前

大きなプログラムのバグを抽象的に聞いても誰も答えられないと思います。
回答は欲しいけどソースコードは開示したくないんじゃムリゲーじゃないでしょうか・・。

アバター
へにっくす
記事: 628
登録日時: 7年前
住所: 東京都

Re: 格ゲーのネット対戦について

#5

投稿記事 by へにっくす » 3年前

マルチスレッドってわかるかな?
すでにそういう実装してるのであれば、そこに関係するコードを掲示してください。
そういう実装をしていないのであれば、描画と接続と通信とすべて一つのスレッドで行うことになるので、同期がとりにくいと思いますね
UDP使っているならなおさら(TCPと違ってただ垂れ流すだけなので)。
オフトピック
個人的にはもっと関数化してほしいですね。stateによって処理が分かれているのは分かるのですが今のコードはちょっと見にくいです・・・
written by へにっくす

アバター
プラム
記事: 164
登録日時: 6年前
住所: 東海地方

Re: 格ゲーのネット対戦について

#6

投稿記事 by プラム » 3年前

>>Dixqさん
ですよね・・・GitHubの使い方がいまいちわからないので、あとでちゃんと調べてうpしておきますね。

>>へにっくすさん
マルチスレッドは実装してないです
やっぱり無いと上手く同期が取れないんですかね・・・
もし作るとしたらどのように実装してどのように処理をしたら良いと思いますか?
いろいろ調べてみたのですが。いまいちよくわかりません・・・無知ですみません。

あと、やっぱり通信にはUDPを使った方が良いんでしょうか、いろいろUDPで試してみて、開発側としては扱いが面倒というか、難しいと感じました。
まだTCPを使ったことはないですが、TCPでも速度は追いつきますかね・・・

アバター
へにっくす
記事: 628
登録日時: 7年前
住所: 東京都

Re: 格ゲーのネット対戦について

#7

投稿記事 by へにっくす » 3年前

ノウル さんが書きました:あと、やっぱり通信にはUDPを使った方が良いんでしょうか、いろいろUDPで試してみて、開発側としては扱いが面倒というか、難しいと感じました。
まだTCPを使ったことはないですが、TCPでも速度は追いつきますかね・・・
UDPは送信側が単純にデータを送るだけで、受信側がどうとかは気にしません。
そして受信側がそのまま連続したデータを受け取れるかというとそんなことはないのですよ。
別の経路を通ってきた同じデータかも知れませんし、前後が入れ替わっているかも知れません。
また、どっかのルータで破棄されてしまっているかも知れません。
UDPはそういうプロトコルです。
TCPはちゃんと連続したデータが送れているかお互い確認しあってやりとりするのでプログラム側は考える必要がありませんが、UDPは受け取ったデータが連続してるかはプログラム側が判断する必要があります。
なので難しいのは当たり前です。
TCPに切り替えたとしても、速度が追いつくかどうかは結局接続した回線の速度に依存するので、あまり変わらないでしょう。逆にお互い確認しあってるぶん、さらに時間がかかるかもしれません。

マルチスレッドにする方法といいますが、それをまともに書いても概念を理解してないとだめです。
まず自分で単純なプログラムなど作成し試行錯誤してみてください。
written by へにっくす

閉鎖

“C言語何でも質問掲示板” へ戻る