ブロックと敵との当たり判定

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

ブロックと敵との当たり判定

#1

投稿記事 by ヨシタケ » 13年前

ブロック崩しを作っていて、敵の設定を行っています。敵をバーの位置に合わせて左右に移動し、ブロックに当たると止まるようにしたいのですが、うまくいきません。
判定はif(敵の右側のx座標 <ブロックの左側のx座標)、if(敵の左側のx座標 <ブロックの右側のx座標)のとき移動というようにしています。
ブロックはj=0~9と横並びで真ん中3つを消しています。j<2のとき上記の左の判定、j>6のとき右の判定のようにしています。
また、線分の四角の当たり判定の方法を調べ関数を作り試してみたのですが駄目でした。

下記のサイトにファイルを上げました。パスワードは「YT2012」です。

http://www1.axfc.net/uploader/so/2695694

当たり判定の設定はhit.cppのstage4_hit()です。
正直もとからブロックとの当たり判定どうすればいいかわからないというのがありました。これから敵の種類を増やしていく場合必要になるためどうか教えてください。

Eeel

Re: ブロックと敵との当たり判定

#2

投稿記事 by Eeel » 13年前

こんばんは。

コード:

if(敵を左に移動させたい){
	if(関数:ブロックにぶつかるか(敵.x-2) == ぶつからない){
		敵.x -= 2;
	}
}
if(敵を右に移動させたい){
	if(関数:ブロックにぶつかるか(敵.x+2) == ぶつからない){
		敵.x += 2;
	}
}
という形にしてはどうでしょうか?
現状では

コード:

for(全てのブロックについて){
	if(このブロックが左側のブロックなら){
		if(敵の左端とブロックの右端がぶつからないなら){
			敵を右か左に移動;
		}
	}
	if(このブロックが右側のブロックなら){
		if(敵の右端とブロックの左端がぶつからないなら){
			敵を右か左に移動;
		}
	}
}
となっていますが、これは
block1[0][0]にぶつからなければ移動
block1[0][1]にぶつからなければ移動
 ・
 ・
 ・

という事です。
左に移動するとblock1[0][2]にぶつかる状態であっても、block1[0][8]にぶつからなければ左に移動する、というような状態が発生し
移動も最大で残っているブロックの個数回行ってしまっています。

ヨシタケ

Re: ブロックと敵との当たり判定

#3

投稿記事 by ヨシタケ » 13年前

ブロックと敵との当たり判定をfor文の外(for文の前)に出して

コード:

		移動
		if(ene1.x < bar.x - 10){            //敵を右に移動させたい
			if(HitRectAndRect(guardian,block1[i][j].hit) != TRUE)//四角同士の当たり判定(敵とブロック)
				ene1.x = ene1.x + 2;
		}
		if(ene1.x > bar.x + 10){     //敵を左に移動させたい
		            if(HitRectAndRect(guardian,block1[i][j].hit) != TRUE)//四角同士の当たり判定(敵とブロック)
				ene1.x = ene1.x - 2;
		}
とやってみたのですがブロックをすり抜けてしまいます。

Eeel

Re: ブロックと敵との当たり判定

#4

投稿記事 by Eeel » 13年前

for文の外にする事自体は間違っていませんが、for文の外だと

コード:

block1[i][j].hit
の i と j の中身はどうなっているのでしょうか?

また、HitRectAndRectは名前から想像すると、ある二つの Rect 同士(つまりこの場合は、敵とある一つのブロック)がぶつかるかどうかだけを返す関数だと思うのですが、
敵がブロックとぶつかっていないかを判断する場合は

 敵が、全てのブロックの内一つ以上のブロックとぶつかる → 「ぶつかる(ヨシタケさんの例での TRUE )」を返す
 敵が、全てのブロックの内どのブロックともぶつからない → 「ぶつからない」を返す

という風にやる必要があります。

Eeel

Re: ブロックと敵との当たり判定

#5

投稿記事 by Eeel » 13年前

すみません、一つずつ解決していこうと思っていたのですが
小出しにするのもどうかと思い直しました。
今私にわかる範囲で問題になりそうな部分があと二ヶ所あります。

一つ目は詳しい説明は必要無いと思いますが、
衝突判定と同時に、ブロックが消えているか残っているかの判定を行っていない事です。

二つ目です。

コード:

if(関数:ブロックにぶつかるか(敵.x-2) == ぶつからない)
これを同じ意味の別の表現に直すと

コード:

if(移動した先の位置で、ブロックと重なるか == 重ならない)
となります。

ヨシタケさんの

コード:

if(HitRectAndRect(guardian,block1[i][j].hit) != TRUE)
同じように表現し直すと

コード:

if(今の位置で、ブロックと重なるか == 重ならない)
となります。
これだと、判定時にブロックと重なっていないのなら、一度だけブロックと重なるように動く事ができます。
そして、一度ブロックと重なってしまうと、今度は左右どちらにも動けなくなってしまいます。

ヨシタケ

Re: ブロックと敵との当たり判定

#6

投稿記事 by ヨシタケ » 13年前

何度もアドバイスありがとうございます。
試しにi,jの値を決めて

コード:

if(HitRectAndRect(guardian,block1[6][6].hit) == FALSE)
		if(guardian.rb.x < right)
		ene1.x += 2;
というようにしてみたらちゃんとそのブロックの位置でとまるようになりました。
なので、i,jに問題があるのだと思いました。
そこで、判定方法を少し変えて的が左に移動できる位置をleft,右に移動できる位置をrightとし、for文の中に

コード:

				if(block1[i][2].flag == 0){//左から2番目の位置にブロックがあるかどうか
					left = 145;
				}else if(block1[i][1].flag == 0){){//左から1番目の位置にブロックがあるかどうか
					left = 104;
				}else if(block1[i][0].flag == 0){){//左から0番目の位置にブロックがあるかどうか
					left = 63;
				}
				if(block1[i][6].flag == 0){){//左から6番目の位置にブロックがあるかどうか
					right = 270;
				}else if(block1[i][7].flag == 0){){//左から7番目の位置にブロックがあるかどうか
					right = 311;
				}else if(block1[i][8].flag == 0){){//左から8番目の位置にブロックがあるかどうか
					right = 352;
				}
敵移動制御を

コード:

		if(ene1.x +2< bar.x - 10){	//右に移動

			//if(HitRectAndRect(guardian,block1[6][6].hit) == FALSE)
			if(guardian.rb.x < right)
				ene1.x += 2;

			
		}
		if(ene1.x -2> bar.x + 10){
			//if(HitRectAndRect(guardian,block1[6][2].hit) == FALSE)
			if(guardian.lt.x >left)
				ene1.x -=2;

		}
というように追加・変更してみました。(移動制御はfor文の後に移動させました。そうしないとエラーが出てしまうため)一応、ブロックのある位置で止まるようになりました。
しかし、縦3つ並んでるブロックのひとつでも消えると普通にすり抜けてしまいます。

Eeel

Re: ブロックと敵との当たり判定

#7

投稿記事 by Eeel » 13年前

ヨシタケ さんが書きました:というようにしてみたらちゃんとそのブロックの位置でとまるようになりました。
なので、i,jに問題があるのだと思いました。
良いですね。その調子です。

「個々のブロックの衝突判定をまとめたもの」 = 「ブロック全体の衝突判定」
となるわけですが、i,jに問題というのをより厳密に言うとこのまとめる部分に問題がある、となります。
最初は、左側のブロックとの判定が正しくても右側のブロックに当たらなければ左側に動く、という事ができてしまっていました。
次は、一つのブロックとの判定が正しくてもその一つのブロックとしか衝突判定をしない、という動作になってしまっていました。
そして今回は……。

■■
■■■
■■

左側のブロックがこのような形に残っている状況を考えてみてください。
真ん中の列に関しては、2番目のブロックがあるので

コード:

left = 145;
になりますが
上と下の列では、2番目のブロックが無く1番目のブロックがあるので

コード:

left = 63;
になってしまいます。

どこか一列だけを考えれば正しい衝突判定ですが、他の列とまとめてブロック全体の衝突判定を得ようとすると問題が生じています。
今回は全てのブロックについて判定を行っているのですが、その結果の情報を上書きしてしまっているのが原因です。
解決方法は上書きをしないようにする、以外にもたくさんあるので、色々試してみてください。

ここからは蛇足ですが、
ソースを書くときは、モノやコトごとに処理をまとめると作りやすくなります。
見やすくなるだけでなく、考えやすくもなります。

例えば、「ブロックと敵との衝突判定」という一つコトが一つの塊(関数でなくても、すぐ関数にできるような状態)になっていれば
別のステージで別の敵を作る時や、敵の数が増えた時にも簡単に対応する事ができます。
しかし、「ブロックと(ボールと敵)との衝突判定」というように二つのコトが一つの塊になっていると、
もし敵が増えた場合に、敵との衝突判定だけを敵の数だけ繰り返そうとしても必要以上に複雑な処理になってしまいます。

もちろん、塊の分け方は細かければ細かいほど良い、というわけではありません。
どの程度のバランスが良いのかという感覚は、センスと経験で磨いていくしかないのでしょう。
私もまだまだ勉強中です。

今回のプログラムでは考えなくても構いません、。
次回以降、余裕があったらちょっとだけ気にしてみてください。

ヨシタケ

Re: ブロックと敵との当たり判定

#8

投稿記事 by ヨシタケ » 13年前

できればやりたくなかった方法ですが、全然いい方法が思い浮かばなかったのでやりました。

コード:


				//左側の移動できる位置
				if(block1[i][2].flag == 0){
					left = 145;
				}
				else if(block1[i][1].flag == 0){
					if(block1[5][2].flag == 1 && block1[6][2].flag == 1)
						left = 104;
				}
				else if(block1[6][0].flag == 0){
					if(block1[5][1].flag == 1 && block1[6][1].flag == 1)
						left = 63;
				}
				//右側の移動できる位置
				if(block1[i][6].flag == 0){
					right = 270;
				}
				else if(block1[7][7].flag == 0){
					if(block1[5][6].flag == 1 && block1[6][6].flag == 1)
					right = 311;
				}
				else if(block1[i][8].flag == 0 ){
					if(block1[5][7].flag == 1 && block1[6][7].flag == 1)
					right = 352;
				}
今回はブロックが3つなのであまり長くなりませんでしたが、多くなるととても長くなりそうです。
一応、実際やってみた限りでは、問題なさそうでしたが心配なので、大丈夫かどうか聞いてみようと思い、まだ「解決!」はつけませんでした。

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

Re: ブロックと敵との当たり判定

#9

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

比較する条件とか添字とかright ,leftに入れる値とかを構造体配列でテーブルにすると処理はテーブル参照でのループでできるようになります。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

Eeel

Re: ブロックと敵との当たり判定

#10

投稿記事 by Eeel » 13年前

コード:

else if(block1[6][0].flag == 0)

コード:

else if(block1[7][7].flag == 0)
この辺りが問題ですね。

このケースに限れば、思い通りの動作をさせるという意味では、あとはおそらく問題無いでしょう。
ヨシタケ さんが書きました:できればやりたくなかった方法ですが
その感覚は良いと思います。
ブロック数が増えた時も問題ですし、別の場所で設定しているブロックの配置が変わった時も、この部分を書き換える必要があります。
ステージが増えればステージの数だけ当たり判定を行う箇所が増えてしまいます。
もしブロックのサイズ・配置、敵のサイズ・数・移動の仕方(左右だけじゃなく上下にも動いたり)などに影響されない判定が行えるようになると、その後は楽ですよね。

ただ、ここから先どうするかはヨシタケさん次第です。
完成させるというのはモチベーション的にも経験的にも大きなプラスになります。
とりあえず目的の動作をさせられるようになったという事で、完成を目指すのも良いでしょう。
逆にせっかくの機会なので、より良い方法について質問をするのも良いでしょう。
softyaさんは知識も経験も私なんかと比べてずっと素晴らしいものをお持ちですので、
softyaさんに教えてもらうのはとても良い勉強になるだろうと思います。

閉鎖

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