9func get_aabb(body:Dictionary) -> Rect2:
10 var shape = body.get(
"shape", {})
11 var position = body.get(
"position", Vector2.ZERO)
12 var type = str(shape.get(
"type",
"aabb"))
14 var radius = float(shape.get(
"radius", 8.0))
15 return Rect2(position - Vector2(radius, radius), Vector2(radius * 2.0, radius * 2.0))
16 var size = shape.get(
"size", Vector2(16, 16))
17 if size
is Array
and size.size() >= 2:
18 size = Vector2(float(size[0]), float(size[1]))
19 return Rect2(position - size * 0.5, size)
23func broad_phase(bodies:Array, spatial_state = null) -> Array:
25 for i
in range(bodies.size()):
26 for j
in range(i + 1, bodies.size()):
29 if spatial_state != null
and not spatial_state.can_collide(a, b):
31 if get_aabb(a).intersects(get_aabb(b)):
32 pairs.append({
"a": a,
"b": b})
37func narrow_phase(pairs:Array) -> Array:
40 var contact = test_collision(pair.get(
"a", {}), pair.get(
"b", {}))
41 if not contact.is_empty():
42 contacts.append(contact)
47func test_collision(a:Dictionary, b:Dictionary) -> Dictionary:
48 var a_shape = a.get(
"shape", {})
49 var b_shape = b.get(
"shape", {})
50 var a_type = str(a_shape.get(
"type",
"aabb"))
51 var b_type = str(b_shape.get(
"type",
"aabb"))
52 if a_type ==
"circle" and b_type ==
"circle":
53 return _circle_circle(a, b)
54 if a_type ==
"aabb" and b_type ==
"aabb":
55 return _aabb_aabb(a, b)
56 if a_type ==
"circle" and b_type ==
"aabb":
57 return _circle_aabb(a, b)
58 if a_type ==
"aabb" and b_type ==
"circle":
59 var contact = _circle_aabb(b, a)
60 if not contact.is_empty():
61 contact[
"normal"] = -contact.get(
"normal", Vector2.RIGHT)
68func _circle_circle(a:Dictionary, b:Dictionary) -> Dictionary:
69 var delta = b.get(
"position", Vector2.ZERO) - a.get(
"position", Vector2.ZERO)
70 var distance = delta.length()
71 var radius_sum = float(a.get(
"shape", {}).get(
"radius", 8.0)) + float(b.get(
"shape", {}).get(
"radius", 8.0))
72 if distance >= radius_sum:
74 var normal = Vector2.RIGHT
if distance <= 0.0001
else delta / distance
75 return {
"a": a,
"b": b,
"normal": normal,
"penetration": radius_sum - distance,
"point": a.get(
"position", Vector2.ZERO) + normal * float(a.get(
"shape", {}).get(
"radius", 8.0))}
78func _aabb_aabb(a:Dictionary, b:Dictionary) -> Dictionary:
79 var a_rect = get_aabb(a)
80 var b_rect = get_aabb(b)
81 if not a_rect.intersects(b_rect):
83 var a_center = a_rect.get_center()
84 var b_center = b_rect.get_center()
85 var overlap_x = min(a_rect.end.x, b_rect.end.x) - max(a_rect.position.x, b_rect.position.x)
86 var overlap_y = min(a_rect.end.y, b_rect.end.y) - max(a_rect.position.y, b_rect.position.y)
87 if overlap_x < overlap_y:
88 var normal_x = 1.0
if b_center.x >= a_center.x
else -1.0
89 return {
"a": a,
"b": b,
"normal": Vector2(normal_x, 0),
"penetration": overlap_x,
"point": Vector2((a_center.x + b_center.x) * 0.5, (a_center.y + b_center.y) * 0.5)}
90 var normal_y = 1.0
if b_center.y >= a_center.y
else -1.0
91 return {
"a": a,
"b": b,
"normal": Vector2(0, normal_y),
"penetration": overlap_y,
"point": Vector2((a_center.x + b_center.x) * 0.5, (a_center.y + b_center.y) * 0.5)}
94func _circle_aabb(circle:Dictionary, box:Dictionary) -> Dictionary:
95 var circle_position = circle.get(
"position", Vector2.ZERO)
96 var radius = float(circle.get(
"shape", {}).get(
"radius", 8.0))
97 var rect = get_aabb(box)
98 var closest = Vector2(
99 clamp(circle_position.x, rect.position.x, rect.end.x),
100 clamp(circle_position.y, rect.position.y, rect.end.y)
102 var delta = closest - circle_position
103 var distance = delta.length()
104 if distance >= radius:
106 var normal = Vector2.RIGHT
if distance <= 0.0001
else delta / distance
107 return {
"a": circle,
"b": box,
"normal": normal,
"penetration": radius - distance,
"point": closest}