正方形と円の当たり判定(補正付き)

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
夢幻ノ月夜
記事: 143
登録日時: 9年前
住所: Stens;Gate世界線

正方形と円の当たり判定(補正付き)

#1

投稿記事 by 夢幻ノ月夜 » 3年前

2D見下ろし型ゲームで円形の当たり判定を持った物体と正方形の当たり判定を持った壁の当たり判定をしたいです。
ただ判定をするだけなら龍神録のレーザーと同じ要領でやればよいのですが、今回は座標の補正と反射ベクトルの計算もしたいので行き詰ってしまった次第です。

現在パラメータとして持ってるのは
物体:中心座標 2次元ベクトル、速度 2次元ベクトル、半径 実数型、反発係数 実数型
壁:中心座標 2次元ベクトル、一辺の長さ 実数型、反発係数、実数型
以上です
ベクトルはもちろん実数型2つでx要素・y要素を保持していて正規化・内外積を含む基本的な操作はできます。
反発係数は物体のものと壁のものを掛け合わせて反発係数とする予定です。

以上のパラメータを用いて想定通りの当たり判定と座標・速度補正は可能でしょうか。
可能でしたらどのような方法で実装すれば良いでしょうか。
ネット上で探しても、円と正方形(長方形)の判定の仕方に関する情報があまりなく、反射や座標補正のことも含めると情報量は絶望的でした。
毎回ゲーム作ろうとするたびに壁にぶち当たる

アバター
あたっしゅ
記事: 663
登録日時: 13年前
住所: 東京23区
連絡を取る:

Re: 正方形と円の当たり判定(補正付き)

#2

投稿記事 by あたっしゅ » 3年前

 自分で実装するなら、正方形を、適当な大きさの円で近似するのが吉。
 そうでなければ、適当な物理演算エンジン
https://ja.wikipedia.org/wiki/%E7%89%A9 ... 8%E3%83%B3
を使用の事。
VTuber:
東上☆海美☆(とうじょう・うみみ)
http://atassyu.php.xdomain.jp/vtuber/index.html
レスがついていないものを優先して、レスするみみ。時々、見当外れなレスしみみ。

中の人:
手提鞄あたッしュ、[MrAtassyu] 手提鞄屋魚有店
http://ameblo.jp/mratassyu/
Pixiv: 666303
Windows, Mac, Linux, Haiku, Raspbery Pi, Jetson Nano, 電子ブロック 持ち。

アバター
usao
記事: 1887
登録日時: 11年前

Re: 正方形と円の当たり判定(補正付き)

#3

投稿記事 by usao » 3年前

単純には

(1)円が正方形の辺に触れるパターン
(2)正方形の角が円に触れるパターン

に分けて考えればよいのでは.
で,どちらだろうが,物体の速度を「ある方向Nに対して反転する」のがシンプルな処理であろう.

(1)の場合のNとは,触れた辺の法線方向(辺に直交して,正方形の外側に向く方向)であろう.これは単純明快.

(2)の場合は,至極単純には,接触点から円の中心に向かう方向をNとして用いることが考えられるが,
そのような挙動で良いのかどうかはアプリケーション次第なので何とも言えない.

なお,「物体の速度のN方向成分とNとの内積が正である場合には速度反転処理をしない」といったifを仕込んでおくと良いかもしれない.

アバター
usao
記事: 1887
登録日時: 11年前

Re: 正方形と円の当たり判定(補正付き)

#4

投稿記事 by usao » 3年前

上記は反発係数を用いて速度の法線方向(N)成分のみを単純に反射する話.

座標側の補正に関しては,あまりにも(見た目に不自然なほどに)大きくめり込むのでもないならそのまま放っておいても良いのではないかな?とか.
どうしても座標もいじくりたいならば,座標更新処理を「接した時刻」で2分すればよかろう.

なお,
接方向(Nと垂直な方向)成分も扱いたい(:摩擦を表現したいとかいう)場合には,適当に速度の接方向成分もいじくればよいのではないかと.
単純には s (0.0< s <1.0) 倍してやるとか.
s の値を壁側のパラメータにでもすれば,摩擦が強い/弱い 壁みたいなのを表現できるかもね.

アバター
夢幻ノ月夜
記事: 143
登録日時: 9年前
住所: Stens;Gate世界線

Re: 正方形と円の当たり判定(補正付き)

#5

投稿記事 by 夢幻ノ月夜 » 3年前

現在、あたっしゅさんの助言を踏まえて円を内接する正方形として扱い判定を取る方針でプログラムを組んでいます(辺を考えた方が反射の方向は簡単に出そうなので)。
usaoさんの助言を踏まえて反射の方向は考えてみようと思っています。
想定通りの挙動になったら具体的な方針を書いて解決にしたいと思います。
毎回ゲーム作ろうとするたびに壁にぶち当たる

アバター
usao
記事: 1887
登録日時: 11年前

Re: 正方形と円の当たり判定(補正付き)

#6

投稿記事 by usao » 3年前

> 円を内接する正方形として扱い判定を取る

というのは「四角形 vs 四角形 として処理する」ということですか?

コード:

■
 ■
みたいな角同士が触れそうな位置関係付近で,
・「上下」に接したと判定される場合
・「左右」に接したと判定される場合
で挙動が不連続にならないようにしないと不自然に見えるかもしれませんね.

アバター
夢幻ノ月夜
記事: 143
登録日時: 9年前
住所: Stens;Gate世界線

Re: 正方形と円の当たり判定(補正付き)

#7

投稿記事 by 夢幻ノ月夜 » 3年前

先ほど想定通りの挙動が得られたので少し詳細に書いて解決にしたいと思います。
大枠としてこのサイトを参考にしました(http://monooki.ldblog.jp/archives/35959913.html)。
ですが、本ゲームには速度に加えて加速度もあったため、次のフレームの位置を予測するのに現在座標+速度+加速度を採用しました。これで自機のような壁にぶつかってからも力をかけ続けるような物体を壁で制止することが出来ました。
そして、前は「円を内接する正方形に近似する」と言いましたが、ゲーム性の観点から「円を外接する正方形に近似する」方針にしました。
次に反射ですが、速度ベクトルがもともと縦方向と横方向で保存されていたため、正方形の上または下の辺に当たったかと右または左の辺に当たったかとでそれぞれ対応する要素のみを反転させました。
最後に、ソースコードの該当する部分のみを貼っておきます。

コード:

void CMover::onWall(CVector WallPosition, CVector WallSize, double WallReflectionCF)
{
	CVector nextPosition = Position + Velocity + Acceleration*Constant::perFrame;
	double max = (Position.x + Size) - (WallPosition.x - WallSize.x / 2), may = (Position.y + Size) - (WallPosition.y - WallSize.y / 2);
	double nax = (WallPosition.x + WallSize.x / 2) - (Position.x - Size), nay = (WallPosition.y + WallSize.y / 2) - (Position.y - Size);
	double _max = (nextPosition.x + Size) - (WallPosition.x - WallSize.x / 2), _may = (nextPosition.y + Size) - (WallPosition.y - WallSize.y / 2);
	double _nax = (WallPosition.x + WallSize.x / 2) - (nextPosition.x - Size), _nay = (WallPosition.y + WallSize.y / 2) - (nextPosition.y - Size);
	bool U = false, D = false, R = false, L = false;

	//カド同士の判定
	if (0 >= max && 0 >= may && _max > 0 && _may > 0) {
		if (_max >= _may) {
			//下にある
			D = true;
		}
		else {
			//右にある
			R = true;
		}
	}
	if (0 >= nax && 0 >= may && _nax > 0 && _may > 0) {
		if (_nax >= _may) {
			//下にある
			D = true;
		}
		else {
			//左にある
			L = true;
		}
	}
	if (0 >= max && 0 >= nay && _max > 0 && _nay > 0) {
		if (_max >= _nay) {
			//上にある
			U = true;
		}
		else {
			//右にある
			R = true;
		}
	}
	if (0 >= nax && 0 >= nay && _nax > 0 && _nay > 0) {
		if (_nax >= _nay) {
			//上にある
			U = true;
		}
		else {
			//左にある
			L = true;
		}
	}

	//上下左右の判定
	if (may > 0 && nay > 0) {
		if (_nax > 0 && (Position.x - Size) > (WallPosition.x - WallSize.x / 2)) {
			//左にある
			L = true;
		}
		if (_max > 0 && (WallPosition.x + WallSize.x / 2) > (Position.x + Size)) {
			//右にある
			R = true;
		}
	}
	if (max > 0 && nax > 0) {
		if (_nay > 0 && (Position.y - Size) > (WallPosition.y - WallSize.y / 2)) {
			//上にある
			U = true;
		}
		if (_may > 0 && (WallPosition.y + WallSize.y / 2) > (Position.y + Size)) {
			//下にある
			D = true;
		}
	}
	if (U) {
		Position.y = WallPosition.y + WallSize.y / 2 + Size;
		Velocity.y *= -ReflectCF * WallReflectionCF;
		if(Acceleration.y < 0)Acceleration.y = 0;
	}
	if (D) {
		Position.y = WallPosition.y - WallSize.y / 2 - Size;
		Velocity.y *= -ReflectCF * WallReflectionCF;
		if (Acceleration.y > 0)Acceleration.y = 0;
	}
	if (R) {
		Position.x = WallPosition.x - WallSize.x / 2 - Size;
		Velocity.x *= -ReflectCF * WallReflectionCF;
		if (Acceleration.x > 0)Acceleration.x = 0;
	}
	if (L) {
		Position.x = WallPosition.x + WallSize.x / 2 + Size;
		Velocity.x *= -ReflectCF * WallReflectionCF;
		if (Acceleration.x < 0)Acceleration.x = 0;
	}
}
注:ReflectionCFは物体自身の反発係数、WallReflectionCFは壁の反発係数です。壁は重心をPositionとしています。物体のSizeは半径です。perFrameは1/60で加速度をフレームで割って分かりやすくしています(自分にとって)。

最後に、初歩的な質問で申し訳ないのですが、以前まであった「解決ボタン」は廃止されたのですか?
見つからなかったので、私の見落としでしたら教えてください。
毎回ゲーム作ろうとするたびに壁にぶち当たる

返信

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