0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Godot4: 傾いている矩形の衝突判定

0
Last updated at Posted at 2026-01-03

1. はじめに

記事 Godotを使ってBITMAP衝突判定(Scratch3風)のなかで、矩形による「近傍」判定を試行しています。

image.png
【上の青線が画像を囲む矩形です】

2つのスプライトがあって、お互いに傾いている場合でも「矩形をつかって近傍判定」をしたいです!という記事でした。

その記事のなかでは、矩形の辺が水平・垂直になるよう、相手の矩形を自分のローカル座標に変換したうえで、頂点4点を囲む矩形を作り直していました。

image.png

この方法ではカニ側の矩形(赤い線)の大きさが元の矩形(青い線)よりも大きくなりがちであり、近傍判定が甘くなってしまいます。

そこで、今回は、カニの矩形(青い線)のままで、近傍判定をしてみたいと思います。

2. 矩形(Rect2)→座標配列(Array[Vector2])

矩形の4頂点を座標配列にしてみましょう

  • own: 自スプライトノード(Sprite2D)
  • target: 衝突判定をする対象のスプライトノード(Sprite2D)

2.1 相手画像の矩形(Rect2)

    var rect = target.get_rect()
    # rect.position → 矩形の左上の点(Vector2)
    # rect.size → 矩形の横幅(x)、縦幅(y)
    # rect.end →  矩形の右下の点(Vector2)

2.2 座標配列(Array[Vector2])

    var v_arr = [
        rect.position,   # 左上
        rect.position + Vector2(rect.size.x , 0),  # 右上
        rect.position + rect.size,  # 右下
        rect.position + Vector2(0, rect.size.y ),  # 左下
    ]

2.3 相手矩形の座標配列(Array[Vector2])をローカル座標へ

    var own_v_arr:Array[Vector2] = []
    for _pos:Vector2 in v_arr:
        # グローバル座標にする
        var _p_g = target.to_global(_pos)
        # ローカル座標にする
        var _own_p = own.to_local(_p_g)
        own_v_arr.append(_own_p)
    

2.4 自分の矩形(Rect2)を座標配列(Array[Vector2])へ

    var own_rect:Rect2 = own.get_rect()
    var own_arr:Array[Vector2] = [
        own_rect.position,   # 左上
        own_rect.position + Vector2(own_rect.size.x , 0),  # 右上
        own_rect.position + own_rect.size,  # 右下
        own_rect.position + Vector2(0, own_rect.size.y ),  # 左下
    ]

3. 矩形(Array[Vector2])同士の衝突判定

矩形A,矩形Bとし、以下のいずれかが成立する場合にA,Bが衝突しています。

  • 矩形Aの4頂点のいずれかが 矩形B内部に存在する
  • 矩形Bの4頂点のいずれかが 矩形A内部に存在する

3.1 collision_rectangle()

Array[Vector2]で表現した矩形同士が衝突していることを判定します。
指定した点が矩形(Array[Vector2])内に存在する判定をpoint_is_inside()としています。

# rectA: 矩形A
# rectB: 矩形B

func collision_rectangle(rectA:Array[Vector2], rectB:Array[Vector2])->bool:
	for pos in rectA:
		var inside = point_is_inside(pos, rectB)
		if inside:
			return true
	for pos in rectB:
		var inside = point_is_inside(pos, rectA)
		if inside:
			return true
	return false

3.2 任意の点が矩形の中に含まれているかの判定処理(内積を利用)

  • P:任意の点

  • O:矩形の頂点の点

  • A:点Oからみて矩形の隣の頂点

  • B:点Aとは反対方向の隣の頂点

  • OA : 点O→点Aのベクトル

  • OB : 点O→点Bのベクトル

  • OP : 点O→点Pのベクトル

とするとき、点Pが矩形の中にあるときは、すべての頂点において次の条件が成立します。

  • 内積(OA・OP) ≧ 0 and 内積(OB・OP) ≧ 0

この判定方法をGdscriptで実装してみました。

# 指定した点が四角形の中にある場合はTrue
# 指定した点が四角形の辺上、頂点にある場合、true
func point_is_inside(point:Vector2, rect:Array[Vector2])->bool:
	var size:int = rect.size()
    if size != 4 : # 頂点は4個
		return false
	for idx in range(size):
        # 基本点(O)
		var o:Vector2 = rect.get(idx)
		# 隣(前)の点(A)
		var idx_A:int = idx -1
		if idx == 0 :
			idx_A = size -1
		var v_A:Vector2 = rect.get(idx_A)
		# 隣(後)の点(B)
		var idx_B = idx +1
		if idx == size -1:
			idx_B = 0
		var v_B:Vector2 = rect.get(idx_B)
		# 隣(前)の点Aと指定点Pに向いたベクトル同士との内積
		var d1 = (v_A-o).dot((point-o))
		# 隣(後)の点Bと指定点Pに向いたベクトル同士との内積
		var d2 = (v_B-o).dot((point-o))
        # OA と OB は 90°の角度で交わる
		# OAとOPの角度、OBとOP の角度が 90°以下(内積≧0)の場合、
		# 点Pは点Oからみると矩形の内側(辺上を含む)の方向にある。
		# 内積のどちらかがマイナスのとき 指定点(P)は点Oからみて
        # 矩形の外側の方向にある
		if d1 < 0 or d2 < 0:
			return false
	return true

結論

image.png

今回記事にある方法を使えば、矩形A(赤)と矩形B(青)を使って、A,B が衝突しているときに スプライト同士が「近傍」にあると判定できそうですね。

GITHUB

Godot4で動くプロジェクトをGITHUBにあげています

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?