1. はじめに
記事 Godotを使ってBITMAP衝突判定(Scratch3風)のなかで、矩形による「近傍」判定を試行しています。
2つのスプライトがあって、お互いに傾いている場合でも「矩形をつかって近傍判定」をしたいです!という記事でした。
その記事のなかでは、矩形の辺が水平・垂直になるよう、相手の矩形を自分のローカル座標に変換したうえで、頂点4点を囲む矩形を作り直していました。
この方法ではカニ側の矩形(赤い線)の大きさが元の矩形(青い線)よりも大きくなりがちであり、近傍判定が甘くなってしまいます。
そこで、今回は、カニの矩形(青い線)のままで、近傍判定をしてみたいと思います。
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
結論
今回記事にある方法を使えば、矩形A(赤)と矩形B(青)を使って、A,B が衝突しているときに スプライト同士が「近傍」にあると判定できそうですね。


