RapidGameFramework
Reusable Godot managers for data-driven small games
Loading...
Searching...
No Matches
collision_pipeline.gd
Go to the documentation of this file.
1extends RefCounted
2
3
6
7
8
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"))
13 if type == "circle":
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)
20
21
22
23func broad_phase(bodies:Array, spatial_state = null) -> Array:
24 var pairs = []
25 for i in range(bodies.size()):
26 for j in range(i + 1, bodies.size()):
27 var a = bodies[i]
28 var b = bodies[j]
29 if spatial_state != null and not spatial_state.can_collide(a, b):
30 continue
31 if get_aabb(a).intersects(get_aabb(b)):
32 pairs.append({"a": a, "b": b})
33 return pairs
34
35
36
37func narrow_phase(pairs:Array) -> Array:
38 var contacts = []
39 for pair in pairs:
40 var contact = test_collision(pair.get("a", {}), pair.get("b", {}))
41 if not contact.is_empty():
42 contacts.append(contact)
43 return contacts
44
45
46
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)
62 contact["a"] = a
63 contact["b"] = b
64 return contact
65 return {}
66
67
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:
73 return {}
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))}
76
77
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):
82 return {}
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)}
92
93
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)
101 )
102 var delta = closest - circle_position
103 var distance = delta.length()
104 if distance >= radius:
105 return {}
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}