自学内容网 自学内容网

【Godot4.3】图形碰撞相关函数库ShapeTests

概述

最近积累了一些图形重叠检测,以及求图形的轴对齐包围盒Rect2,还有求Rect2的外接圆等函数。感觉可以作为一个单独的函数库,提供日常的使用,所以汇总成了ShapeTests

注意:函数名和写法可能会不断改进。

代码

# ==============================================
# ShapeTests
# 一个用于求取基础图形轴对齐包围盒、外接圆以及重叠检测的函数库
# 巽星石 v4.3.stable.steam [77dcf97d8]
# 202410215:02:18
# 202410221:52:23
# ==============================================
class_name ShapeTests
# =======================Rect2 =======================
# 获取点集的轴对齐包围盒(Rect2static func get_points_rect2(points:PackedVector2Array) -> Rect2:
var rect = Rect2()
var xs = Array(points).map(func(num):return num.x)
var ys = Array(points).map(func(num):return num.y)
rect.position = Vector2(xs.min(),ys.min())
rect.end = Vector2(xs.max(),ys.max())
return rect

# 获取圆的Rect2
static func get_circle_rect2(center:Vector2,radius:float) -> Rect2:
var rect = Rect2()
rect.position = center - Vector2.ONE * radius
rect.size = Vector2.ONE * radius * 2
return rect
# =======================Rect2的外接圆 =======================
class Circle:
var center:Vector2
var radius:float

# 返回 Rect2 的外接圆
static func get_rect2_out_circle(rect2:Rect2) -> Circle:
var c = Circle.new()
c.center = rect2.get_center()
c.radius = (rect2.get_center() - rect2.position).length()
return c

# 返回 points 的外接圆
static func get_points_out_circle(points:PackedVector2Array) -> Circle:
var rect:Rect2  = get_points_rect2(points)
return get_rect2_out_circle(rect)

# ======================= 判断图形是否完全在Rect2=======================
# 判断圆是否完全在 rect2 内
static func is_circle_in_rect2(center:Vector2,radius:float,rect2:Rect2) -> bool:
var rect1 = get_circle_rect2(center,radius)
return is_rect_in_rect2(rect1,rect2)

# 判断 rect1 是否完全在 rect2 内
static func is_rect_in_rect2(rect1:Rect2,rect2:Rect2) -> bool:
return rect2.has_point(rect1.position) and rect2.has_point(rect1.end)

# ======================= 图形间的重叠检测 =======================

# 判断两个圆形是否重叠
static func is_circle_circle_overlap(c1:Vector2,r1:float,c2:Vector2,r2:float):
var dis = c1.distance_to(c2)  # 距离
return dis <= r1 + r2 

# 获取两个圆形重叠部分的向量
static func get_circle_circle_overlap(c1:Vector2,r1:float,c2:Vector2,r2:float):
var dis = c1.distance_to(c2)  # 距离
var dir = c1.direction_to(c2) # 方向
var min_dis = r1 + r2 
var over_dis := 0.0   # 重叠部分的距离
if dis < min_dis:
over_dis = min_dis - dis
return dir * over_dis

# 返回矩形四个角点
static func get_rect_points(rect:Rect2) -> PackedVector2Array:
var pots:PackedVector2Array
var pos = rect.position;var end = rect.end;var size = rect.size
pots = [pos,pos + Vector2(size.x,0),end,end - Vector2(size.x,0)]
return pots

# 判断rect2和rect1是否有重叠
static func is_rect_rect_overlap(rect1:Rect2,rect2:Rect2) -> bool:
var points = Array(get_rect_points(rect2))
# 矩形1的任何一个点在矩形2var bol = points.any(func(p):
return rect1.has_point(p)
)
return bol

# 返回rect2和rect1是重叠的点集合
static func get_rect_rect_overlap_points(rect1:Rect2,rect2:Rect2) -> PackedVector2Array:
var pots:PackedVector2Array
var points = Array(get_rect_points(rect2))
# 矩形1的任何一个点在矩形2for p in points:
if rect1.has_point(p):
pots.append(p)
return pots

# 获取Rect24分矩形集合
static func get_rect_4_parts(rect:Rect2) -> Array[Rect2]:
var rects:Array[Rect2]
var c = rect.get_center()
var half_size = rect.size/2.0
var r1 = Rect2(rect.position,half_size)
rects.append(r1)
rects.append(Transform2D(0,Vector2(half_size.x,0)) * r1)
rects.append(Transform2D(0,half_size) * r1)
rects.append(Transform2D(0,Vector2(0,half_size.y)) * r1)
return rects

# 获取两个矩形重叠部分的向量
static func get_rect_rect_overlap(rect1:Rect2,rect2:Rect2) -> PackedVector2Array:
var c1 = rect1.get_center()
var c2 = rect2.get_center()
var vec12 = c2 - c1


var half_size = (rect1.size + rect2.size)/2.0


var ove_points = get_rect_rect_overlap_points(rect1,rect2)  # rect2进入rect1的点集合

var points2 = Array(get_rect_points(rect2))
var points1 = Array(get_rect_points(rect1))
var len = 0
var vec = Vector2()           # 方向
var pos = Vector2()           # 位置

var sub_rects = get_rect_4_parts(rect1) # 将rect1划分为四个象限

match ove_points.size():
4:  # rect2完全进入rect1
if sub_rects[0].has_point(c2):
vec = points1[0] - points2[2]
pos = points2[2]
if sub_rects[1].has_point(c2):
vec = points1[1] - points2[3]
pos = points2[3]
if sub_rects[2].has_point(c2):
vec = points1[2] - points2[0]
pos = points2[0]
if sub_rects[3].has_point(c2):
vec = points1[3] - points2[1]
pos = points2[1]
2:  # rect2一侧进入rect1
var dw = half_size.x - abs(vec12.x)
var dh = half_size.y - abs(vec12.y)

if ove_points == PackedVector2Array([points2[0],points2[1]]):
vec = Vector2.DOWN * dh
pos = points2[0].lerp(points2[1],0.5)
if ove_points == PackedVector2Array([points2[2],points2[3]]):
vec = Vector2.UP * dh
pos = points2[2].lerp(points2[3],0.5)
if ove_points == PackedVector2Array([points2[0],points2[3]]):
vec = Vector2.RIGHT * dw
pos = points2[0].lerp(points2[3],0.5)
if ove_points == PackedVector2Array([points2[1],points2[2]]):
vec = Vector2.LEFT * dw
pos = points2[1].lerp(points2[2],0.5)
1: # rect2仅一个角进入rect1
if ove_points == PackedVector2Array([points2[3]]):
vec = points1[1] - points2[3]
pos = points2[3]
if ove_points == PackedVector2Array([points2[2]]):
vec = points1[0] - points2[2]
pos = points2[2]
if ove_points == PackedVector2Array([points2[1]]):
vec = points1[3] - points2[1]
pos = points2[1]
if ove_points == PackedVector2Array([points2[0]]):
vec = points1[2] - points2[0]
pos = points2[0]
return PackedVector2Array([pos,pos + vec]) 

使用测试

获取圆的Rect2

extends Node2D

var c = Vector2(300,300)  # 圆心
var r = 100               # 半径

func _draw() -> void:
var rect = ShapeTests.get_circle_rect2(c,r) # 获取圆的Rect2
draw_rect(rect,Color.AQUAMARINE,false,1)    # 绘制圆的Rect2
draw_circle(c,r,Color.ORANGE_RED,false,1)# 绘制圆

求点集的Rect2

extends Node2D

var points = Points2D.Vec2Arr("100,200 300,400 200,100")  # 定义点集

func _draw() -> void:
var rect = ShapeTests.get_points_rect2(points)    # 求点集的Rect2
draw_rect(rect,Color.AQUAMARINE,false,1)          # 绘制Rect2

draw_polyline(points,Color.YELLOW_GREEN,1)  # 绘制折线
    # 绘制顶点
for p in points:
draw_circle(p,3,Color.ORANGE_RED)

求Rect2的外接圆

extends Node2D

var rect = Rect2(100,100,300,200)  # 定义Rect2

func _draw() -> void:
var c = ShapeTests.get_rect2_out_circle(rect)            # 求Rect2的外接圆
draw_circle(c.center,c.radius,Color.ORANGE_RED,false,1)  # 绘制外接圆
draw_rect(rect,Color.AQUAMARINE,false,1)                 # 绘制Rect2

求任意点集的外接圆

extends Node2D

var points = Points2D.Vec2Arr("100,200 300,400 200,100")

func _draw() -> void:
var c = ShapeTests.get_points_out_circle(points)
draw_circle(c.center,c.radius,Color.ORANGE_RED,false,1)
draw_polyline(points,Color.YELLOW_GREEN,1)
for p in points:
draw_circle(p,3,Color.ORANGE_RED)

可以看到,实际上是先求点集的Rect2然后求Rect2的外接圆,这里并不太精确,是因为它是基于Rect2的,但是已经满足无论如何旋转,所有点都始终在外接圆内的要求。

判断圆是否在矩形内

extends Node2D

var rect = Rect2(100,100,900,400)  # 定义边界矩形
var c = Vector2(300,300)  # 圆心
var r = 100               # 半径


func _process(delta: float) -> void:
c = get_global_mouse_position() # 用鼠标位置更新圆的位置
queue_redraw()

func _draw() -> void:
draw_rect(rect,Color.AQUAMARINE,false,1)      # 绘制边界矩形
if ShapeTests.is_circle_in_rect2(c,r,rect):
draw_circle(c,r,Color.ORANGE_RED,false,1)  # 绘制圆
else:
draw_circle(c,r,Color.ORANGE_RED,true)  # 绘制圆

判断矩形是否在矩形内

extends Node2D

var rect = Rect2(100,100,900,400)  # 定义边界矩形
var rect2 = Rect2(200,200,100,80)  # 小矩形

func _process(delta: float) -> void:
# 用鼠标位置更新矩形的位置
rect2.position = get_global_mouse_position() - rect2.size/2.0
queue_redraw()

func _draw() -> void:
draw_rect(rect,Color.AQUAMARINE,false,1)       # 绘制边界矩形
if ShapeTests.is_rect_in_rect2(rect2,rect):
draw_rect(rect2,Color.ORANGE_RED,false,1)  # 绘制非填充矩形
else:
draw_rect(rect2,Color.ORANGE_RED,true)     # 绘制填充矩形

注意这里是判断小矩形是否完完全全在大矩形内:

圆与圆的重叠向量

extends Node2D

var c1 = Vector2(300,300)  # 圆心
var r1 = 100               # 半径

var c2 = Vector2(300,300)  # 圆心
var r2 = 60               # 半径

# 箭头
var arrow:PackedVector2Array = [
Vector2.LEFT.rotated(deg_to_rad(-30)) * 5,
Vector2(),
Vector2.LEFT.rotated(deg_to_rad(30)) * 5,
]

func _process(delta: float) -> void:
# 用鼠标位置圆2的圆心
c2 = get_global_mouse_position()
queue_redraw()

func _draw() -> void:
var overlap = ShapeTests.get_circle_circle_overlap(c1,r1,c2,r2)


var a = c1.distance_to(c2) - r2

draw_circle(c1,r1,Color.YELLOW_GREEN,false,1)  # 绘制圆1
draw_circle(c2,r2,Color.YELLOW_GREEN,false,1)  # 绘制圆2

var p1 = c1.move_toward(c2,a)
draw_line(p1,p1+overlap,Color.ORANGE_RED,1)    # 绘制重叠向量
draw_polyline( # 绘制箭头
Transform2D(c1.direction_to(c2).angle(),p1+overlap) * arrow,
Color.ORANGE_RED,1
)

重叠向量是我自己发明的一个词,是能够表现一个图形与另一个图形重叠程度的向量。可以使用反向重叠向量移动被动物体,或者使用重叠向量将移动物体排出被动物体。

矩形与矩形的重叠检测

判断一个举行与另一个矩形是否重合

矩形A的4个点,有任意一个点在另一个矩形B中,就认为矩形A与矩形B重叠。

# 判断rect2和rect1是否有重叠
static func is_rect_rect_overlap(rect1:Rect2,rect2:Rect2) -> bool:
var pos = rect2.position
var end = rect2.end
var size = rect2.size
var points = [
pos,pos + Vector2(size.x,0),
end,end - Vector2(size.x,0)
]
# 矩形1的任何一个点在矩形2var bol = points.any(func(p):
return rect1.has_point(p)
)
return bol

测试:

extends Node2D

var rect1 = Rect2(100,100,400,350)
var rect2 = Rect2(200,200,100,80)

# 箭头
var arrow:PackedVector2Array = [
Vector2.LEFT.rotated(deg_to_rad(-30)) * 5,
Vector2(),
Vector2.LEFT.rotated(deg_to_rad(30)) * 5,
]

func _process(delta: float) -> void:
# 用鼠标位置控制Rect2的位置
rect2.position = get_global_mouse_position() - rect2.size/2.0
queue_redraw()

func _draw() -> void:
var overlap = ShapeTests.get_rect_rect_overlap(rect1,rect2)
var c1 = rect1.get_center()
var c2 = rect2.get_center()


for rect in ShapeTests.get_rect_4_parts(rect1):
draw_rect(rect,Color.YELLOW_GREEN,false,1)  # 绘制矩形1

#draw_rect(rect1,Color.YELLOW_GREEN,false,1)  # 绘制矩形1
draw_rect(rect2,Color.YELLOW_GREEN,false,1)  # 绘制矩形2

draw_line(overlap[0],overlap[1],Color.ORANGE_RED,1)    # 绘制重叠向量
draw_polyline( # 绘制箭头
Transform2D(overlap[0].direction_to(overlap[1]).angle(),overlap[1]) * arrow,
Color.ORANGE_RED,1
)


原文地址:https://blog.csdn.net/graypigen1990/article/details/142690579

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!