概述
这个方法是我自己想到的,经典的矩形重叠(碰撞)检测,是一段很复杂的逻辑判断,而根据两个矩形中点连线,与两个矩形宽度和高度之和一半的比较,就可以判断两个矩形是否重叠,并且能够计算出重叠向量。
- 通过给两个矩形施加反向的一半重叠向量,可以将两个矩形“推开”。
- 通过用鼠标自由移动的矩形,可以使用给原本静止的矩形施加重叠向量,从而推动它
真实的物理碰撞是要考虑两个物体的质量和速度的,本文和ShapeTest所研究的只是几何上的重叠和重叠部分的向量求取。
基本原理
将两矩形中心进行连线:
- 则横向距离 d w = ∣ c 2 − c 1 ∣ cos θ = ( c 2 − c 1 ) . x d_w = |c2-c1|\cos \theta = (c2-c1).x dw=∣c2−c1∣cosθ=(c2−c1).x,纵向距离 d h = ∣ c 2 − c 1 ∣ sin θ = ( c 2 − c 1 ) . y d_h = |c2-c1|\sin \theta = (c2-c1).y dh=∣c2−c1∣sinθ=(c2−c1).y
- 如果 d w < w 1 + w 2 2 d_w<\frac{w_1+w_2}{2} dw<2w1+w2且 d h < h 1 + h 2 2 d_h<\frac{h_1+h_2}{2} dh<2h1+h2说明两个矩形发生了重叠。
- 重叠的距离: o w = w 1 + w 2 2 − d w o_w=\frac{w_1+w_2}{2} - d_w ow=2w1+w2−dw, o h = h 1 + h 2 2 − d h o_h = \frac{h_1+h_2}{2} - d_h oh=2h1+h2−dh
代码实现
# 获取重叠部分的向量
func get_overlap(rect1:Rect2,rect2:Rect2) -> Vector2:
var overlap:= Vector2()
var c1:Vector2 = rect1.get_center()
var c2:Vector2 = rect2.get_center()
var vec12:Vector2 = c2-c1
var half_size = (rect1.size + rect2.size)/2.0
var dw = abs(vec12.x)
var dh = abs(vec12.y)
if dw < half_size.x and dh < half_size.y:
overlap.x = (half_size.x - dw)
overlap.y = (half_size.y - dh)
return overlap
测试代码
extends Node2D
var rect1 = Rect2(300,300,100,60)
var rect2 = Rect2(400,400,100,60)
func _process(delta: float) -> void:
rect2.position = get_global_mouse_position()
var ove = get_overlap(rect1,rect2)
var d_pos = rect2.position - rect1.position
if ove.x >= ove.y:
rect1.position -= Vector2(0,ove.y) * sign(d_pos.y)
else:
rect1.position -= Vector2(ove.x,0) * sign(d_pos.x)
queue_redraw()
func _draw() -> void:
draw_rect(rect1,Color.AQUAMARINE,false,1)
draw_rect(rect2,Color.AQUAMARINE,false,1)
var c1 = rect1.get_center()
var c2 = rect2.get_center()
效果:
推动按钮
将矩形换为两个按钮控件:
测试代码:
extends Node2D
@onready var button1: Button = $Button1
@onready var button2: Button = $Button2
func _process(delta: float) -> void:
button2.position = get_global_mouse_position()
var ove = get_overlap(button1.get_rect(),button2.get_rect())
var d_pos = button2.position - button1.position
if ove.x >= ove.y:
button1.position -= Vector2(0,ove.y) * sign(d_pos.y)
else:
button1.position -= Vector2(ove.x,0) * sign(d_pos.x)
测试效果:
其实,你可以将按钮替换为任意的控件或容器。