Wizard Engine
2D cross-platform game engine built around SDL2
 
Loading...
Searching...
No Matches
collider.cpp
1/*
2 Wizard Engine
3 Copyright (C) 2023-2024 Zana Domán
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
23#define __WIZARD_ENGINE_INTERNAL__
24
26
27std::array<std::vector<wze::collider*>, std::numeric_limits<uint8_t>::max()>
28 wze::collider::_worlds = {};
29
30std::vector<wze::collider*> wze::collider::contacts() const {
31 std::vector<collider*> contacts;
32
33 std::for_each(_worlds.at(world()).begin(), _worlds.at(world()).end(),
34 [&](collider* contact) -> void {
35 if (contact != this &&
36 body().overlap<bool>(contact->body())) {
37 contacts.push_back(contact);
38 }
39 });
40
41 return contacts;
42}
43
44float wze::collider::contacts_mass(std::vector<collider*> const& contacts) {
45 float mass;
46
47 mass = 0;
48 std::for_each(contacts.begin(), contacts.end(),
49 [&](collider const* contact) -> void {
50 mass += contact->mass();
51 });
52
53 return mass;
54}
55
56void wze::collider::dual_static_resolver(collider const& other) {
57 std::pair<float, float> difference;
58 float collision;
59
60 difference.first = other.body().x() - body().x();
61 difference.second = other.body().y() - body().y();
62 if (!(bool)difference.first && !(bool)difference.second) {
63 return;
64 }
65
66 collision = body().overlap<float>(other.body());
67 if (!(bool)collision) {
68 return;
69 }
70
71 difference = math::normalize(difference.first, difference.second);
72 collision += math::epsilon();
73 _body.set_x(body().x() - difference.first * collision);
74 _body.set_y(body().y() - difference.second * collision);
75}
76
77bool wze::collider::dual_dynamic_resolver(collider& other, float force) {
78 std::pair<float, float> difference;
79 float collision;
80 std::pair<float, float> movement;
81
82 difference.first = other.body().x() - body().x();
83 difference.second = other.body().y() - body().y();
84 if (!(bool)difference.first && !(bool)difference.second) {
85 return false;
86 }
87
88 collision = body().overlap<float>(other.body());
89 if (!(bool)collision) {
90 return false;
91 }
92
93 difference = math::normalize(difference.first, difference.second);
94 movement = dynamic_movement(collision, force, other.mass());
95 _body.set_x(body().x() - difference.first * movement.first);
96 _body.set_y(body().y() - difference.second * movement.first);
97 other._body.set_x(other.body().x() + difference.first * movement.second);
98 other._body.set_y(other.body().y() + difference.second * movement.second);
99
100 return true;
101}
102
103std::pair<float, float>
104wze::collider::dynamic_movement(float collision, float force, float mass) {
105 float other_movement;
106 float movement;
107
108 collision += math::epsilon();
109 other_movement = collision * force / (force + mass);
110 movement = collision - other_movement;
111
112 return {movement, other_movement};
113}
114
115void wze::collider::align_entities() const {
116 std::for_each(_worlds.at(world()).begin(), _worlds.at(world()).end(),
117 [](collider* instance) -> void {
118 if (instance->entity::x() != instance->body().x()) {
119 instance->entity::set_x(instance->body().x());
120 }
121 if (instance->entity::y() != instance->body().y()) {
122 instance->entity::set_y(instance->body().y());
123 }
124 if (instance->entity::angle() !=
125 instance->body().angle()) {
126 instance->entity::set_angle(instance->body().angle());
127 }
128 });
129}
130
131wze::polygon const& wze::collider::body() const {
132 return _body;
133}
134
135void wze::collider::set_body(polygon const& body) {
136 _body = body;
137 entity::set_x(this->body().x());
138 entity::set_y(this->body().y());
139 entity::set_angle(this->body().angle());
140 entity::set_x_offset(this->body().x_offset());
141 entity::set_y_offset(this->body().y_offset());
142 entity::set_angle_offset(this->body().angle_offset());
143 entity::set_attach_x(this->body().attach_x());
144 entity::set_attach_y(this->body().attach_y());
145 entity::set_attach_angle(this->body().attach_angle());
146 entity::set_x_angle_lock(this->body().x_angle_lock());
147 entity::set_y_angle_lock(this->body().y_angle_lock());
148}
149
150float wze::collider::force() const {
151 return _force;
152}
153
154void wze::collider::set_force(float force) {
155 _force = force;
156}
157
158float wze::collider::mass() const {
159 return _mass;
160}
161
162void wze::collider::set_mass(float mass) {
163 _mass = mass;
164}
165
166uint8_t wze::collider::world() const {
167 return _world;
168}
169
170void wze::collider::set_world(uint8_t world) {
171 if (this->world() != std::numeric_limits<uint8_t>::max()) {
172 _worlds.at(this->world())
173 .erase(std::find(_worlds.at(this->world()).begin(),
174 _worlds.at(this->world()).end(), this));
175 }
176 _world = world;
177 if (this->world() != std::numeric_limits<uint8_t>::max()) {
178 _worlds.at(this->world()).push_back(this);
179 }
180}
181
182float wze::collider::x() const {
183 return _body.x();
184}
185
186void wze::collider::set_x(float x) {
187 _body.set_x(x);
188
189 if (world() == std::numeric_limits<uint8_t>::max()) {
190 entity::set_x(body().x());
191 return;
192 }
193
194 push<&collider::solo_static_resolver<&polygon::x, &polygon::set_x>,
195 &collider::solo_dynamic_resolver<&polygon::x, &polygon::set_x>>(
196 force());
197 align_entities();
198}
199
200float wze::collider::y() const {
201 return body().y();
202}
203
204void wze::collider::set_y(float y) {
205 _body.set_y(y);
206
207 if (world() == std::numeric_limits<uint8_t>::max()) {
208 entity::set_y(body().y());
209 return;
210 }
211
212 push<&collider::solo_static_resolver<&polygon::y, &polygon::set_y>,
213 &collider::solo_dynamic_resolver<&polygon::y, &polygon::set_y>>(
214 force());
215 align_entities();
216}
217
218float wze::collider::angle() const {
219 return _body.angle();
220}
221
222void wze::collider::set_angle(float angle) {
223 _body.set_angle(angle);
224
225 if (world() == std::numeric_limits<uint8_t>::max()) {
226 entity::set_angle(body().angle());
227 return;
228 }
229
230 push<&collider::dual_static_resolver, &collider::dual_dynamic_resolver>(
231 force());
232 align_entities();
233}
234
235float wze::collider::scale() const {
236 return body().scale();
237}
238
239void wze::collider::set_scale(float scale) {
240 _body.set_scale(scale);
241
242 if (world() == std::numeric_limits<uint8_t>::max()) {
243 return;
244 }
245
246 push<&collider::dual_static_resolver, &collider::dual_dynamic_resolver>(
247 force());
248 align_entities();
249}
250
251float wze::collider::x_offset() const {
252 return _body.x_offset();
253}
254
255void wze::collider::set_x_offset(float x_offset) {
256 _body.set_x_offset(x_offset);
257 entity::set_x_offset(body().x_offset());
258}
259
260float wze::collider::y_offset() const {
261 return body().y_offset();
262}
263
264void wze::collider::set_y_offset(float y_offset) {
265 _body.set_y_offset(y_offset);
266 entity::set_y_offset(body().y_offset());
267}
268
269float wze::collider::angle_offset() const {
270 return body().angle_offset();
271}
272
273void wze::collider::set_angle_offset(float angle_offset) {
274 _body.set_angle_offset(angle_offset);
275 entity::set_angle_offset(body().angle_offset());
276}
277
278bool wze::collider::attach_x() const {
279 return body().attach_x();
280}
281
282void wze::collider::set_attach_x(bool attach_x) {
283 _body.set_attach_x(attach_x);
284 entity::set_attach_x(body().attach_x());
285}
286
287bool wze::collider::attach_y() const {
288 return body().attach_y();
289}
290
291void wze::collider::set_attach_y(bool attach_y) {
292 _body.set_attach_y(attach_y);
293 entity::set_attach_y(body().attach_y());
294}
295
296bool wze::collider::attach_angle() const {
297 return body().attach_angle();
298}
299
300void wze::collider::set_attach_angle(bool attach_angle) {
301 _body.set_attach_angle(attach_angle);
302 entity::set_attach_angle(attach_angle);
303}
304
305bool wze::collider::x_angle_lock() const {
306 return body().x_angle_lock();
307}
308
309void wze::collider::set_x_angle_lock(bool x_angle_lock) {
310 _body.set_x_angle_lock(x_angle_lock);
311 entity::set_x_angle_lock(body().x_angle_lock());
312}
313
314bool wze::collider::y_angle_lock() const {
315 return body().y_angle_lock();
316}
317
318void wze::collider::set_y_angle_lock(bool y_angle_lock) {
319 _body.set_y_angle_lock(y_angle_lock);
320 entity::set_y_angle_lock(body().y_angle_lock());
321}
322
323wze::collider::collider(polygon const& body, float force, float mass,
324 uint8_t world,
325 std::vector<std::weak_ptr<component>> const& components)
326 : entity(components), _world(std::numeric_limits<uint8_t>::max()) {
327 set_body(body);
328 set_force(force);
329 set_mass(mass);
330 set_world(world);
331}
332
333wze::collider::collider(collider const& other)
334 : entity(other), _world(std::numeric_limits<uint8_t>::max()) {
335 *this = other;
336}
337
338wze::collider::~collider() {
339 if (world() != std::numeric_limits<uint8_t>::max()) {
340 _worlds.at(world()).erase(std::find(_worlds.at(world()).begin(),
341 _worlds.at(world()).end(), this));
342 }
343}
344
345wze::collider& wze::collider::operator=(collider const& other) {
346 if (&other != this) {
347 entity::operator=(other);
348 set_body(other.body());
349 set_force(other.force());
350 set_mass(other.mass());
351 set_world(other.world());
352 }
353
354 return *this;
355}
static std::pair< float, float > normalize(float x, float y)
Normalizes a vector.
Definition math.cpp:41
static constexpr float epsilon()
Single precision epsilon.
Definition math.hpp:47
Initiates a collision.