2Dの衝突判定がキッチリと出来ない、、

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

2Dの衝突判定がキッチリと出来ない、、

#1

投稿記事 by たかし2018 » 2ヶ月前

16×16で区切る碁盤上のマップに配置した16×16の障害物に対して
プレイヤーの幅、障害物の幅とが、キッチリと判定してくれません。
特に、障害物が並んでいる場合だと判定がおかしくなります。
移動先の調べ方に抜けがあるのか、半ずらしでは障害物の間に入り込めたりできる場合があります。
多分、初心者にありがちなミスだと思うのですが、、、、
考え方としてなにが足りないのか、どこがおかしいのか、、。
(ソースが長いので今回「PLAYERが下方向に進んでいる時の先の障害物調べる」処理だけ
抜粋させて頂きました。
movespeedでプレイヤーの移動方向を認識し、その方向に障害物があるかを調べる処理です。
マップは二次元配列で、PLAYERの移動先のX、Yを求め、マップ[Y][X]が障害物ブロックか、調べるという感じです。

コード:

//自分の下にあるブロック
if(getPLAYERmovespeedY()>0){//下に移動中

for(int i=0; i<2+(PLAYER横幅()-2)/16; i++){//障害物の境目に当たる

	//移動先の配列のYの添え字を出す
	int y=(int)(PLAYERposY+PLAYER縦幅+getmovespeedY-1)/16;

	int x;//配列Xの添え字を求める
	if(i==0){//自分の画像の左端
			x=(int)(PLAYERposX+1)/16;
}
else if(i==2+(PLAYER横幅-2)/16-1){//画像の右端
		x=(int)(PLAYERposX()+PLAYER横幅()-2)/16;
	}
else{//中間
		x=(int)(PLAYERposX()+(i*16-2))/16;
	}
//移動先のMAP配列確認	
if(マップ[y][x]==障害物 ){
 障害物なら、PLAYERの移動数値修正、、、
}
自分はなにか間違っているとは思うのですが
ご指摘頂けるとありがたいです。

16×16の障害物が単体で配置してある場合は、処理が簡単なんですが
障害物が連続して繋がって並んでいると、なにか変な動きになります。
障害物と障害物の間に一瞬入り込んだり、とかです。

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#2

投稿記事 by usao » 2ヶ月前

オフトピック
どこをミスっているのかがわからないのは,処理を大きい塊で書いているからでは.
やるべき事柄をもっと小さい要素に分解して見てはどうでしょうか.
例えば,

・何もなく移動できた場合の,"PLAYER"の当たり判定(ここではPixel座標系(?)における矩形範囲か)を算出する処理
・Pixel座標系における矩形が与えられたとき,この矩形を包括するのに必要なマップのマスを列挙する処理

といった基本処理を書いて,それぞれをちゃんとテストする.
そうすれば,処理のどこの段階にミスがあるのかを絞り込みやすくなる.

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

Re: 2Dの衝突判定がキッチリと出来ない、、

#3

投稿記事 by Dixq (管理人) » 2ヶ月前

当たり判定の確認をするときは、視覚的に確認できるようにするのも手ですよ。
例えばシューティングを作るとき、レーザーと自機が正しく当たり判定計算できているか確認するとき
https://dixq.net/rp/34.html
こんな風に実際のレーザーの上に当たり判定を描画して確認しました。
当たり判定として計算する部分を四角形で上書きするなどし、視覚的に正しいか確認してみると、
分かりやすいと思いますよ。
意外と±やベクトルの方向が逆に画面右半分全部抜けてるなんてこともあります。

たかし2018

e: 2Dの衝突判定がキッチリと出来ない、、

#4

投稿記事 by たかし2018 » 2ヶ月前

URL見ましたが、弾と自機の衝突判定の仕方は
理解してます。普段は矩形判定でやってます。
今回は、オブジェクト同士の当たり判定の説明じゃ無く、
配列上のブロックに対するオブジェクトの当たり判定する場合の組み立て方を知りたいんです。

■壁、□空き7
□□□□
■□■□
□□□□
 ↑
 ●
例えば、●のplayerが上に進んでいく場合に
自分の座標の上にある配列を割り出し
それが移動出来ない壁かを調べるわけですよね?
で、これが、正面だけの要素を割り出し
壁かを判断するだけなら、
右上や左上の壁にplayerが気づかず、
それらと半分重なっていようとも、playerは
進めてしまいます。
それが困ります。
なので、私は、playerが上に進む場合は、
まず自分の真上の配列を出して壁かを調べ、
そのあと、左上、右上の配列を調べてから
上に進めるか、の様な処理を書いてます。
しかし、それでもやっぱりおかしいです
この考えはよくないですか?
多分よくないから、
半分食い込んだりするんでしょう、、

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#5

投稿記事 by usao » 2ヶ月前

んー,だから,あなたの場合において調べるべき事柄を描画して見ましょうよ.
playerが上に進む場合は云々~の結果として
そのplayerの移動先に関して壁とぶつかるかどうかを判定するために「配列のどこどこの要素を調べることになったのか」という結果に不安があるなら,その結果を可視化してみればどうですか.

おそらく,配列の1要素が表示上は16x16[pixel]の領域に相当するんだろうから,
"player"の当たり判定領域と,調べることになった配列要素群に相当する領域とを描画すれば
「調べるべき配列要素を列挙する処理」がまともに動いているかどうかわかるでしょ.
何なら,「壁に当たる」と判定された場合に,その根拠になった配列要素箇所もわかるように描画すれば
「"player"の当たり判定領域のうちのある箇所に壁があるときだけ失敗しているようだ」とかもわかるかもしれない.

そうやって
・問題が生じている箇所を絞り込む
・アルゴリズムの実装が,自分が意図した通りにできているのかどうかを確認する
といった調査をしましょう
(…という,「個人的意見」というか「ひとつの提案」ですよ. デバッグしないのがトレンドなのかもしれないし)

たかし2018

Re: 2Dの衝突判定がキッチリと出来ない、、

#6

投稿記事 by たかし2018 » 2ヶ月前

>・問題が生じている箇所を絞り込む
>・アルゴリズムの実装が,自分が意図した通りにできているのかどうかを確認する
>といった調査をしましょう

なんか、そのアドバイスって
別に今回の私の問題じゃなくても、
プログラムで悩む誰にでも当てはまる
アドバイスですよね。。

そ~ゆ~言葉が欲しかったわけでは無かったんですけど、、
今回の場合の処理の組み立て方というか、
考え方を手ほどきして欲しかったというか。。

まあ、全部自分で考えろということですよね
ありがとうございました。

たかし2018

Re: 2Dの衝突判定がキッチリと出来ない、、

#7

投稿記事 by たかし2018 » 2ヶ月前

ちなみに、当たり判定を描画して、なんて
やってますから。
やってて、解決しないから相談してるわけで。

私がやりたいことを説明させて頂いた上で、
じゃあ私の記載したソース(考えかた)じゃ
なにが足りないか、どこが間違ってるか、
をご指摘してもらいたい。
それが相談内容です。

難しいことしたい話ではないはずだし、
上級者なら間違ってる部分は
見てわかるはずですし。

相談したら、
おかしい部分を自分で探せ、で済むなら
相談ませんし、
そんなアドバイス、初心者でも言えますから。
URL貼ってここを見ろ、も
誰でもできるアドバイスで、丸投げに感じました。
残念です。

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#8

投稿記事 by usao » 2ヶ月前

>ちなみに、当たり判定を描画して、なんて
>やってますから。
>やってて、解決しないから相談してるわけで。

つまり,その描画結果を観測してみた結果として,

【問題が発生する状況下において
当たり判定と重なる配列要素全てが的確に列挙されていることと,
それらのうちのいくつかが壁であるという妥当な判定結果が生成されていること
までを確認できている】

という主張ですよね.
(要するに,このとき,「"player"は壁に当たっている」という結果が得られている,と.)
であれば

【問題の原因は「当たり判定」の処理ではない】

と結論づけられるのであって,
あたなたの言っている
> 私の記載したソース(考えかた)
をいくら読もうが,おかしい個所を外界の我々がわかるわけがない.
そう思いません???

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#9

投稿記事 by usao » 2ヶ月前

オフトピック
> 処理の組み立て方

その塊コードじゃらちが明かない(から質問してるんでしょ?)なら
要素を細分化して,各処理の信頼性を確保した上で組み立てろ,というのは
「処理の組み立て方」のアドバイスには当たらないのか.

> 丸投げに感じました

これ,丸投げしている本人が言うのか.

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#10

投稿記事 by usao » 2ヶ月前

オフトピック
全く役にも立たない無駄話だが,

> 正面だけの要素を割り出し

うまく動かない場合には
まずこういう枝葉末節の高速化の工夫みたいな要素をやめて,
素直に"player"の当たり判定の形状全体に関して行ってみればどうか,とか思うわけ.

というか,本当に碁盤のマスの個数がたかだか16x16しかないのであれば
調べるべき配列要素を全く絞り込むことなく,
「壁」である全N個( N <= 16x16 )に関して毎度総当たりで調べても,問題がないんじゃ?とか思われる.

以下は本件とも無関係なさらなる無駄話であるが,

まずは「効率はともかくシンプルでわかりやすくて妥当な結果が得られる」方法
から初めて,
そこから徐々に効率の良い方法に差し替えて行くという手順は,
自身が扱いに苦労するかもしれない複雑さに対抗せねばならない際に役立つ(個人の感想です).
常に「まともな結果を生む」コードが手元に存在する状態をキープしつつ,必要ならば部分部分をより効率が良い方法に差し替える.
ある部分がとりあえず妥当に動くならば,その部分の効率化作業は,別の箇所の実装よりも後に回すことだってできる.
とりあえず動くコードが完成した際に動かしてみたらその状態で既に十分な速度が出ていて,後回しにした箇所の効率化なんて特別必要ないのかもしれない.

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

Re: 2Dの衝突判定がキッチリと出来ない、、

#11

投稿記事 by Dixq (管理人) » 2ヶ月前

少ない情報からない頭で考えてみるに、複数の壁があるときにおかしくなるということを考えると、
そもそも当たり判定の判定先が一つなのが奇妙に感じます。
無題.png
無題.png (2.05 KiB) 閲覧数: 988 回
今二つの壁に向かって円いプレイヤーが進んでいたとして、その先に進めるかどうかは少なくとも二つのブロックを調べる必要があるはずです。
比較対象が一つしかないと思っているからおかしいのではないでしょうか。

しかし仕様もよくわからない状態でそのようなコードを見せられても私には明確な回答ができません。
プレイヤーがインベーダーのような動きをするのか、
初代ドラクエのような動きをするのか、
ボンバーマンのような動きをするのかにもよるでしょう。
もしボンバーマンのような動きをさせたいのなら少々難しいコーディングをする必要があります。

それに、デバッグの方法、何か問題があった時にそれを解決する方法を養わないと
今後無限に遭遇するバグがあるたびにトピックを立てなければならなくなります。
それを踏まえた上でアドバイスを受けてデバッグしてみるのは間違っているのでしょうか。

usaoさんがおっしゃる通り、バグに遭遇した時、それがすぐに解決できないのであれば、
「単純化してみる」「可視化してみる」という対応が王道です。
今回の問題が可視化で解決しないのであれば、条件式を最小限にして自分の想定通りの動きになっていることを確認しながら少しずつ処理を追加していくといいです。

・ブロックを1つにしてみる
・ブロックを2つにしてみる
・ブロックをfor文で計算してみる
・ブロックを1次元にしてみる
・二次元配列にしてみる

すでに可視化していると言っていますが、おそらくデバッグ可能な正しい可視化ができていないのでしょう。
例えば今回の問題となっている添え字のx,yがどの領域になるかをリアルタイムで可視化していますか?
必要な可視化ができていればデバッグのしようがあると思います。
仮にブロックの当たり判定先の領域を表示できているとして、
今回の問題が一つのブロックしか調査してないことが原因だとしたら、可視化していれば分かるはずと思います。

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#12

投稿記事 by usao » 2ヶ月前

さて,もう見てないっぽいけども,もうちょっと回答っぽい雰囲気のことを書くならば……

この当たり判定コードで算出される
xの値は連続した値を取ることが期待されるハズです.
(そうじゃないと判定が「歯抜け」になってしまうから)
例えば,

PLAYERposX = 1
PLAYER横幅 = 33

の場合を考えてみます.暗算で.
forループは i={0,1,2} で3回まわるでしょう.

i=0のとき,最初のifの条件に合致するので,xは0です.
i=1のとき,elseブロックの処理により,xは0です.
i=2のとき,真ん中のelse ifの条件を満たし,xは2です.

この暗算が合っていれば,x=1に対応する配列要素はチェックされないことになり,すり抜けが発生するでしょう.
つまり,少なくとも全てのパターンにおいて正常な結果が得られる処理になっていないわけです.
(変数値を視覚化しながら動作させていれば,ある瞬間にこういった変な結果を生じることがわかったはず.)

こういうコードを示された場合,このように「ダメな場合があるっぽい」ことは発見できますけども,
「じゃあ何が間違っているのか?」をずばり言うのは(少なくとも私には)難しいです.
1足したり2引いたりする正確な意図がわからないですし,
各値の座標系や意味の説明もありませんし(幅や高さはpixelなのか? PLAYERposXってのは真ん中?左端?その他? etc)
コード末尾の「移動数値修正」なる処理がfor内に入っていることの影響も全く未知数です.
移動量は16を超えることはない,というような暗黙の制約があるのかもしれませんし,無いのかもしれません.


> 組み立て方

とかいう話に相当するかどうかわかりませんけども,
今回の当たり判定の処理というのは
「移動先の当たり判定矩形の底辺を形成する全ての画素について,その画素が所属するマス(配列要素)を調べる」
を行うことに相当すると思われ,
だったらそれをその通りに書くのが最も手っ取り早い.
素直にfor文で書いたらpixel座標についてのループになる.
「全ての画素」についてループを回すのは明らかに無駄だと思うならば,forのstep量を適切に増やせばよく,
その際に注意すべき事柄は,うっかり最後の座標がチェック対象から外れないようにすることくらいか.

dic
記事: 510
登録日時: 8年前
住所: 宮崎県

Re: 2Dの衝突判定がキッチリと出来ない、、

#13

投稿記事 by dic » 2ヶ月前

読んだ限りでは
□□
P
□□
という時にバグっているとおっしゃっているようですが
(配列[0]と[1]の間にプレイヤーがいるとき)
管理人さんの言われるように、この時判定がおかしいのではないでしょうか。

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#14

投稿記事 by usao » 1ヶ月前

Dixq (管理人) さんが書きました:
2ヶ月前
少ない情報からない頭で考えてみるに、複数の壁があるときにおかしくなるということを考えると、
そもそも当たり判定の判定先が一つなのが奇妙に感じます。
もはや無意味だとは思いまが,一応……
質問者のコードでは, 判定先が一つ ということはないです.
forの奇怪な繰り返し条件により,判定箇所(x)を算出するループが最低2回は回ることが保証されているので,
あとは,この繰り返し内で,iの値毎に算出されるxの値が異なれば,複数の箇所が判定先になる.
 ↓
つまり,このコードで真っ先に疑うところは,
【iによって変動するxの算出具合がおかしい】のだろうということ.
(だから,結果として判定することになった「配列要素」を描画してみろという話なんですけどね.)

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

Re: 2Dの衝突判定がキッチリと出来ない、、

#15

投稿記事 by Dixq (管理人) » 1ヶ月前

あ、確かによく読んだらその通りでしたね。失礼しました。

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#16

投稿記事 by usao » 1ヶ月前

usao さんが書きました:
2ヶ月前
> 組み立て方

とかいう話に相当するかどうかわかりませんけども,
今回の当たり判定の処理というのは
「移動先の当たり判定矩形の底辺を形成する全ての画素について,その画素が所属するマス(配列要素)を調べる」
を行うことに相当すると思われ,
だったらそれをその通りに書くのが最も手っ取り早い.
素直にfor文で書いたらpixel座標についてのループになる.
「全ての画素」についてループを回すのは明らかに無駄だと思うならば,forのstep量を適切に増やせばよく,
その際に注意すべき事柄は,うっかり最後の座標がチェック対象から外れないようにすることくらいか.
さて,(予想通りの無風状態だが)
> 組み立て方
とかいう話に終止符を打とう.

ここまで,
最もシンプルな形から改善していくという手続きについて述べ,その例として,
(1)最も安直な方法:1画素単位で走査
(2)ちょっと効率化:step量を適切(例えば16とか)に増やす
という話を書いた.

ここまでくれば,もう,誰でも
「forの中で毎回 pixel座標→配列index の変換をしているのが無駄すぎる」という考えに行き着く.
列挙されるべき配列indexの値は連続的であるハズなのだから
最初と最後のindexさえわかれば中間のindex群を馬鹿正直に計算する必要はないからだ.
よって,forの主体がpixelであった上記(2)の状態から,forの主体が配列indexである状態へ,自然な形に変換できる.

コード:

//調べるべき配列indexの両端を求む
const int x_Left = PixelToArrayIndex( playerの当たり判定の左端pixel座標 );
const int x_Right = PixelToArrayIndex( playerの当たり判定の右端pixel座標 );
//調べるべき全配列要素について調査
for( int x=x_Left; x<=x_Right; ++x )
{
  //配列indexがxの箇所について判定
}
という形になるだろう.

関数PixelToArrayIndex()は,名前の通り,pixel座標系から配列index座標系への座標変換を行うものだ.
(ここでは単に引数を16で割った結果を返す程度のものになるだろうが,
 処理が単純だからといって,関数化せずに16で割る演算を素で各所に書き散らかすのはやめよう.
 何も良いことはない.)

おわり

たかし2018

Re: 2Dの衝突判定がキッチリと出来ない、、

#17

投稿記事 by たかし2018 » 1ヶ月前

数日開きましたが、確認させていただきました。
#9以降のusaoさんや管理人の回答には有益に感じられる内容がいくつかありました。
最初からソースに対するそれくらいの議論、ないしは建設的な回答が頂ければな、
とも思いましたが、ちゃんと相手して頂いたことにはお礼申し上げます。
ただ、いくつか思うところがありますので言わせて頂きたいと思います。

>碁盤のマスの個数がたかだか16x16しかないのであれば

誤解しています。個数では無く、1マスのサイズを16×16で設定してあり、
それが数百×数千と碁盤上に並んだ(二次元配列で作られた)マップです。
「たかだか」のゲームを作っているわけではありません。
(もちろん、今回のプログラミングミスを検証するなら
小さなマップでも作ってからやるのがいいのでしょうけども)

>プレイヤーがインベーダーのような動きをするのか、
>初代ドラクエのような動きをするのか、
>ボンバーマンのような動きをするのか

初代ドラクエって斜め移動できたかは知りませんが
まあ、よくある斜め移動込みのRPG移動です。
斜めもあるんで、判定も少しは面倒だと思うんですけど。

>幅や高さはpixelなのか? PLAYERposXってのは真ん中?左端?その他?

pixelです。基本posは左上のつもりですコリジョン判定する時には
当り判定を座標を割り出してます。

>forの奇怪な繰り返し条件により,
>判定箇所(x)を算出するループが最低2回は回ることが保証されているので,

ここ、一番気になりました。私のソースを理解してくださっていることは
ありがたいのですが、「奇怪」呼ばわりは解せません。
がんばって考え抜いた処理とは言え、未熟者なのは認めますが
なぜこの繰り返しは奇怪なんでしょうか。
合理的では無いのかもしれませんが、
理論的には正解のひとつではないですか?
要は、自分の進行方向にあるインデックスをすべて見ているわけです。
見ないとすり抜けるだろうと踏んでいるので。
で、それに対するusaoさんの提示される処理は

>列挙されるべき配列indexの値は連続的であるハズなのだから
>最初と最後のindexさえわかれば中間のindex群を馬鹿正直に計算する必要はない
>forの主体がpixelであった上記(2)の状態から,forの主体が配列indexである状態へ,自然な形に変換できる

というものですが、アバウトすぎてわかりませんでした。
配列のインデックスは連続的でしょうけど、そのマスが壁か、
そしてその壁が連続かはわからないことです。どこまで壁かもわかりません。
だからいちいち視野に入るインデックスを調べる処理になってしまいました。
1マス16×16が連続してることをさしているのでしたら。
管理人さんの貼った画像みたく、PLAYERが中途半端な場所にいる場合は
どうされるのでしょうか?
16で割ってインデックスを出しても、片側だけのマスしか出せないはずです。
するとそこだけ見て移動しては、PLAYERの画像はすり抜けが起こるはずです。

「調べるべき配列indexの両端を求む」とありますし、
もしかしたらここではPLAYERの座標では無く、PLAYERの幅も考慮してるのかな、とも思いますが
一番重要な
usao さんが書きました:
1ヶ月前
//配列indexがxの箇所について判定
をusamiさんはぼやかしていらっしゃるんで、わからないです。正直。
ソースを見せるのに抵抗があるのでしたら、文章でも流れ図的なものでもいいんで
具体的な説明お願いできませんか?

あとどうでもいい内容ですが、管理人さん
この掲示板投稿時にメルアド必須なのどーにかなりませんでしょうか?
(メルアド必要ですかね?)

たかし2018

Re: 2Dの衝突判定がキッチリと出来ない、、

#18

投稿記事 by たかし2018 » 1ヶ月前

さっきの投稿で、usaoさんの名前を
最後「usami」と誤って記載してしまいました、
訂正します。失礼しました。

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#19

投稿記事 by usao » 1ヶ月前

usao さんが書きました:
1ヶ月前

コード:

//調べるべき配列indexの両端を求む
const int x_Left = PixelToArrayIndex( playerの当たり判定の左端pixel座標 );
const int x_Right = PixelToArrayIndex( playerの当たり判定の右端pixel座標 );
//調べるべき全配列要素について調査
for( int x=x_Left; x<=x_Right; ++x )
{
  //配列indexがxの箇所について判定
}
この疑似コードにおけるxというのは,
質問者のコードにおいてfor文内で算出しているxに対応している.
for内の
//配列indexがxの箇所について判定
を,もっと明示的に書けば
//配列要素[y][x]が壁であるか否かを判定
//(して,壁だったならば相応の必要な措置を講じる)
ということ.(ここではyは既に求められているものとして)


質問者のコードと対比するならば,
質問者コードでは,forの内部でifによって,算出するxが以下の3種類に分岐しているように見受けられる.
・playerの当たり判定の左端に対応する配列index
・playerの当たり判定の右端に対応する配列index
・そのどちらでもない(中間)
このうち,最初の2つを算出することについては,ループ内で条件分岐した先で行う必要性は全くない.
私の疑似コードではforの外部に出ている.
そして,バグっていると思われる「中間」に関しては,
「壁かどうかを調査すべきxは連続しているハズ」なのだから,わざわざ都度頑張って算出する必要はない.
両端がわかっているのだから,左端から右端までを単純に全て走査するだけでいい.


「奇怪」というのは個人的な感想.
要は,「過度に複雑で,意味が捉えにくく,その形にする必要性もわからない」というような意味合い.
確かに,謎の媒介変数iを導入してそこからxを算出しようが一つの方法ではあるだろうけど,
その不要な複雑さが要因でバグってしまっているように見えるし,その不明瞭さがデバッグを困難にしているとも見える.

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#20

投稿記事 by usao » 1ヶ月前

念のため補足しておくと,
私が書いているのは,
>組み立て方
という側の話題に関する事柄のつもりで,質問者のコードが直面している問題への直接の答えではない.
だから,
「別に質問者の方法はダメだからこうしろ」とか言っているわけじゃない.そこは誤解なきよう.
質問者のゴールは,質問者のコードのデバッグにあるハズ.

オフトピック
> 最初からソースに対するそれくらいの議論、ないしは建設的な回答が頂ければな、

納得できない話かもしれないけれども,
その議論自体が,解決のために不要かもしれない んですよね.
外界の人間がどこまで内容に突っ込まなきゃいけないのか,っていう程度が,外界の人間には最初からはわからない.
「描画してみる」というデバッグ手段さえ講じてみれば,それだけで早々に「自己解決しました!」ってなるような話かもしれないし,そうじゃないかもしれない.
(そして,「これはすぐに自己解決するんちゃうの?」と想定するから,最初のようなアドバイスになる.
 要は,あえて偉そうな言葉を用いて書くならば,
 あなたが最初に示した文面とコードの様子から,あなたはすぐに自己解決できるだろうと見積もっているということ.)

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#21

投稿記事 by usao » 1ヶ月前

> ソースを見せるのに抵抗があるのでしたら、文章でも流れ図的なものでもいいんで
> 具体的な説明お願いできませんか?

具体的…
ざっくりこの程度の話をしているのですが,不足がある感じですか?
(下記コード例は,下方向に移動したときの話に限定せずに,"player"の当たり判定矩形全域に関する形)

コード:

//マップチップのサイズ[pixel]
const int MAP_CHIP_PIXEL_SIZE = 16;

//pixel座標から,マップindex座標への変換
//
//ただし,素の計算結果のindex値が,マップ配列領域外になる場合には,
//有効範囲ぎりぎりの値に修正した結果の値を返す.
//[Args]
//  Pix : pixel座標
//  ArraySize : マップ配列のサイズ
inline int PixelToArrayIndex( int Pix, int ArraySize )
{
    //素の計算結果.
    int RowIndex = Pix/MAP_CHIP_PIXEL_SIZE;
    //配列範囲外チェック
    if( RowIndex<0 )return 0;
    if( RowIndex>=ArraySize )return ArraySize-1;
    return RowIndex;
}

//main
int main(void)
{
    //マップ配列のサイズ(仮)
    //
    //多分,マップデータの配列がこんな感じで存在する
    //const int Map[MAP_H][MAP_W] = { ... };
    const int MAP_H = 8;
    const int MAP_W = 12;

    //...

    //"player"の当たり判定矩形の座標(Pixel座標系) (仮)
        //当たり判定の左上
    const int PlayerLeft = 77;
    const int PlayerTop = 38;
        //当たり判定のサイズ
    const int PlayerWidth = 41;
    const int PlayerHeight = 32;

    //...

    //
    //【話の内容は以下の事柄】
    //
    //"player"の当たり判定を包括する配列indexの範囲を算出
    //この範囲内の配列要素に関して,壁かどうかしらべればよい.
    const int Top = PixelToArrayIndex( PlayerTop, MAP_H );
    const int Bottom = PixelToArrayIndex( PlayerTop + PlayerHeight-1, MAP_H );
    const int Left = PixelToArrayIndex( PlayerLeft, MAP_W );
    const int Right = PixelToArrayIndex( PlayerLeft + PlayerWidth-1, MAP_W );

    //当たり判定処理
    for( int y=Top; y<=Bottom; ++y )
    {
        for( int x=Left; x<=Right; ++x )
        {
            //if( Map[y][x]が壁だったら… )
            //{ 
                //...
            //}
        }
    }

    //...
}
上記コード例における,算出された配列indexの範囲は以下の図.
白い矩形が"player"の当たり判定で,
赤い矩形群が,それに対して算出されたindex{Top,Bottom,Left,Right}による範囲です.
この赤い部分に壁があるかどうか調べることが「当たり判定」だと思っているのですが,違うのかな?
Fig.png
コード例の結果を図示
Fig.png (1.92 KiB) 閲覧数: 632 回

アバター
usao
記事: 1471
登録日時: 5年前

Re: 2Dの衝突判定がキッチリと出来ない、、

#22

投稿記事 by usao » 1ヶ月前

>「PLAYERが下方向に進んでいる時の先の障害物調べる」処理

に限定した場合なら…

playerをある移動量だけ下方向に移動させた場合に
その移動先におけるplayerの当たり判定が図で白矩形で表される範囲であるとき,
図で赤くなっているマスのうちの最下段の4マスに関してのみ,それらが壁であるか否かの判定を行えばよい

…という話.
(この場合なら,Topの算出は不要で,{Bottom,Left,Right}だけ求めればいい)

ISLe
記事: 2625
登録日時: 8年前
連絡を取る:

Re: 2Dの衝突判定がキッチリと出来ない、、

#23

投稿記事 by ISLe » 1ヶ月前

オフトピック
ズレるとめり込むといった現象は、当たり判定に複雑な前提条件を持ち込んだ末の条件漏れが原因で発生することがあります。
質問と一緒に提示されたコードからはそのようなニオイを感じます。


ループではキャラの周辺状況を取得するだけにして、あとから座標の補正をするようにすると、漏れにくくなります。
2D横スクロールアクションゲームの壁判定についての質問
RECT型を使ってのめり込み防止の実装について。

#ブロックひとつひとつに対して逐次補正する方法もあります。
#前者はスーパーマリオっぽく、後者はロックマンっぽい動きになります。


ループを回す範囲については、うちのブログの記事ですが…
【JavaSE】フレームワークのタイルマップ実装
Tilemapクラスのpaintメソッドのコードが、見える範囲だけタイルを回すループになってます。
タイルサイズや描画サイズが可変で、マップの上下左右反転にも対応してます。
これは描画用のコードですが当たり判定にもそのまま応用できます。
(Javaだけど、C++の知識があれば読めるはず)

タイルマップ座標のオフセットを求める
こちらも参考にどうぞ。

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

Re: 2Dの衝突判定がキッチリと出来ない、、

#24

投稿記事 by Dixq (管理人) » 1ヶ月前

ユーザー登録してもらえたらボット対応含めた全項目入力不要になりますよ。

dic
記事: 510
登録日時: 8年前
住所: 宮崎県

Re: 2Dの衝突判定がキッチリと出来ない、、

#25

投稿記事 by dic » 2週間前

コード:

	int x;//配列Xの添え字を求める
	if (i == 0) {//自分の画像の左端
		x = (int)(PLAYERposX + 1) / 16;
	}
	else if (i == 2 + (PLAYER横幅 - 2) / 16 - 1) {//画像の右端
		x = (int)(PLAYERposX() + PLAYER横幅() - 2) / 16;
	}
	else {//中間
		x = (int)(PLAYERposX() + (i * 16 - 2)) / 16;
	}
インデックスを出したいのであれば
/ 割り算ではなくて 剰余 % ではないでしょうか?
やり方で違うので、もう少しソースコードが欲しいですね。


=====追加=====
ISLeさんのおっしゃるオフセットの求め方とだぶりました。
失礼しました。

返信

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