敵の弾が自機を狙わない

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ひかりまる

敵の弾が自機を狙わない

#1

投稿記事 by ひかりまる » 13年前

C++でシューティングゲームを作成しようと思い、プログラミング入門サイト ~easy to understand~様のを見ながらコード入力してたのですが
そこの13章の敵の弾が自機を狙う処理でつまづきました。
3日程試行錯誤して、1から打ち直したりしたのですがどうにも改善しないのでここで質問させていただきました。
ここに載せる事は上のHPの管理人様には許可を得ています。
Microsoft VisualC++ 2010 Express で作成しています Dxライブラリ使用です。
C++は少しかじった程度で、しかも勉強してからだいぶ期間空いてしまったので理解してない点が多いです。
長いですが宜しくお願いします。
まず↓が敵クラスの発射関数です。
発射フラグが立った時にプレイヤーの座標を取得し、1度だけプレイヤーへの角度を求めています。
ですがショットパターンが1及び2の時、プレイヤーの初期座標が常に入りっぱなしで、動いた後次の敵が来ても、敵の弾がプレイヤーを狙ってくれないのです。

コード:

void CEnemy::Shot()
{
	//CONTROLクラスの参照
	CONTROL &control = CONTROL::GetInstance();
	double dbPx = 0, dbPy = 0;


	//発射タイミングになったらフラグを立てる
	if( this->n_Shottime == g_Count ){
		this->fShot = TRUE;
	}

	//フラグが立っている時だけ
	if( this->fShot ){
		if( !this->fDead){
			//プレイヤーの座標取得
			control.GetPlayerPos( &dbPx, &dbPy );
			//敵とプレイヤーの位置から逆正接を求める
			//初回だけ実行
			if( this->nScount == 0 )this->dbRad = atan2( (dbPy - this->db_eY), (dbPx - this->db_eX) );
			
			//ショットパターンを参照して発射方法を分ける
			switch( this->byS_pat ){
				//前方にショット
				case 0:
					//5ループに1回発射 20までなので5発発射
					if( this->nScount%5 == 0 && this->nScount <= 20 ){
						for( int i = 0; i < E_SHOT_MAX; i++ ){
							//フラグが立ってない弾を探して座標等をセット
							if( this->E_shot[i].fShot == FALSE ){
								this->E_shot[i].fShot = TRUE;		//発射中フラグ
								this->E_shot[i].dbX = this->db_eX;	//X座標
								this->E_shot[i].dbY	 = this->db_eY;	//Y座標
								this->E_shot[i].dbRad = this->dbRad;//発射角度
								break;
							}
						}
					}
					break;
				//プレイヤーに向かって直線ショット
				case 1:
					//6ループに1回発射、54までなので10発発射
					if( this->nScount % 6 == 0 && this->nScount <= 54 ){
						//フラグが立ってない弾を探して座標などをセット
						for( int i = 0; i < E_SHOT_MAX; ++i ){
							if( !this->E_shot[i].fShot ){
								this->E_shot[i].fShot = TRUE;
								this->E_shot[i].dbX   = this->db_eX;
								this->E_shot[i].dbY   = this->db_eY;
								this->E_shot[i].dbRad = this->dbRad;
								break;
							}
						}
					}
					break;
				//3方向ショット
				case 2:
					//10ループに1回発射、1ループに3発なので5ループさせると15発発射
					if( this->nScount % 10 == 0 && this->nScount <= 40 ){
						for( int i = 0; i < E_SHOT_MAX; ++i ){
							//フラグが立っていない弾を探して、座標等をセット
							if( !this->E_shot[i].fShot ){
								this->E_shot[i].fShot = TRUE;
								this->E_shot[i].dbX = this->db_eX;
								this->E_shot[i].dbY = this->db_eY;
								//3方向発射の処理。発射中弾数カウント用変数を参照して
								//0の時は左より
								if( this->nBulletNum == 0 ){
									//敵とプレイヤーとの逆正接から10度引いたラジアンを代入
									this->E_shot[i].dbRad = this->dbRad - ( 10 * 3.14 / 180 );
								//1の時はプレイヤー一直線
								}else if( this->nBulletNum == 1 ){
									//敵とプレイヤーの逆正接を代入
									this->E_shot[i].dbRad = this->dbRad;
								//2の時は右より
								}else if( this->nBulletNum == 2 ){
									//敵とプレイヤーとの逆正接から10度足したラジアンを代入
									this->E_shot[i].dbRad = this->dbRad + ( 10 * PI / 180 );
								}
								//弾数カウントを足す
								this->nBulletNum++;
								//3発発射したらカウントを0にしてループを抜ける
								if( this->nBulletNum == 3 ){
									this->nBulletNum = 0;
									break;
								}
							}
						}
					}
					break;
				//乱射ショット
				case 3:
					//5ループに1回発射、70までなので15発発射
					if( this->nScount % 5 == 0 && this->nScount <= 70 ){
						for( int i = 0; i < E_SHOT_MAX; ++i ){
							//フラグが立ってない弾を探して、座標等をセット
							if( !this->E_shot[i].fShot ){
								this->E_shot[i].fShot = TRUE;
								this->E_shot[i].dbX = this->db_eX;
								this->E_shot[i].dbY = this->db_eY;
								//初回だけ乱数初期化
								if( this->nBulletNum == 0 ){
									srand( (unsigned int)time( NULL ) );
								}
								this->E_shot[i].dbRad = atan2( dbPy - this->db_eY, dbPx - this->db_eX ) - ( 60 * PI / 180 ) + ( (rand() % 120) * PI / 180 );
								this->nBulletNum++;
								break;
							}
						}
					}
					break;
			}
		}

		//フラグが立ってる弾の数
		int fBullet = 0;

		//フラグが立っている弾の数だけ移動を行う
		for( int i = 0; i < E_SHOT_MAX; ++i ){
			if( this->E_shot[i].fShot ){
				switch( this->E_shot[i].byS_pat ){
				//単純に下に発射
				case 0 :
					this->E_shot[i].dbY += this->E_shot[i].byB_Speed;
					break;
				case 1:
					this->E_shot[i].dbX += this->E_shot[i].byB_Speed * cos( this->E_shot[i].dbRad );
					this->E_shot[i].dbY += this->E_shot[i].byB_Speed * sin( this->E_shot[i].dbRad );
					break;
				case 2:
					this->E_shot[i].dbX += this->E_shot[i].byB_Speed * cos( this->E_shot[i].dbRad );
					this->E_shot[i].dbY += this->E_shot[i].byB_Speed * sin( this->E_shot[i].dbRad );
					break;
				case 3:
					this->E_shot[i].dbX += this->E_shot[i].byB_Speed * cos( this->E_shot[i].dbRad );
					this->E_shot[i].dbY += this->E_shot[i].byB_Speed * sin( this->E_shot[i].dbRad );
					break;
				}

				//弾が画面外へ行ったらフラグを戻す
				if( this->BulletOutCheck(i) ){
					this->E_shot[i].fShot = FALSE;
					continue;
				}
				++fBullet;
			}
		}
		//fBulletが0という事は発射中の弾がない
		//かつdeadflagがTRUEということはこの敵クラスを消滅させても良い	
		if( fBullet == 0 && this->fDead ){
			this->fEnd = TRUE;
		}

		++this->nScount;
	}

	DrawFormatString( 30, 200, 0xffff, "X:%d", (int)dbPx );
	DrawFormatString( 30, 220, 0xffff, "Y:%d", (int)dbPy );

}
全てを統括するcontrolクラスでプレイヤーや敵などのクラスの関数を動かしています

コード:

void CONTROL::All()
{

	//描画領域の指定
	SetDrawArea( MARGIN, MARGIN, (MARGIN+BGWIDTH), (MARGIN+BGHEIGHT) );
	bground->All();		//背景クラスのAll関数実行
	player->All();		//プレイヤークラスのAll関数実行

	//敵の総数分敵クラスを作成
	for( int i = 0; i < ENEMY_MAX; ++i ){
		if( enemy[i] != NULL ){
			if( enemy[i]->All() ){	
				delete enemy[i];	//敵クラス消滅フラグがTRUEなら解放する
				enemy[i] = NULL;
			}
		}
	}


	++g_Count;	//ゲームループカウント
}
下がプレイヤークラスでの座標取得関数と統括であるcontrolクラスでのプレイヤー座標取得関数です
敵クラスで座標取得関数を呼び出すためにcontorlクラスにプレイヤー座標取得関数を作っています

コード:

void CPlayer::GetPosition( double* lpdbX, double* lpdbY )
{
	*lpdbX = this->dbPx;
	*lpdbY = this->dbPy;

}
void CONTROL::GetPlayerPos( double* lpdbX, double* lpdbY )
{
	double tempx, tempy;	//一時取得用

	this->player->GetPosition( &tempx, &tempy );

	*lpdbX = tempx;
	*lpdbY = tempy;
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 敵の弾が自機を狙わない

#2

投稿記事 by softya(ソフト屋) » 13年前

デバッガで調べたり、OutputDebugString()関数で動きを知らべたりされていますか?
printfを中心としたデバッグ技法をお教えしますので、まずご自分で調べてみてください。

「簡単RPG講座 番外編。 デバッグ入門 • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/blog.php?u=114&b=982&c=2

デバッグ技法でわからないことは聞いてくださいね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ひかりまる

Re: 敵の弾が自機を狙わない

#3

投稿記事 by ひかりまる » 13年前

Printfでは無いのですが、同じ効果の関数でプレイヤーの座標や、敵の情報等は画面に表示しています。
デバッガもやりましたがどのタイミングでも値が変わることはありませんでした。
プレイヤークラスコンストラクタで設定した初期座標がGetPlayerPosition関数を介して得られているので
取得関数自体にミスは無いと思われます。 やはり敵のShot関数かプレイヤークラスかどこかに落ち度があると思うので
もう少し粘ってみます。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 敵の弾が自機を狙わない

#4

投稿記事 by softya(ソフト屋) » 13年前

GetPlayerPos()でちゃんと座標が取得できていないなら別の問題があることになります。
プログラムの流れをちゃんと追いかけましたか?
最初のCONTROL::GetInstance();で取得しているインスタンスが正しいかも確認する必要があります。
あとGetPlayerPos()などの変数の受け渡しはポインタではなく参照を使った方がすっきりしますよ。

【追記】
発射時にthis->nScountが0なのかも気になる所です。
this->dbRadがちゃんと変化するか変わる条件を見直し見るべきでしょう。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ひかりまる

Re: 敵の弾が自機を狙わない

#5

投稿記事 by ひかりまる » 13年前

GetInstanceで取得しているインスタンスがおかしい可能性が出てきたのですが
controlクラスを生成しているmainの方でGetPlayerPositionrを使い値を表示させたところ正常に値が反映されていました。
ところが敵クラスのインスタンスから参照している方は条件文を外して、常に取得する状態にしてもプレイヤークラスのコンストラクタで
初期化されている座標のまま変動無しでした。
インスタンスが良くわかってないのですが、controlヘッダーで、GetInstanceという静的関数を作り、CONTROLクラス自体はその関数内で、
static指定子をつけ、静的変数という形で宣言しています。
静的変数でも宣言された段階でこの静的関数の中で各クラスのコンストラクタ・関数が呼ばれ、その値を参照してしまってるという事は考えられないでしょうか?
うちの頭だとそれなら常に、コンストラクタで設定しているプレイヤーの初期座標を参照している理由になるのですが・・・・
インスタンスについて勉強してきた方が良さそうです

アバター
h2so5
副管理人
記事: 2212
登録日時: 15年前
住所: 東京
連絡を取る:

Re: 敵の弾が自機を狙わない

#6

投稿記事 by h2so5 » 13年前

ひかりまる さんが書きました: インスタンスが良くわかってないのですが、controlヘッダーで、GetInstanceという静的関数を作り、CONTROLクラス自体はその関数内で、
static指定子をつけ、静的変数という形で宣言しています。
静的変数でも宣言された段階でこの静的関数の中で各クラスのコンストラクタ・関数が呼ばれ、その値を参照してしまってるという事は考えられないでしょうか?
うちの頭だとそれなら常に、コンストラクタで設定しているプレイヤーの初期座標を参照している理由になるのですが・・・・
静的変数として宣言しているということは、
最初にGetInstanceを呼んだ時のみ初期化が行われますからコンストラクタも1回しか呼ばれません。

ひかりまる

Re: 敵の弾が自機を狙わない

#7

投稿記事 by ひかりまる » 13年前

遅くに返信ありがとうございます。
イメージとしては、GetInstanceを呼んだ時、もう一つのcontrolオブジェクトが生成されて その中のプレイヤーの座標を取得してるのでは・・・と。
mainの方ではちゃんと動かしてもプレイヤー座標が取得できているのにインスタンスの方では常に初期座標というのがどうにも腑に落ちなくて。

へにっくす

Re: 敵の弾が自機を狙わない

#8

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

ひかりまる さんが書きました:遅くに返信ありがとうございます。
イメージとしては、GetInstanceを呼んだ時、もう一つのcontrolオブジェクトが生成されて その中のプレイヤーの座標を取得してるのでは・・・と。
mainの方ではちゃんと動かしてもプレイヤー座標が取得できているのにインスタンスの方では常に初期座標というのがどうにも腑に落ちなくて。
完全なSingletonパターンでない可能性が高いですね。
コンストラクタをprivateにする必要がありますが、そうしていますか?
Singletonパターン - ソフトウェア工学

ひかりまる

Re: 敵の弾が自機を狙わない

#9

投稿記事 by ひかりまる » 13年前

完全なSingletonパターンでない可能性が高いですね。
コンストラクタをprivateにする必要がありますが、そうしていますか?

>へにっくすさん
singletonでした。
controlクラスのヘッダーでコンストラクタをprivateにしてませんでした。お恥ずかしい限りです。
コンストラクタをprivate属性にしたところ無事治りました。
返信してくださった方ありがとうございました。

ひかりまる

Re: 敵の弾が自機を狙わない

#10

投稿記事 by ひかりまる » 13年前

この掲示板は初なのですが引用レスで解決にチェック入れてもトピック自体に解決は付かないのでしょうか?
解決したのに、一覧では「解決」となってなかったので心配になって書き込みました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 15年前
住所: 東海地方
連絡を取る:

Re: 敵の弾が自機を狙わない

#11

投稿記事 by softya(ソフト屋) » 13年前

ひかりまる さんが書きました:この掲示板は初なのですが引用レスで解決にチェック入れてもトピック自体に解決は付かないのでしょうか?
解決したのに、一覧では「解決」となってなかったので心配になって書き込みました。
まれにそういう事がありますが気にしないで下さい。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

閉鎖

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