ページ 11

DxLibでインベーダーを作っています 質問です

Posted: 2013年7月01日(月) 20:57
by かず
今、私は敵の最前列を求めその最前列に弾を打たせようとしています
ですが2次元配列を使った時の敵の弾の発射がよくわからず最前列の求め方がこれで合っているのかわかりません。
最前列の求め方はこのような感じのほうがいいのではないか、弾はこのようなことをすればできるなど、解説といいますか、助言がほしいです。
プログラム歴はまだ2ヶ月半ととぼしく、ソースコードも大変見にくいとは思いますが、なにかこのような方法があるんじゃないか?と言う方はお願いします。



code
#include "DxLib.h"

void Player( );
void Enemy( );

//プレイヤーの表示座標
///////////////////////
int px = 100;
int py = 420;
///////////////////////

//int j;
//int i;//2次元配列に使う変数

//プレイヤーとプレイヤーの弾関係の変数
//////////////////////////////
int ShotX = 0;//プレイヤーの弾の位置 X
int ShotY = 0;// Y
int PlayerFlag = 0;//プレイヤーが生存しているかどうかを
int ShotFlag = 0;//プレイヤーの弾が発射されたかどうかのフラグ
int PlayerGraph;//プレイヤーの画像の読み込みに使う
int PlayerShotGraph;//プレイヤーの弾の画像の読み込みに使う
int Playerw, Playerh, PlayerShotw, PlayerShoth;//プレイヤーとプレイヤーの弾のサイズを得るときに使う変数
//////////////////////////////

//敵を表示するための2次元配列
///////////////////////////
int inv_x[ 10 ][ 8 ] = {};
int inv_ya[ 10 ][ 8 ] = {};
//////////////////////////

//敵と敵の弾関係の変数
///////////////////////////////
int EnemyShot = 0;
int EnemyShotX = 0;//敵の弾の位置 X
int EnemyShotY = 0;// Y
int EnemyFlag[ 10 ][ 8 ] = {};//敵の生存フラグ
int EnemyShotFlag[ 10 ][ 8 ];//敵の弾が発射されたかどうかのフラグ
int EnemyShotGraph[ 3 ];//敵の弾の画像読み込みに使う(分割画像なので配列)
int EnemyaGraph[ 2 ];//敵のアニメーションに使う A
int EnemybGraph[ 2 ];// B
int EnemycGraph[ 2 ];// C
int EnemyCount = 0;
int EnemySpaceFlag = 0;
int EnemySpace = 30;
int Enemyw, Enemyh, EnemyShotw, EnemyShoth;//敵の弾のサイズを得るときに使う変数
int direct = 1;//敵の移動方向
int REnd = 0;//敵の最端の変数
int FirstRow[ 10 ];//敵の最前列の変数
/////////////////////////////

unsigned int time = 0;
enum muki{ LEFT,RIGHT,DOWN };
int UnderLine = LoadGraph("image/UnderLine");//アンダーラインの画像の読み込み

int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR IpCmdLine,int nCmdShow)
{
ChangeWindowMode( TRUE ); //windowモードに変更
if ( DxLib_Init() == 1 ) //DXライブラリ初期化処理
return -1;

SetDrawScreen(DX_SCREEN_BACK); //描画先を裏画面に

PlayerGraph = LoadGraph("image/player.png");
PlayerShotGraph = LoadGraph("image/PlayerShot.png");
//分割画像の読み込み
LoadDivGraph( "image/Type1Enemy.png" , 2 , 2 , 1 , 24 , 16 , EnemyaGraph );
LoadDivGraph( "image/Type2Enemy.png" , 2 , 2 , 1 , 22 , 16 , EnemybGraph );
LoadDivGraph( "image/Type3Enemy.png" , 2 , 2 , 1 , 19 , 16 , EnemycGraph );
LoadDivGraph( "image/EnemyShot2.png" , 3 , 3 , 1 , 7 , 1 , EnemyShotGraph );


//敵Cの間隔
for ( int i = 2; i < 4; i++ ) {
for ( int j = 0; j < 10; j ++ ) {
inv_x[ j ][ i ] = 30 * j;
inv_ya[ j ][ i ] = 25 * i;
}
}

//敵Bの間隔
for ( int i = 4; i < 6; i++ ) {
for ( int j = 0; j < 10; j ++ ) {
inv_x[ j ][ i ] = 30 * j;
inv_ya[ j ][ i ] = 25 * i;
}
}

//敵Aの間隔
for ( int i = 6; i < 8; i++ ) {
for ( int j = 0; j < 10; j ++ ) {
inv_x[ j ][ i ] = 30 * j;
inv_ya[ j ][ i ] = 25 * i;
}
}

while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 ) {

Player( );
Enemy( );
time++;
}

DxLib_End( );
return 0;

}

// プレイヤーの移動、表示
void Player( ) {

//プレイヤーの移動
if ( CheckHitKey(KEY_INPUT_RIGHT) == 1) px += 2;
if ( CheckHitKey(KEY_INPUT_LEFT) == 1) px -= 2;

// スペースキーを押していて、且弾が撃ち出されていなかったら弾を発射する
if( CheckHitKey( KEY_INPUT_SPACE ) == 1 && ShotFlag == 0 ) {

//プレイヤーと弾の画像のサイズを得る
GetGraphSize( PlayerGraph , &Playerw , &Playerh ) ;
GetGraphSize( PlayerShotGraph , &PlayerShotw , &PlayerShoth ) ;

// 弾の位置をセット、位置はプレイヤーの中心にする
ShotX = ( Playerw - PlayerShotw ) / 2 + px ;
ShotY = ( Playerh - PlayerShoth ) / 2 + py ;

// 弾は現時点を持って存在するので、存在状態を保持する変数に1を代入する
ShotFlag = 1 ;
}

//プレイヤーの描画
DrawGraph(px,py,PlayerGraph,TRUE);

// 自機の弾の移動ルーチン( 存在状態を保持している変数の内容が1(存在する)の場合のみ行う )
if( ShotFlag == 1 ) {

// 弾を7ドット上に移動させる
ShotY -= 7;

// 画面外に出てしまった場合は存在状態を保持している変数に0(存在しない)を代入する
if( ShotY < -80 ) {
ShotFlag = 0 ;
}

// 画面に弾を描画する
DrawGraph( ShotX, ShotY, PlayerShotGraph, FALSE ) ;
}

//プレイヤーの壁あたり判定
if ( px < 20 ) {
px = 20;
}
if ( px > 600 ) {
px = 600;
}
}


//敵の表示
void Enemy( ) {

//敵の当たり判定
for ( int i = 3; i < 8; i++ ) {
for ( int j = 0; j < 10; j ++ ) {
if ( ( ( ShotX + 3 > inv_x[ j ][ i ] ) && ( ShotX < inv_x[ j ][ i ] + 22 ) )
&& ( ( ShotY + 5 > inv_ya[ j ][ i ] ) && ( ShotY < inv_ya[ j ][ i ] + 16 ) ) ) {
if ( EnemyFlag[ j ][ i ] == 0 ) {
EnemyFlag[ j ][ i ] = 1;
ShotX = 0;
ShotY = 0;
ShotFlag = 0;
EnemyCount++;
}
}
}
}

//4体倒したらスピード上昇
if ( EnemyCount % 5 == 4 ) {
if ( EnemySpaceFlag == 0 ) {
EnemySpace--;
EnemySpaceFlag = 1;
}
}else{
EnemySpaceFlag = 0;
}

//最端の敵の座標を求める(右)
if ( direct == RIGHT ) {
for ( int i = 0; i < 10; i++ ) {
for ( int j = 3; j < 8; j++ ) {
if ( EnemyFlag[ i ][ j ] == 0 ) {
REnd = i;
}
}
}
}else{//(左)
for ( int i = 9; i >= 0; i-- ) {
for ( int j = 3; j < 8; j++ ) {
if ( EnemyFlag[ i ][ j ] == 0 ) {
REnd = i;
}
}
}
}
DrawFormatString( 0, 0, 0xffffff, "REnd = %d", REnd );

//敵の最前列を求める
for ( int i = 0; i < 10; i++ ) {
for ( int j = 0; j < 8; j++ ) {
if ( EnemyFlag[ i ][ j ] == 0 ) {
FirstRow[ i ] = j;
}
}
}


//敵の移動(右)
if ( time % EnemySpace == 0 ) {
if ( direct == RIGHT ) {
for ( int i = 7; i >= 2 ; i-- ) {
for ( int j = 0; j < 10; j++ ) {
if ( inv_x[ REnd ][ 2 ] < 600 && direct == RIGHT ) {
inv_x[ j ][ i ] += 15;
}else{
inv_ya[ j ][ i ] += 15;
if ( i == 7 && j == 0) {
direct = LEFT;

}
}
}
}
}else{//左の移動
for ( int i = 7; i >= 2; i-- ) {
for ( int j = 9; j >= 0; j-- ) {
if( inv_x[ REnd ][ 2 ] > 40 && direct == LEFT ) {
inv_x[ j ][ i ] -= 15;
}else{
inv_ya[ j ][ i ] += 15;
if ( i == 3 && j == 0 ){
direct = RIGHT;
}
}
}
}
}
}
DrawFormatString( 0, 20, 0xffffff, " inv_x[ REnd ][ 2 ] = %d", inv_x[ REnd ][ 2 ] );
//敵を指定の2次元配列の位置に表示(敵C)
for ( int i = 3; i < 4; i++ ) {
for ( int j = 0; j < 10; j ++ ) {
if ( EnemyFlag[ j ][ i ] == 0 ) {
DrawGraph(inv_x[ j ][ i ],inv_ya[ j ][ i ],EnemycGraph[ ( time / EnemySpace ) % 2 ],TRUE);
}
}
}

//(敵B)
for ( int i = 4; i < 6; i++ ) {
for ( int j = 0; j < 10; j ++ ) {
if ( EnemyFlag[ j ][ i ] == 0 ) {
DrawGraph(inv_x[ j ][ i ],inv_ya[ j ][ i ],EnemybGraph[ ( time / EnemySpace ) % 2 ],TRUE);
}
}
}

//(敵A)
for ( int i = 6; i < 8; i++ ) {
for ( int j = 0; j < 10; j ++ ) {
if ( EnemyFlag[ j ][ i ] == 0 ) {
DrawGraph(inv_x[ j ][ i ],inv_ya[ j ][ i ],EnemyaGraph[ ( time / EnemySpace ) % 2 ],TRUE);
}
}
}
}
/code

Re: DxLibでインベーダーを作っています 質問です

Posted: 2013年7月01日(月) 21:15
by softya(ソフト屋)
codeタグを、ちゃんとご利用くださいね。プレビューで動作確認ができます。
一旦コードタグで投稿しますので、回答は次からになります。
かず さんが書きました:今、私は敵の最前列を求めその最前列に弾を打たせようとしています
ですが2次元配列を使った時の敵の弾の発射がよくわからず最前列の求め方がこれで合っているのかわかりません。
最前列の求め方はこのような感じのほうがいいのではないか、弾はこのようなことをすればできるなど、解説といいますか、助言がほしいです。
プログラム歴はまだ2ヶ月半ととぼしく、ソースコードも大変見にくいとは思いますが、なにかこのような方法があるんじゃないか?と言う方はお願いします。


コード:

#include "DxLib.h"

void Player( );
void Enemy( );

//プレイヤーの表示座標
///////////////////////
int px = 100;
int py = 420;
///////////////////////

//int j;
//int i;//2次元配列に使う変数

//プレイヤーとプレイヤーの弾関係の変数
//////////////////////////////
int ShotX = 0;//プレイヤーの弾の位置 X
int ShotY = 0;//						  Y
int PlayerFlag = 0;//プレイヤーが生存しているかどうかを
int ShotFlag = 0;//プレイヤーの弾が発射されたかどうかのフラグ
int PlayerGraph;//プレイヤーの画像の読み込みに使う
int PlayerShotGraph;//プレイヤーの弾の画像の読み込みに使う
int Playerw, Playerh, PlayerShotw,  PlayerShoth;//プレイヤーとプレイヤーの弾のサイズを得るときに使う変数
//////////////////////////////

//敵を表示するための2次元配列
///////////////////////////
int inv_x[ 10 ][ 8 ] = {};
int inv_ya[ 10 ][ 8 ] = {};
//////////////////////////

//敵と敵の弾関係の変数
///////////////////////////////
int EnemyShot = 0;
int EnemyShotX = 0;//敵の弾の位置 X
int EnemyShotY = 0;//			   Y
int EnemyFlag[ 10 ][ 8 ] = {};//敵の生存フラグ
int EnemyShotFlag[ 10 ][ 8 ];//敵の弾が発射されたかどうかのフラグ
int EnemyShotGraph[ 3 ];//敵の弾の画像読み込みに使う(分割画像なので配列)
int EnemyaGraph[ 2 ];//敵のアニメーションに使う A
int EnemybGraph[ 2 ];//						B
int EnemycGraph[ 2 ];//							C
int EnemyCount = 0;
int EnemySpaceFlag = 0;
int EnemySpace = 30;
int Enemyw, Enemyh, EnemyShotw, EnemyShoth;//敵の弾のサイズを得るときに使う変数
int direct = 1;//敵の移動方向
int REnd = 0;//敵の最端の変数
int FirstRow[ 10 ];//敵の最前列の変数
/////////////////////////////

unsigned int time = 0;
enum muki{ LEFT,RIGHT,DOWN };
int UnderLine = LoadGraph("image/UnderLine");//アンダーラインの画像の読み込み

int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR IpCmdLine,int nCmdShow)
{	
	ChangeWindowMode( TRUE );	//windowモードに変更
	if ( DxLib_Init() == 1 )				//DXライブラリ初期化処理
		return -1;

	SetDrawScreen(DX_SCREEN_BACK);	//描画先を裏画面に

	PlayerGraph = LoadGraph("image/player.png");
	PlayerShotGraph = LoadGraph("image/PlayerShot.png");
	//分割画像の読み込み
	LoadDivGraph( "image/Type1Enemy.png" , 2 , 2 , 1 , 24 , 16 , EnemyaGraph  );
	LoadDivGraph( "image/Type2Enemy.png" , 2 , 2 , 1 , 22 , 16 , EnemybGraph  );
	LoadDivGraph( "image/Type3Enemy.png" , 2 , 2 , 1 , 19 , 16 , EnemycGraph  );
	LoadDivGraph( "image/EnemyShot2.png" , 3 , 3 , 1 , 7 , 1 , EnemyShotGraph );
	

	//敵Cの間隔
	for ( int i = 2; i < 4; i++ ) {
		for ( int j = 0; j < 10; j ++ ) {
			inv_x[ j ][ i ] = 30 * j;
			inv_ya[ j ][ i ] = 25 * i;
		}
	}

	//敵Bの間隔
	for ( int i = 4; i < 6; i++ ) {
		for ( int j = 0; j < 10; j ++ ) {
			inv_x[ j ][ i ] = 30 * j;
			inv_ya[ j ][ i ] = 25 * i;
		}
	}

	//敵Aの間隔
	for ( int i = 6; i < 8; i++ ) {
		for ( int j = 0; j < 10; j ++ ) {
			inv_x[ j ][ i ] = 30 * j;
			inv_ya[ j ][ i ] = 25 * i;
		}
	}

	while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 ) {

		Player( );
		Enemy( );
		time++;
	}

	DxLib_End( );
	return 0;

}

// プレイヤーの移動、表示
void Player( ) {

	//プレイヤーの移動
	if ( CheckHitKey(KEY_INPUT_RIGHT) == 1) px += 2;
	if ( CheckHitKey(KEY_INPUT_LEFT) == 1) px -= 2;

	// スペースキーを押していて、且弾が撃ち出されていなかったら弾を発射する
	if( CheckHitKey( KEY_INPUT_SPACE ) == 1 && ShotFlag == 0 ) {

		//プレイヤーと弾の画像のサイズを得る
		GetGraphSize( PlayerGraph , &Playerw , &Playerh ) ;
		GetGraphSize( PlayerShotGraph , &PlayerShotw , &PlayerShoth ) ;

		// 弾の位置をセット、位置はプレイヤーの中心にする
		ShotX = ( Playerw - PlayerShotw ) / 2 + px ;
		ShotY = ( Playerh - PlayerShoth ) / 2 + py ;

		// 弾は現時点を持って存在するので、存在状態を保持する変数に1を代入する
		ShotFlag = 1 ;
	}

	//プレイヤーの描画
	DrawGraph(px,py,PlayerGraph,TRUE);

	// 自機の弾の移動ルーチン( 存在状態を保持している変数の内容が1(存在する)の場合のみ行う )
	if( ShotFlag == 1 ) {

		// 弾を7ドット上に移動させる
		ShotY -= 7;

		// 画面外に出てしまった場合は存在状態を保持している変数に0(存在しない)を代入する
		if( ShotY < -80 ) {
			ShotFlag = 0 ;
		}

		// 画面に弾を描画する
		DrawGraph( ShotX, ShotY, PlayerShotGraph, FALSE ) ;
	}

	//プレイヤーの壁あたり判定
	if ( px < 20 ) {
		px = 20;
	}
	if ( px > 600 ) {
		px = 600;
	}
}


//敵の表示
void Enemy( ) {

	//敵の当たり判定
	for ( int i = 3; i < 8; i++ ) {
		for ( int j = 0; j < 10; j ++ ) {
			if ( ( ( ShotX + 3 > inv_x[ j ][ i ] ) && ( ShotX < inv_x[ j ][ i ] + 22 ) )
				&& ( ( ShotY + 5 > inv_ya[ j ][ i ] ) && ( ShotY < inv_ya[ j ][ i ] + 16 ) ) ) {
					if ( EnemyFlag[ j ][ i ] == 0 ) {
						EnemyFlag[ j ][ i ] = 1;
						ShotX = 0;
						ShotY = 0;
						ShotFlag = 0;
						EnemyCount++;
					}
			}
		}
	}

	//4体倒したらスピード上昇
	if ( EnemyCount % 5 == 4 ) {
		if ( EnemySpaceFlag == 0 ) {
			EnemySpace--;
			EnemySpaceFlag = 1;
		}
		}else{
			EnemySpaceFlag = 0;
	}

	//最端の敵の座標を求める(右)
	if ( direct == RIGHT ) {
		for ( int i = 0; i < 10; i++ ) {
			for ( int j = 3; j < 8; j++ ) {
				if ( EnemyFlag[ i ][ j ] == 0 ) {
					REnd = i;
				}
			}
		}
	}else{//(左)
		for ( int i = 9; i >= 0; i-- ) {
			for ( int j = 3; j < 8; j++ ) {
				if ( EnemyFlag[ i ][ j ] == 0 ) {
					REnd = i;
				}
			}
		}
	}
	DrawFormatString( 0, 0, 0xffffff, "REnd = %d", REnd );

	//敵の最前列を求める
	for ( int i = 0; i < 10; i++ ) {
		for ( int j = 0; j < 8; j++ ) {
			if ( EnemyFlag[ i ][ j ] == 0 ) {
				FirstRow[ i ] = j;
			}
		}
	}


	//敵の移動(右)
	if ( time % EnemySpace == 0 ) {
		if ( direct == RIGHT ) {
			for ( int i = 7; i >= 2 ; i-- ) {
				for ( int j = 0; j < 10; j++ ) {
					if ( inv_x[ REnd ][ 2 ] < 600 && direct == RIGHT ) {
						inv_x[ j ][ i ] += 15;
					}else{
						inv_ya[ j ][ i ] += 15;
						if ( i == 7 && j == 0) { 
							direct = LEFT;
							
						}
					}
				}
			}
		}else{//左の移動
			for ( int i = 7; i >= 2; i-- ) {
				for ( int j = 9; j >= 0; j-- ) {
					if( inv_x[ REnd ][ 2 ] > 40 && direct == LEFT ) {
						inv_x[ j ][ i ] -= 15;
					}else{
						inv_ya[ j ][ i ] += 15;
						if ( i == 3 && j == 0 ){ 
							direct = RIGHT;
						}
					}
				}
			}
		}
	}
	DrawFormatString( 0, 20, 0xffffff, " inv_x[ REnd ][ 2 ] = %d", inv_x[ REnd ][ 2 ] );
	//敵を指定の2次元配列の位置に表示(敵C)
	for ( int i = 3; i < 4; i++ ) {
		for ( int j = 0; j < 10; j ++ ) {
			if ( EnemyFlag[ j ][ i ] == 0 ) {
				DrawGraph(inv_x[ j ][ i ],inv_ya[ j ][ i ],EnemycGraph[ ( time / EnemySpace ) % 2 ],TRUE);
			}
		}
	}

	//(敵B)
	for ( int i = 4; i < 6; i++ ) {
		for ( int j = 0; j < 10; j ++ ) {
			if ( EnemyFlag[ j ][ i ] == 0 ) {
				DrawGraph(inv_x[ j ][ i ],inv_ya[ j ][ i ],EnemybGraph[ ( time / EnemySpace ) % 2 ],TRUE);
			}
		}
	}

	//(敵A)
	for ( int i = 6; i < 8; i++ ) {
		for ( int j = 0; j < 10; j ++ ) {
			if ( EnemyFlag[ j ][ i ] == 0 ) {
				DrawGraph(inv_x[ j ][ i ],inv_ya[ j ][ i ],EnemyaGraph[ ( time / EnemySpace ) % 2 ],TRUE);
			}
		}
	}
}

Re: DxLibでインベーダーを作っています 質問です

Posted: 2013年7月01日(月) 21:23
by かず
コードタグですね。ありがとうございます。
次はしっかりと利用したいと思います。

Re: DxLibでインベーダーを作っています 質問です

Posted: 2013年7月01日(月) 21:27
by softya(ソフト屋)
まず、他の人から見てもi,jはややこしいのでx,yを使うことをおすすめします。
それと、EnemyFlag[ i ][ j ] == 0が生存状態みたいですが一般的には1だと思うので、今のうちに1にされた方が良いと思います。
これはZERO=無と言うイメージからくる連想の問題なので、なかなか逆は戸惑います。
あと、構造体を使われていないので変数の結びつきが分かり辛いなですね。できるだけ利用された方が良いと思います。

さて、質問の件ですが、最前列は限定条件で探せています。
ただ、問題は列が全滅して最前列がなかった時の処理が無いのが問題です。
前の値が残ったままとなるでしょう。

弾の発射の件ですが何が問題でしょうか?
主人公は発射できているので、同じだと思いますが。

Re: DxLibでインベーダーを作っています 質問です

Posted: 2013年7月01日(月) 21:56
by かず
ありがとうございます。
構造体はまだ手を付けてないという感じなので、これから先勉強して行きたいと思います。
他の指摘された点はすぐにでも直したいと思います。

最前列の件は、ほんの少しではありますが、自分の中でこうではないかというものが出てきています。
最後の弾の件なんですが、2次元配列で表示している敵に弾を打たせるということが、どのようにすればいいか、あまり検討がつきません。
そこで詰まっています。

Re: DxLibでインベーダーを作っています 質問です

Posted: 2013年7月01日(月) 22:03
by softya(ソフト屋)
たしかインベーダー本家は、同時に発射できる弾の数が2個程度だったような気がします(うる覚えなので間違っているかも)。
なので、配列で2個の弾を管理してください。弾の飛んでいるフラグとか座標とか色んな物を配列にしないといけなくなります。
 → 数が多くややこしいので、ここが本当なら構造体の出番です。
で発射する座標は、最前列が判明しているなら表示する座標も分かりますよね? そこから求められるはずです。
発射するかどうかは、配列のフラグを見て発射を決めてください。当然一列毎に発射有無を乱数で決める必要があります。

Re: DxLibでインベーダーを作っています 質問です

Posted: 2013年7月01日(月) 23:06
by かず
2個...ですか。そこも確かめてみますね。
構造体の勉強を疎かにせず変数の結びつきをわかりやすくしたいと思います。
少しですがなにかピンときて書けそうです。
softyaさんの解説、助言を参考に敵の弾の発射をやりたいと思います。
丁寧な解説ありがとうございました。