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

news/2024/10/3 17:14:43 标签: godot, gdscript, 图形, 重叠检测, 碰撞

概述

最近积累了一些图形重叠检测,以及求图形的轴对齐包围盒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
	)


http://www.niftyadmin.cn/n/5688740.html

相关文章

OPENCV判断图像中目标物位置及多目标物聚类

文章目录 在最近的项目中&#xff0c;又碰到一个有意思的问题需要通过图像算法来解决。就是显微拍摄的到的医疗图像中&#xff0c;有时候目标物比较偏&#xff0c;也就是在图像的比较偏的位置&#xff0c;需要通过移动样本&#xff0c;将目标物置于视野正中央&#xff0c;然后再…

DolphinScheduler 资源中心无法上传大文件

服务&#xff1a;dolphinscheduler 版本&#xff1a;v3.16 问题描述&#xff1a;资源中心-文件管理中使用文件上传是出现中断或上传失败 排除思路&#xff1a; 测试小文件或其他类型文件时是否正常&#xff1b;F12查看接口调用成功以及失败时的对比&#xff0c;发现接口调用…

几个十六进制位表示一个字节

在计算机中一个二进制位表示一个bite位&#xff0c;bite就是二进制中最小的一位。那么什么是一字节呢&#xff1f;一个字节就是8个bite位 二进制位 顾名思义&#xff0c;二进制位就只能用1和0进行表示&#xff0c;假如我要存放数字2&#xff0c;并用二进制位进行表示&#xff…

简单介绍Wiki和历史

Wiki 是一种基于网络的协作工具&#xff0c;它允许多个用户创建、编辑和分享信息。Wiki 的特点是页面内容可以由用户自由修改&#xff0c;并且这些修改会立即生效&#xff0c;促进了集体协作和知识共享。Wiki 的本质是一种内容管理系统&#xff0c;支持版本控制&#xff0c;使得…

Kotlin 处理字符串和正则表达式(二十一)

导读大纲 1.1 处理字符串和正则表达式1.1.1 分割字符串1.1.2 正则表达式和三引号字符串1.1.3 多行三引号字符串IntelliJ IDEA 和 Android Studio 中三重引号字符串内部的语法高亮显示 1.1 处理字符串和正则表达式 Kotlin 字符串与 Java 字符串完全相同 可以将 Kotlin 代码中创建…

Microsoft 解釋修改後的 Recall 將如何保障隱私與安全

Microsoft 為 Copilot AI 電腦推出的 Recall 功能&#xff0c;本意是藉由定期進行螢幕截圖&#xff0c;並分析上面的資訊&#xff0c;來協助用戶找到查看過的網頁、工作過的文件等各種資訊。然而雖然立意很好&#xff0c;但 Microsoft 推出之初似乎完全沒有考慮到用戶的隱私安全…

RTSP作为客户端 推流 拉流的过程分析

之前写过一个 rtsp server 作为服务端的简单demo 这次分析下 rtsp作为客户端 推流和拉流时候的过 A.作为客户端拉流 TCP方式 1.Client发送OPTIONS方法 Server回应告诉支持的方法 2.Client发送DESCRIPE方法 这里是从海康摄像机拉流并且设置了用户名密码 Server回复未认证 3.客…

ISA-95制造业中企业和控制系统的集成的国际标准-(5)

ISA-95 文章目录 ISA-95ISA-95与工业互联网一、工业互联网在哪里&#xff1f;二、维护自动化金字塔 ISA-95与工业互联网 ISA95作为指导性原则&#xff0c;自动化的阶段构建了以人和业务流程为中心的生产组织方式&#xff0c;极大的提高了生产的效率和灵活性&#xff0c;也满足…