diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-05-08 18:38:28 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-05-09 17:59:13 -0400 |
commit | 96e6f3231aed9919d660a06944f1d96dc8241f8e (patch) | |
tree | b2e633e7df6e0251afeec8a84a69be7b5944b175 | |
parent | 296a1c3aa0bdb27e7ee9b53f0382938d0fe6d1a0 (diff) | |
download | therapy-96e6f3231aed9919d660a06944f1d96dc8241f8e.tar.gz therapy-96e6f3231aed9919d660a06944f1d96dc8241f8e.tar.bz2 therapy-96e6f3231aed9919d660a06944f1d96dc8241f8e.zip |
Started refactoring collision detection to use directional functor
Because there was a lot of code replication with collision detection, this new implementation uses a functor that takes a parameter object describing the things that are different between the four directions. This allows the collision detection code to be writen only once. It's currently very ugly, but should work identically to before.
-rw-r--r-- | src/systems/pondering.cpp | 1088 | ||||
-rw-r--r-- | src/systems/pondering.h | 40 |
2 files changed, 533 insertions, 595 deletions
diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index ed14772..7b3ab2d 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp | |||
@@ -96,15 +96,6 @@ void PonderingSystem::tickBody( | |||
96 | return; | 96 | return; |
97 | } | 97 | } |
98 | 98 | ||
99 | auto& realizable = game_.getEntityManager(). | ||
100 | getComponent<RealizableComponent>( | ||
101 | game_.getSystemManager().getSystem<RealizingSystem>().getSingleton()); | ||
102 | |||
103 | id_type mapEntity = realizable.activeMap; | ||
104 | |||
105 | auto& mappable = game_.getEntityManager(). | ||
106 | getComponent<MappableComponent>(mapEntity); | ||
107 | |||
108 | auto& transformable = game_.getEntityManager(). | 99 | auto& transformable = game_.getEntityManager(). |
109 | getComponent<TransformableComponent>(entity); | 100 | getComponent<TransformableComponent>(entity); |
110 | 101 | ||
@@ -121,14 +112,9 @@ void PonderingSystem::tickBody( | |||
121 | } | 112 | } |
122 | } | 113 | } |
123 | 114 | ||
124 | const double oldX = transformable.x; | 115 | // Move |
125 | const double oldY = transformable.y; | 116 | double newX = transformable.x; |
126 | const double oldRight = oldX + transformable.w; | 117 | double newY = transformable.y; |
127 | const double oldBottom = oldY + transformable.h; | ||
128 | |||
129 | CollisionResult result; | ||
130 | result.newX = transformable.x; | ||
131 | result.newY = transformable.y; | ||
132 | 118 | ||
133 | if (!ponderable.frozen) | 119 | if (!ponderable.frozen) |
134 | { | 120 | { |
@@ -137,692 +123,626 @@ void PonderingSystem::tickBody( | |||
137 | auto& ferryTrans = game_.getEntityManager(). | 123 | auto& ferryTrans = game_.getEntityManager(). |
138 | getComponent<TransformableComponent>(ponderable.ferry); | 124 | getComponent<TransformableComponent>(ponderable.ferry); |
139 | 125 | ||
140 | result.newX = ferryTrans.x + ponderable.relX; | 126 | newX = ferryTrans.x + ponderable.relX; |
141 | result.newY = ferryTrans.y + ponderable.relY; | 127 | newY = ferryTrans.y + ponderable.relY; |
142 | } | 128 | } |
143 | 129 | ||
144 | result.newX += ponderable.velX * dt; | 130 | newX += ponderable.velX * dt; |
145 | result.newY += ponderable.velY * dt; | 131 | newY += ponderable.velY * dt; |
146 | } | 132 | } |
147 | 133 | ||
148 | bool oldGrounded = ponderable.grounded; | 134 | CollisionResult result = |
149 | ponderable.grounded = false; | 135 | moveBody( |
136 | entity, | ||
137 | newX, | ||
138 | newY); | ||
150 | 139 | ||
151 | if (ponderable.collidable) | 140 | // Perform cleanup for orientable entites |
141 | bool groundedChanged = (ponderable.grounded != result.grounded); | ||
142 | ponderable.grounded = result.grounded; | ||
143 | |||
144 | if (game_.getEntityManager().hasComponent<OrientableComponent>(entity)) | ||
152 | { | 145 | { |
153 | // Find horizontal collisions. | 146 | auto& orientable = game_.getEntityManager(). |
154 | if (result.newX < oldX) | 147 | getComponent<OrientableComponent>(entity); |
155 | { | ||
156 | bool boundaryCollision = false; | ||
157 | auto it = mappable.leftBoundaries.lower_bound(oldX); | ||
158 | 148 | ||
159 | // Find the axis distance of the closest environmental boundary. | 149 | // Handle changes in groundedness |
160 | for (; | 150 | if (groundedChanged) |
161 | (it != std::end(mappable.leftBoundaries)) && | 151 | { |
162 | (it->first >= result.newX); | 152 | if (ponderable.grounded) |
163 | it++) | ||
164 | { | 153 | { |
165 | // Check that the boundary is in range for the other axis. | 154 | game_.getSystemManager().getSystem<OrientingSystem>().land(entity); |
166 | if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) | 155 | } else { |
167 | { | 156 | game_.getSystemManager(). |
168 | // We have a collision! | 157 | getSystem<OrientingSystem>().startFalling(entity); |
169 | boundaryCollision = true; | ||
170 | |||
171 | break; | ||
172 | } | ||
173 | } | 158 | } |
159 | } | ||
174 | 160 | ||
175 | // Find a list of potential colliders, sorted so that the closest is | 161 | // Complete dropping, if necessary |
176 | // first. | 162 | if (orientable.getDropState() == OrientableComponent::DropState::active) |
177 | std::vector<id_type> colliders; | 163 | { |
164 | orientable.setDropState(OrientableComponent::DropState::none); | ||
165 | } | ||
166 | } | ||
178 | 167 | ||
179 | for (id_type collider : entities) | 168 | // Ferry or unferry as necessary |
180 | { | 169 | if ((ponderable.type == PonderableComponent::Type::freefalling) && |
181 | // Can't collide with self. | 170 | groundedChanged) |
182 | if (collider == entity) | 171 | { |
183 | { | 172 | if (ponderable.grounded && |
184 | continue; | 173 | game_.getEntityManager(). |
185 | } | 174 | hasComponent<PonderableComponent>(result.groundEntity)) |
175 | { | ||
176 | // The body is now being ferried | ||
177 | auto& ferryPonder = game_.getEntityManager(). | ||
178 | getComponent<PonderableComponent>(result.groundEntity); | ||
179 | |||
180 | ponderable.ferried = true; | ||
181 | ponderable.ferry = result.groundEntity; | ||
186 | 182 | ||
187 | auto& colliderPonder = game_.getEntityManager(). | 183 | ferryPonder.passengers.insert(entity); |
188 | getComponent<PonderableComponent>(collider); | 184 | } else if (ponderable.ferried) |
185 | { | ||
186 | // The body is no longer being ferried | ||
187 | unferry(entity); | ||
188 | } | ||
189 | } | ||
189 | 190 | ||
190 | // Only check objects that are active and collidable. | 191 | // Update a ferry passenger's relative position |
191 | if (!colliderPonder.active || !colliderPonder.collidable) | 192 | if (ponderable.ferried) |
192 | { | 193 | { |
193 | continue; | 194 | auto& ferryTrans = game_.getEntityManager(). |
194 | } | 195 | getComponent<TransformableComponent>(ponderable.ferry); |
195 | 196 | ||
196 | auto& colliderTrans = game_.getEntityManager(). | 197 | ponderable.relX = transformable.x - ferryTrans.x; |
197 | getComponent<TransformableComponent>(collider); | 198 | ponderable.relY = transformable.y - ferryTrans.y; |
198 | 199 | } | |
199 | // Check if the entity would move into the potential collider, | ||
200 | if ((colliderTrans.x + colliderTrans.w > result.newX) && | ||
201 | // that it wasn't already colliding, | ||
202 | (colliderTrans.x + colliderTrans.w <= oldX) && | ||
203 | // that the position on the other axis is in range, | ||
204 | (colliderTrans.y + colliderTrans.h > oldY) && | ||
205 | (colliderTrans.y < oldBottom) && | ||
206 | // and that the collider is not farther away than the environmental | ||
207 | // boundary. | ||
208 | (!boundaryCollision || | ||
209 | (colliderTrans.x + colliderTrans.w >= it->first))) | ||
210 | { | ||
211 | colliders.push_back(collider); | ||
212 | } | ||
213 | } | ||
214 | 200 | ||
215 | std::sort( | 201 | // Handle ferry passengers |
216 | std::begin(colliders), | 202 | std::set<id_type> passengers = ponderable.passengers; |
217 | std::end(colliders), | ||
218 | [&] (id_type left, id_type right) { | ||
219 | auto& leftTrans = game_.getEntityManager(). | ||
220 | getComponent<TransformableComponent>(left); | ||
221 | 203 | ||
222 | auto& rightTrans = game_.getEntityManager(). | 204 | for (id_type passenger : passengers) |
223 | getComponent<TransformableComponent>(right); | 205 | { |
206 | tickBody( | ||
207 | passenger, | ||
208 | dt, | ||
209 | entities); | ||
210 | } | ||
224 | 211 | ||
225 | return (rightTrans.x < leftTrans.x); | 212 | // Move to an adjacent map, if necessary |
226 | }); | 213 | if (result.adjacentlyWarping) |
214 | { | ||
215 | double warpX = result.newX; | ||
216 | double warpY = result.newY; | ||
227 | 217 | ||
228 | for (id_type collider : colliders) | 218 | switch (result.adjWarpDir) |
219 | { | ||
220 | case Direction::left: | ||
229 | { | 221 | { |
230 | auto& colliderTrans = game_.getEntityManager(). | 222 | warpX = GAME_WIDTH + WALL_GAP - transformable.w; |
231 | getComponent<TransformableComponent>(collider); | ||
232 | |||
233 | // Check if the entity would still move into the potential collider. | ||
234 | if (colliderTrans.x + colliderTrans.w <= result.newX) | ||
235 | { | ||
236 | break; | ||
237 | } | ||
238 | 223 | ||
239 | auto& colliderPonder = game_.getEntityManager(). | 224 | break; |
240 | getComponent<PonderableComponent>(collider); | 225 | } |
241 | 226 | ||
242 | processCollision( | 227 | case Direction::right: |
243 | entity, | 228 | { |
244 | collider, | 229 | warpX = -WALL_GAP; |
245 | Direction::left, | ||
246 | colliderPonder.colliderType, | ||
247 | colliderTrans.x + colliderTrans.w, | ||
248 | colliderTrans.y, | ||
249 | colliderTrans.y + colliderTrans.h, | ||
250 | result); | ||
251 | 230 | ||
252 | if (result.stopProcessing) | 231 | break; |
253 | { | ||
254 | break; | ||
255 | } | ||
256 | } | 232 | } |
257 | 233 | ||
258 | // If movement hasn't been stopped by an intermediary object, and | 234 | case Direction::up: |
259 | // collision checking hasn't been stopped, process the environmental | ||
260 | // boundaries closest to the entity. | ||
261 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) | ||
262 | { | 235 | { |
263 | double boundaryAxis = it->first; | 236 | warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.h; |
264 | 237 | ||
265 | for (; | 238 | break; |
266 | (it != std::end(mappable.leftBoundaries)) && | ||
267 | (it->first == boundaryAxis); | ||
268 | it++) | ||
269 | { | ||
270 | if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) | ||
271 | { | ||
272 | processCollision( | ||
273 | entity, | ||
274 | mapEntity, | ||
275 | Direction::left, | ||
276 | it->second.type, | ||
277 | it->first, | ||
278 | it->second.lower, | ||
279 | it->second.upper, | ||
280 | result); | ||
281 | |||
282 | if (result.stopProcessing) | ||
283 | { | ||
284 | break; | ||
285 | } | ||
286 | } | ||
287 | } | ||
288 | } | 239 | } |
289 | } else if (result.newX > oldX) | ||
290 | { | ||
291 | bool boundaryCollision = false; | ||
292 | auto it = mappable.rightBoundaries.lower_bound(oldRight); | ||
293 | 240 | ||
294 | // Find the axis distance of the closest environmental boundary. | 241 | case Direction::down: |
295 | for (; | ||
296 | (it != std::end(mappable.rightBoundaries)) | ||
297 | && (it->first <= (result.newX + transformable.w)); | ||
298 | it++) | ||
299 | { | 242 | { |
300 | // Check that the boundary is in range for the other axis. | 243 | warpY = -WALL_GAP; |
301 | if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) | ||
302 | { | ||
303 | // We have a collision! | ||
304 | boundaryCollision = true; | ||
305 | 244 | ||
306 | break; | 245 | break; |
307 | } | ||
308 | } | 246 | } |
247 | } | ||
309 | 248 | ||
310 | // Find a list of potential colliders, sorted so that the closest is | 249 | game_.getSystemManager().getSystem<PlayingSystem>(). |
311 | // first. | 250 | changeMap( |
312 | std::vector<id_type> colliders; | 251 | entity, |
313 | 252 | result.adjWarpMapId, | |
314 | for (id_type collider : entities) | 253 | warpX, |
315 | { | 254 | warpY); |
316 | // Can't collide with self. | 255 | } |
317 | if (collider == entity) | 256 | } |
318 | { | ||
319 | continue; | ||
320 | } | ||
321 | 257 | ||
322 | auto& colliderPonder = game_.getEntityManager(). | 258 | CollisionResult PonderingSystem::moveBody( |
323 | getComponent<PonderableComponent>(collider); | 259 | id_type entity, |
260 | double x, | ||
261 | double y) | ||
262 | { | ||
263 | auto& ponderable = game_.getEntityManager(). | ||
264 | getComponent<PonderableComponent>(entity); | ||
324 | 265 | ||
325 | // Only check objects that are active and collidable. | 266 | auto& transformable = game_.getEntityManager(). |
326 | if (!colliderPonder.active || !colliderPonder.collidable) | 267 | getComponent<TransformableComponent>(entity); |
327 | { | ||
328 | continue; | ||
329 | } | ||
330 | 268 | ||
331 | auto& colliderTrans = game_.getEntityManager(). | 269 | const double oldX = transformable.x; |
332 | getComponent<TransformableComponent>(collider); | 270 | const double oldY = transformable.y; |
333 | 271 | const double oldRight = oldX + transformable.w; | |
334 | // Check if the entity would move into the potential collider, | 272 | const double oldBottom = oldY + transformable.h; |
335 | if ((colliderTrans.x < result.newX + transformable.w) && | ||
336 | // that it wasn't already colliding, | ||
337 | (colliderTrans.x >= oldRight) && | ||
338 | // that the position on the other axis is in range, | ||
339 | (colliderTrans.y + colliderTrans.h > oldY) && | ||
340 | (colliderTrans.y < oldBottom) && | ||
341 | // and that the collider is not farther away than the environmental | ||
342 | // boundary. | ||
343 | (!boundaryCollision || (colliderTrans.x <= it->first))) | ||
344 | { | ||
345 | colliders.push_back(collider); | ||
346 | } | ||
347 | } | ||
348 | 273 | ||
349 | std::sort( | 274 | CollisionResult result; |
350 | std::begin(colliders), | ||
351 | std::end(colliders), | ||
352 | [&] (id_type left, id_type right) { | ||
353 | auto& leftTrans = game_.getEntityManager(). | ||
354 | getComponent<TransformableComponent>(left); | ||
355 | 275 | ||
356 | auto& rightTrans = game_.getEntityManager(). | 276 | if (ponderable.collidable) |
357 | getComponent<TransformableComponent>(right); | 277 | { |
278 | result = detectCollisions(entity, x, y); | ||
279 | } else { | ||
280 | result.newX = x; | ||
281 | result.newY = y; | ||
282 | } | ||
358 | 283 | ||
359 | return (leftTrans.x < rightTrans.x); | 284 | // Move |
360 | }); | 285 | if (!ponderable.frozen) |
286 | { | ||
287 | transformable.x = result.newX; | ||
288 | transformable.y = result.newY; | ||
289 | } | ||
361 | 290 | ||
362 | for (id_type collider : colliders) | 291 | return result; |
363 | { | 292 | } |
364 | auto& colliderTrans = game_.getEntityManager(). | ||
365 | getComponent<TransformableComponent>(collider); | ||
366 | 293 | ||
367 | // Check if the entity would still move into the potential collider. | 294 | namespace CollisionParams { |
368 | if (colliderTrans.x >= result.newX + transformable.w) | ||
369 | { | ||
370 | break; | ||
371 | } | ||
372 | 295 | ||
373 | auto& colliderPonder = game_.getEntityManager(). | 296 | template <typename HorizVert> |
374 | getComponent<PonderableComponent>(collider); | 297 | class Desc : public HorizVert { |
298 | public: | ||
375 | 299 | ||
376 | processCollision( | 300 | inline static bool AtLeastInAxisSweep( |
377 | entity, | 301 | double boundaryAxis, |
378 | collider, | 302 | double entityAxis) |
379 | Direction::right, | 303 | { |
380 | colliderPonder.colliderType, | 304 | return (boundaryAxis >= entityAxis); |
381 | colliderTrans.x, | 305 | } |
382 | colliderTrans.y, | ||
383 | colliderTrans.y + colliderTrans.h, | ||
384 | result); | ||
385 | 306 | ||
386 | if (result.stopProcessing) | 307 | inline static bool IsPastAxis( |
387 | { | 308 | double colliderAxis, |
388 | break; | 309 | double entityAxis) |
389 | } | 310 | { |
390 | } | 311 | return (colliderAxis > entityAxis); |
312 | } | ||
391 | 313 | ||
392 | // If movement hasn't been stopped by an intermediary object, and | 314 | inline static double OldAxis(const TransformableComponent& transformable) |
393 | // collision checking hasn't been stopped, process the environmental | 315 | { |
394 | // boundaries closest to the entity. | 316 | return HorizVert::AxisOldLower(transformable); |
395 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) | 317 | } |
396 | { | ||
397 | double boundaryAxis = it->first; | ||
398 | 318 | ||
399 | for (; | 319 | inline static double NewAxis( |
400 | (it != std::end(mappable.rightBoundaries)) && | 320 | const CollisionResult& result, |
401 | (it->first == boundaryAxis); | 321 | const TransformableComponent&) |
402 | it++) | 322 | { |
403 | { | 323 | return HorizVert::AxisNewLower(result); |
404 | if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) | ||
405 | { | ||
406 | processCollision( | ||
407 | entity, | ||
408 | mapEntity, | ||
409 | Direction::right, | ||
410 | it->second.type, | ||
411 | it->first, | ||
412 | it->second.lower, | ||
413 | it->second.upper, | ||
414 | result); | ||
415 | |||
416 | if (result.stopProcessing) | ||
417 | { | ||
418 | break; | ||
419 | } | ||
420 | } | ||
421 | } | ||
422 | } | ||
423 | } | 324 | } |
424 | } | ||
425 | 325 | ||
426 | // Find vertical collisions | 326 | inline static double ObjectAxis(const TransformableComponent& transformable) |
427 | if (ponderable.collidable && !result.stopProcessing) | 327 | { |
428 | { | 328 | return HorizVert::AxisOldUpper(transformable); |
429 | result.touchedWall = false; | 329 | } |
430 | 330 | ||
431 | if (result.newY < oldY) | 331 | inline static bool Closer(double left, double right) |
432 | { | 332 | { |
433 | bool boundaryCollision = false; | 333 | return right < left; |
434 | auto it = mappable.upBoundaries.lower_bound(oldY); | 334 | } |
335 | }; | ||
435 | 336 | ||
436 | // Find the axis distance of the closest environmental boundary. | 337 | template <typename HorizVert> |
437 | for (; | 338 | class Asc : public HorizVert { |
438 | (it != std::end(mappable.upBoundaries)) && | 339 | public: |
439 | (it->first >= result.newY); | ||
440 | it++) | ||
441 | { | ||
442 | // Check that the boundary is in range for the other axis. | ||
443 | if ((result.newX + transformable.w > it->second.lower) && | ||
444 | (result.newX < it->second.upper)) | ||
445 | { | ||
446 | // We have a collision! | ||
447 | boundaryCollision = true; | ||
448 | 340 | ||
449 | break; | 341 | inline static bool AtLeastInAxisSweep( |
450 | } | 342 | double boundaryAxis, |
451 | } | 343 | double entityAxis) |
344 | { | ||
345 | return (boundaryAxis <= entityAxis); | ||
346 | } | ||
452 | 347 | ||
453 | // Find a list of potential colliders, sorted so that the closest is | 348 | inline static bool IsPastAxis( |
454 | // first. | 349 | double colliderAxis, |
455 | std::vector<id_type> colliders; | 350 | double entityAxis) |
351 | { | ||
352 | return (colliderAxis < entityAxis); | ||
353 | } | ||
456 | 354 | ||
457 | for (id_type collider : entities) | 355 | inline static double OldAxis(const TransformableComponent& transformable) |
458 | { | 356 | { |
459 | // Can't collide with self. | 357 | return HorizVert::AxisOldUpper(transformable); |
460 | if (collider == entity) | 358 | } |
461 | { | ||
462 | continue; | ||
463 | } | ||
464 | 359 | ||
465 | auto& colliderPonder = game_.getEntityManager(). | 360 | inline static double NewAxis( |
466 | getComponent<PonderableComponent>(collider); | 361 | const CollisionResult& result, |
362 | const TransformableComponent& transformable) | ||
363 | { | ||
364 | return HorizVert::AxisNewUpper(result, transformable); | ||
365 | } | ||
467 | 366 | ||
468 | // Only check objects that are active and collidable. | 367 | inline static double ObjectAxis(const TransformableComponent& transformable) |
469 | if (!colliderPonder.active || !colliderPonder.collidable) | 368 | { |
470 | { | 369 | return HorizVert::AxisOldLower(transformable); |
471 | continue; | 370 | } |
472 | } | ||
473 | 371 | ||
474 | auto& colliderTrans = game_.getEntityManager(). | 372 | inline static bool Closer(double left, double right) |
475 | getComponent<TransformableComponent>(collider); | 373 | { |
476 | 374 | return left < right; | |
477 | // Check if the entity would move into the potential collider, | 375 | } |
478 | if ((colliderTrans.y + colliderTrans.h > result.newY) && | 376 | }; |
479 | // that it wasn't already colliding, | ||
480 | (colliderTrans.y + colliderTrans.h <= oldY) && | ||
481 | // that the position on the other axis is in range, | ||
482 | (colliderTrans.x + colliderTrans.w > result.newX) && | ||
483 | (colliderTrans.x < result.newX + transformable.w) && | ||
484 | // and that the collider is not farther away than the environmental | ||
485 | // boundary. | ||
486 | (!boundaryCollision || | ||
487 | (colliderTrans.y + colliderTrans.h >= it->first))) | ||
488 | { | ||
489 | colliders.push_back(collider); | ||
490 | } | ||
491 | } | ||
492 | 377 | ||
493 | std::sort( | 378 | class Horizontal { |
494 | std::begin(colliders), | 379 | public: |
495 | std::end(colliders), | ||
496 | [&] (id_type left, id_type right) { | ||
497 | auto& leftTrans = game_.getEntityManager(). | ||
498 | getComponent<TransformableComponent>(left); | ||
499 | 380 | ||
500 | auto& rightTrans = game_.getEntityManager(). | 381 | inline static double AxisOldLower( |
501 | getComponent<TransformableComponent>(right); | 382 | const TransformableComponent& transformable) |
383 | { | ||
384 | return transformable.x; | ||
385 | } | ||
502 | 386 | ||
503 | return (rightTrans.y < leftTrans.y); | 387 | inline static double AxisOldUpper( |
504 | }); | 388 | const TransformableComponent& transformable) |
389 | { | ||
390 | return transformable.x + transformable.w; | ||
391 | } | ||
505 | 392 | ||
506 | for (id_type collider : colliders) | 393 | inline static double AxisNewLower(const CollisionResult& result) |
507 | { | 394 | { |
508 | auto& colliderTrans = game_.getEntityManager(). | 395 | return result.newX; |
509 | getComponent<TransformableComponent>(collider); | 396 | } |
510 | 397 | ||
511 | // Check if the entity would still move into the potential collider. | 398 | inline static double AxisNewUpper( |
512 | if (colliderTrans.y + colliderTrans.h <= result.newY) | 399 | const CollisionResult& result, |
513 | { | 400 | const TransformableComponent& transformable) |
514 | break; | 401 | { |
515 | } | 402 | return result.newX + transformable.w; |
403 | } | ||
516 | 404 | ||
517 | auto& colliderPonder = game_.getEntityManager(). | 405 | inline static double NonAxisOldLower( |
518 | getComponent<PonderableComponent>(collider); | 406 | const TransformableComponent& transformable) |
407 | { | ||
408 | return transformable.y; | ||
409 | } | ||
519 | 410 | ||
520 | processCollision( | 411 | inline static double NonAxisOldUpper( |
521 | entity, | 412 | const TransformableComponent& transformable) |
522 | collider, | 413 | { |
523 | Direction::up, | 414 | return transformable.y + transformable.h; |
524 | colliderPonder.colliderType, | 415 | } |
525 | colliderTrans.y + colliderTrans.h, | ||
526 | colliderTrans.x, | ||
527 | colliderTrans.x + colliderTrans.w, | ||
528 | result); | ||
529 | 416 | ||
530 | if (result.stopProcessing) | 417 | inline static double NonAxisNewLower( |
531 | { | 418 | const CollisionResult& result, |
532 | break; | 419 | const TransformableComponent& transformable) |
533 | } | 420 | { |
534 | } | 421 | return result.newY; |
422 | } | ||
535 | 423 | ||
536 | // If movement hasn't been stopped by an intermediary object, and | 424 | inline static double NonAxisNewUpper( |
537 | // collision checking hasn't been stopped, process the environmental | 425 | const CollisionResult& result, |
538 | // boundaries closest to the entity. | 426 | const TransformableComponent& transformable) |
539 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) | 427 | { |
540 | { | 428 | return result.newY + transformable.h; |
541 | double boundaryAxis = it->first; | 429 | } |
430 | }; | ||
542 | 431 | ||
543 | for (; | 432 | class Vertical { |
544 | (it != std::end(mappable.upBoundaries)) && | 433 | public: |
545 | (it->first == boundaryAxis); | 434 | |
546 | it++) | 435 | inline static double AxisOldLower( |
547 | { | 436 | const TransformableComponent& transformable) |
548 | if ((result.newX + transformable.w > it->second.lower) && | ||
549 | (result.newX < it->second.upper)) | ||
550 | { | ||
551 | processCollision( | ||
552 | entity, | ||
553 | mapEntity, | ||
554 | Direction::up, | ||
555 | it->second.type, | ||
556 | it->first, | ||
557 | it->second.lower, | ||
558 | it->second.upper, | ||
559 | result); | ||
560 | |||
561 | if (result.stopProcessing) | ||
562 | { | ||
563 | break; | ||
564 | } | ||
565 | } | ||
566 | } | ||
567 | } | ||
568 | } else if (result.newY > oldY) | ||
569 | { | 437 | { |
570 | bool boundaryCollision = false; | 438 | return transformable.y; |
571 | auto it = mappable.downBoundaries.lower_bound(oldBottom); | 439 | } |
572 | 440 | ||
573 | // Find the axis distance of the closest environmental boundary. | 441 | inline static double AxisOldUpper( |
574 | for (; | 442 | const TransformableComponent& transformable) |
575 | (it != std::end(mappable.downBoundaries)) | 443 | { |
576 | && (it->first <= (result.newY + transformable.h)); | 444 | return transformable.y + transformable.h; |
577 | it++) | 445 | } |
578 | { | ||
579 | // Check that the boundary is in range for the other axis. | ||
580 | if ((result.newX + transformable.w > it->second.lower) && | ||
581 | (result.newX < it->second.upper)) | ||
582 | { | ||
583 | // We have a collision! | ||
584 | boundaryCollision = true; | ||
585 | 446 | ||
586 | break; | 447 | inline static double AxisNewLower(const CollisionResult& result) |
587 | } | 448 | { |
588 | } | 449 | return result.newY; |
450 | } | ||
589 | 451 | ||
590 | // Find a list of potential colliders, sorted so that the closest is | 452 | inline static double AxisNewUpper( |
591 | // first. | 453 | const CollisionResult& result, |
592 | std::vector<id_type> colliders; | 454 | const TransformableComponent& transformable) |
455 | { | ||
456 | return result.newY + transformable.h; | ||
457 | } | ||
593 | 458 | ||
594 | for (id_type collider : entities) | 459 | inline static double NonAxisOldLower( |
595 | { | 460 | const TransformableComponent& transformable) |
596 | // Can't collide with self. | 461 | { |
597 | if (collider == entity) | 462 | return transformable.x; |
598 | { | 463 | } |
599 | continue; | ||
600 | } | ||
601 | 464 | ||
602 | auto& colliderPonder = game_.getEntityManager(). | 465 | inline static double NonAxisOldUpper( |
603 | getComponent<PonderableComponent>(collider); | 466 | const TransformableComponent& transformable) |
467 | { | ||
468 | return transformable.x + transformable.w; | ||
469 | } | ||
604 | 470 | ||
605 | // Only check objects that are active and collidable. | 471 | inline static double NonAxisNewLower( |
606 | if (!colliderPonder.active || !colliderPonder.collidable) | 472 | const CollisionResult& result, |
607 | { | 473 | const TransformableComponent& transformable) |
608 | continue; | 474 | { |
609 | } | 475 | return result.newX; |
476 | } | ||
610 | 477 | ||
611 | auto& colliderTrans = game_.getEntityManager(). | 478 | inline static double NonAxisNewUpper( |
612 | getComponent<TransformableComponent>(collider); | 479 | const CollisionResult& result, |
613 | 480 | const TransformableComponent& transformable) | |
614 | // Check if the entity would move into the potential collider, | 481 | { |
615 | if ((colliderTrans.y < result.newY + transformable.h) && | 482 | return result.newX + transformable.w; |
616 | // that it wasn't already colliding, | 483 | } |
617 | (colliderTrans.y >= oldBottom) && | 484 | }; |
618 | // that the position on the other axis is in range, | ||
619 | (colliderTrans.x + colliderTrans.w > result.newX) && | ||
620 | (colliderTrans.x < result.newX + transformable.w) && | ||
621 | // and that the collider is not farther away than the environmental | ||
622 | // boundary. | ||
623 | (!boundaryCollision || (colliderTrans.y <= it->first))) | ||
624 | { | ||
625 | colliders.push_back(collider); | ||
626 | } | ||
627 | } | ||
628 | 485 | ||
629 | std::sort( | 486 | template <Direction dir, typename AscDesc> |
630 | std::begin(colliders), | 487 | class DetectCollisions : public AscDesc { |
631 | std::end(colliders), | 488 | public: |
632 | [&] (id_type left, id_type right) { | ||
633 | auto& leftTrans = game_.getEntityManager(). | ||
634 | getComponent<TransformableComponent>(left); | ||
635 | 489 | ||
636 | auto& rightTrans = game_.getEntityManager(). | 490 | static const Direction Dir = dir; |
637 | getComponent<TransformableComponent>(right); | 491 | }; |
638 | 492 | ||
639 | return (leftTrans.y < rightTrans.y); | 493 | class Left : public DetectCollisions<Direction::left, Desc<Horizontal>> { |
640 | }); | 494 | public: |
641 | 495 | ||
642 | for (id_type collider : colliders) | 496 | inline static const MappableComponent::desc_boundaries_type& MapBoundaries( |
643 | { | 497 | const MappableComponent& mappable) |
644 | auto& colliderTrans = game_.getEntityManager(). | 498 | { |
645 | getComponent<TransformableComponent>(collider); | 499 | return mappable.leftBoundaries; |
500 | } | ||
501 | }; | ||
646 | 502 | ||
647 | // Check if the entity would still move into the potential collider. | 503 | class Right : public DetectCollisions<Direction::right, Asc<Horizontal>> { |
648 | if (colliderTrans.y >= result.newY + transformable.h) | 504 | public: |
649 | { | ||
650 | break; | ||
651 | } | ||
652 | 505 | ||
653 | auto& colliderPonder = game_.getEntityManager(). | 506 | inline static const MappableComponent::asc_boundaries_type& MapBoundaries( |
654 | getComponent<PonderableComponent>(collider); | 507 | const MappableComponent& mappable) |
508 | { | ||
509 | return mappable.rightBoundaries; | ||
510 | } | ||
511 | }; | ||
655 | 512 | ||
656 | processCollision( | 513 | class Up : public DetectCollisions<Direction::up, Desc<Vertical>> { |
657 | entity, | 514 | public: |
658 | collider, | ||
659 | Direction::down, | ||
660 | colliderPonder.colliderType, | ||
661 | colliderTrans.y, | ||
662 | colliderTrans.x, | ||
663 | colliderTrans.x + colliderTrans.w, | ||
664 | result); | ||
665 | 515 | ||
666 | if (result.stopProcessing) | 516 | inline static const MappableComponent::desc_boundaries_type& MapBoundaries( |
667 | { | 517 | const MappableComponent& mappable) |
668 | break; | 518 | { |
669 | } | 519 | return mappable.upBoundaries; |
670 | } | 520 | } |
521 | }; | ||
671 | 522 | ||
672 | // If movement hasn't been stopped by an intermediary object, and | 523 | class Down : public DetectCollisions<Direction::down, Asc<Vertical>> { |
673 | // collision checking hasn't been stopped, process the environmental | 524 | public: |
674 | // boundaries closest to the entity. | ||
675 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) | ||
676 | { | ||
677 | double boundaryAxis = it->first; | ||
678 | 525 | ||
679 | for (; | 526 | inline static const MappableComponent::asc_boundaries_type& MapBoundaries( |
680 | (it != std::end(mappable.downBoundaries)) && | 527 | const MappableComponent& mappable) |
681 | (it->first == boundaryAxis); | 528 | { |
682 | it++) | 529 | return mappable.downBoundaries; |
683 | { | ||
684 | if ((result.newX + transformable.w > it->second.lower) && | ||
685 | (result.newX < it->second.upper)) | ||
686 | { | ||
687 | processCollision( | ||
688 | entity, | ||
689 | mapEntity, | ||
690 | Direction::down, | ||
691 | it->second.type, | ||
692 | it->first, | ||
693 | it->second.lower, | ||
694 | it->second.upper, | ||
695 | result); | ||
696 | |||
697 | if (result.stopProcessing) | ||
698 | { | ||
699 | break; | ||
700 | } | ||
701 | } | ||
702 | } | ||
703 | } | ||
704 | } | 530 | } |
705 | } | 531 | }; |
532 | }; | ||
706 | 533 | ||
707 | // Move | 534 | CollisionResult PonderingSystem::detectCollisions( |
708 | if (!ponderable.frozen) | 535 | id_type entity, |
536 | double x, | ||
537 | double y) | ||
538 | { | ||
539 | auto& transformable = game_.getEntityManager(). | ||
540 | getComponent<TransformableComponent>(entity); | ||
541 | |||
542 | CollisionResult result; | ||
543 | result.newX = x; | ||
544 | result.newY = transformable.y; | ||
545 | |||
546 | // Find horizontal collisions. | ||
547 | if (result.newX < transformable.x) | ||
709 | { | 548 | { |
710 | transformable.x = result.newX; | 549 | detectCollisionsInDirection<CollisionParams::Left>(entity, result); |
711 | transformable.y = result.newY; | 550 | } else if (result.newX > transformable.x) |
551 | { | ||
552 | detectCollisionsInDirection<CollisionParams::Right>(entity, result); | ||
712 | } | 553 | } |
713 | 554 | ||
714 | // Perform cleanup for orientable entites | 555 | // Find vertical collisions |
715 | if (game_.getEntityManager().hasComponent<OrientableComponent>(entity)) | 556 | if (!result.stopProcessing) |
716 | { | 557 | { |
717 | auto& orientable = game_.getEntityManager(). | 558 | result.newY = y; |
718 | getComponent<OrientableComponent>(entity); | 559 | result.touchedWall = false; |
719 | 560 | ||
720 | // Handle changes in groundedness | 561 | if (result.newY < transformable.y) |
721 | if (ponderable.grounded != oldGrounded) | ||
722 | { | 562 | { |
723 | if (ponderable.grounded) | 563 | detectCollisionsInDirection<CollisionParams::Up>(entity, result); |
724 | { | 564 | } else if (result.newY > transformable.y) |
725 | game_.getSystemManager().getSystem<OrientingSystem>().land(entity); | 565 | { |
726 | } else { | 566 | detectCollisionsInDirection<CollisionParams::Down>(entity, result); |
727 | game_.getSystemManager(). | ||
728 | getSystem<OrientingSystem>().startFalling(entity); | ||
729 | } | ||
730 | } | 567 | } |
568 | } | ||
731 | 569 | ||
732 | // Complete dropping, if necessary | 570 | return result; |
733 | if (orientable.getDropState() == OrientableComponent::DropState::active) | 571 | } |
572 | |||
573 | template <typename Param> | ||
574 | void PonderingSystem::detectCollisionsInDirection( | ||
575 | id_type entity, | ||
576 | CollisionResult& result) | ||
577 | { | ||
578 | // Get map data. | ||
579 | auto& realizable = game_.getEntityManager(). | ||
580 | getComponent<RealizableComponent>( | ||
581 | game_.getSystemManager().getSystem<RealizingSystem>().getSingleton()); | ||
582 | |||
583 | id_type mapEntity = realizable.activeMap; | ||
584 | |||
585 | auto& mappable = game_.getEntityManager(). | ||
586 | getComponent<MappableComponent>(mapEntity); | ||
587 | |||
588 | // Get old location. | ||
589 | auto& transformable = game_.getEntityManager(). | ||
590 | getComponent<TransformableComponent>(entity); | ||
591 | |||
592 | bool boundaryCollision = false; | ||
593 | |||
594 | auto boundaries = Param::MapBoundaries(mappable); | ||
595 | auto it = boundaries.lower_bound(Param::OldAxis(transformable)); | ||
596 | |||
597 | // Find the axis distance of the closest environmental boundary. | ||
598 | for (; | ||
599 | (it != std::end(boundaries)) && | ||
600 | Param::AtLeastInAxisSweep( | ||
601 | it->first, | ||
602 | Param::NewAxis(result, transformable)); | ||
603 | it++) | ||
604 | { | ||
605 | // Check that the boundary is in range for the other axis. | ||
606 | if ((Param::NonAxisNewUpper(result, transformable) > it->second.lower) && | ||
607 | (Param::NonAxisNewLower(result, transformable) < it->second.upper)) | ||
734 | { | 608 | { |
735 | orientable.setDropState(OrientableComponent::DropState::none); | 609 | // We have a collision! |
610 | boundaryCollision = true; | ||
611 | |||
612 | break; | ||
736 | } | 613 | } |
737 | } | 614 | } |
738 | 615 | ||
739 | // Ferry or unferry as necessary | 616 | // Find a list of potential colliders, sorted so that the closest is |
740 | if ((ponderable.type == PonderableComponent::Type::freefalling) && | 617 | // first. |
741 | (ponderable.grounded != oldGrounded)) | 618 | std::vector<id_type> colliders; |
619 | |||
620 | auto entities = game_.getEntityManager().getEntitiesWithComponents< | ||
621 | PonderableComponent, | ||
622 | TransformableComponent>(); | ||
623 | |||
624 | for (id_type collider : entities) | ||
742 | { | 625 | { |
743 | if (ponderable.grounded && | 626 | // Can't collide with self. |
744 | game_.getEntityManager(). | 627 | if (collider == entity) |
745 | hasComponent<PonderableComponent>(result.groundEntity)) | ||
746 | { | 628 | { |
747 | // The body is now being ferried | 629 | continue; |
748 | auto& ferryPonder = game_.getEntityManager(). | 630 | } |
749 | getComponent<PonderableComponent>(result.groundEntity); | ||
750 | 631 | ||
751 | ponderable.ferried = true; | 632 | auto& colliderPonder = game_.getEntityManager(). |
752 | ponderable.ferry = result.groundEntity; | 633 | getComponent<PonderableComponent>(collider); |
753 | 634 | ||
754 | ferryPonder.passengers.insert(entity); | 635 | // Only check objects that are active and collidable. |
755 | } else if (ponderable.ferried) | 636 | if (!colliderPonder.active || !colliderPonder.collidable) |
756 | { | 637 | { |
757 | // The body is no longer being ferried | 638 | continue; |
758 | unferry(entity); | ||
759 | } | 639 | } |
760 | } | ||
761 | |||
762 | // Update a ferry passenger's relative position | ||
763 | if (ponderable.ferried) | ||
764 | { | ||
765 | auto& ferryTrans = game_.getEntityManager(). | ||
766 | getComponent<TransformableComponent>(ponderable.ferry); | ||
767 | 640 | ||
768 | ponderable.relX = transformable.x - ferryTrans.x; | 641 | auto& colliderTrans = game_.getEntityManager(). |
769 | ponderable.relY = transformable.y - ferryTrans.y; | 642 | getComponent<TransformableComponent>(collider); |
643 | |||
644 | // Check if the entity would move into the potential collider, | ||
645 | if (Param::IsPastAxis( | ||
646 | Param::ObjectAxis(colliderTrans), | ||
647 | Param::NewAxis(result, transformable)) && | ||
648 | // that it wasn't already colliding, | ||
649 | !Param::IsPastAxis( | ||
650 | Param::ObjectAxis(colliderTrans), | ||
651 | Param::OldAxis(transformable)) && | ||
652 | // that the position on the non-axis is in range, | ||
653 | (Param::NonAxisOldUpper(colliderTrans) > | ||
654 | Param::NonAxisNewLower(result, transformable)) && | ||
655 | (Param::NonAxisOldLower(colliderTrans) < | ||
656 | Param::NonAxisNewUpper(result, transformable)) && | ||
657 | // and that the collider is not farther away than the environmental | ||
658 | // boundary. | ||
659 | (!boundaryCollision || | ||
660 | Param::AtLeastInAxisSweep( | ||
661 | Param::ObjectAxis(colliderTrans), | ||
662 | it->first))) | ||
663 | { | ||
664 | colliders.push_back(collider); | ||
665 | } | ||
770 | } | 666 | } |
771 | 667 | ||
772 | // Handle ferry passengers | 668 | std::sort( |
773 | std::set<id_type> passengers = ponderable.passengers; | 669 | std::begin(colliders), |
670 | std::end(colliders), | ||
671 | [&] (id_type left, id_type right) { | ||
672 | auto& leftTrans = game_.getEntityManager(). | ||
673 | getComponent<TransformableComponent>(left); | ||
774 | 674 | ||
775 | for (id_type passenger : passengers) | 675 | auto& rightTrans = game_.getEntityManager(). |
776 | { | 676 | getComponent<TransformableComponent>(right); |
777 | tickBody( | ||
778 | passenger, | ||
779 | dt, | ||
780 | entities); | ||
781 | } | ||
782 | 677 | ||
783 | // Move to an adjacent map, if necessary | 678 | return Param::Closer( |
784 | if (result.adjacentlyWarping) | 679 | Param::ObjectAxis(leftTrans), |
680 | Param::ObjectAxis(rightTrans)); | ||
681 | }); | ||
682 | |||
683 | for (id_type collider : colliders) | ||
785 | { | 684 | { |
786 | double warpX = result.newX; | 685 | auto& colliderTrans = game_.getEntityManager(). |
787 | double warpY = result.newY; | 686 | getComponent<TransformableComponent>(collider); |
788 | 687 | ||
789 | switch (result.adjWarpDir) | 688 | // Check if the entity would still move into the potential collider. |
689 | if (!Param::IsPastAxis( | ||
690 | Param::ObjectAxis(colliderTrans), | ||
691 | Param::NewAxis(result, transformable))) | ||
790 | { | 692 | { |
791 | case Direction::left: | 693 | break; |
792 | { | 694 | } |
793 | warpX = GAME_WIDTH + WALL_GAP - transformable.w; | ||
794 | |||
795 | break; | ||
796 | } | ||
797 | |||
798 | case Direction::right: | ||
799 | { | ||
800 | warpX = -WALL_GAP; | ||
801 | 695 | ||
802 | break; | 696 | auto& colliderPonder = game_.getEntityManager(). |
803 | } | 697 | getComponent<PonderableComponent>(collider); |
804 | 698 | ||
805 | case Direction::up: | 699 | processCollision( |
806 | { | 700 | entity, |
807 | warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.h; | 701 | collider, |
702 | Param::Dir, | ||
703 | colliderPonder.colliderType, | ||
704 | Param::ObjectAxis(colliderTrans), | ||
705 | Param::NonAxisOldLower(colliderTrans), | ||
706 | Param::NonAxisOldUpper(colliderTrans), | ||
707 | result); | ||
708 | |||
709 | if (result.stopProcessing) | ||
710 | { | ||
711 | break; | ||
712 | } | ||
713 | } | ||
808 | 714 | ||
809 | break; | 715 | // If movement hasn't been stopped by an intermediary object, and |
810 | } | 716 | // collision checking hasn't been stopped, process the environmental |
717 | // boundaries closest to the entity. | ||
718 | if (!result.stopProcessing && !result.touchedWall && boundaryCollision) | ||
719 | { | ||
720 | double boundaryAxis = it->first; | ||
811 | 721 | ||
812 | case Direction::down: | 722 | for (; |
723 | (it != std::end(boundaries)) && | ||
724 | (it->first == boundaryAxis); | ||
725 | it++) | ||
726 | { | ||
727 | if ((Param::NonAxisNewUpper(result, transformable) > it->second.lower) && | ||
728 | (Param::NonAxisNewLower(result, transformable) < it->second.upper)) | ||
813 | { | 729 | { |
814 | warpY = -WALL_GAP; | 730 | processCollision( |
731 | entity, | ||
732 | mapEntity, | ||
733 | Param::Dir, | ||
734 | it->second.type, | ||
735 | it->first, | ||
736 | it->second.lower, | ||
737 | it->second.upper, | ||
738 | result); | ||
815 | 739 | ||
816 | break; | 740 | if (result.stopProcessing) |
741 | { | ||
742 | break; | ||
743 | } | ||
817 | } | 744 | } |
818 | } | 745 | } |
819 | |||
820 | game_.getSystemManager().getSystem<PlayingSystem>(). | ||
821 | changeMap( | ||
822 | entity, | ||
823 | result.adjWarpMapId, | ||
824 | warpX, | ||
825 | warpY); | ||
826 | } | 746 | } |
827 | } | 747 | } |
828 | 748 | ||
@@ -1012,7 +932,7 @@ void PonderingSystem::processCollision( | |||
1012 | result.newY = axis - transformable.h; | 932 | result.newY = axis - transformable.h; |
1013 | result.groundEntity = collider; | 933 | result.groundEntity = collider; |
1014 | ponderable.velY = 0.0; | 934 | ponderable.velY = 0.0; |
1015 | ponderable.grounded = true; | 935 | result.grounded = true; |
1016 | 936 | ||
1017 | break; | 937 | break; |
1018 | } | 938 | } |
diff --git a/src/systems/pondering.h b/src/systems/pondering.h index eed0d32..abc6db2 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h | |||
@@ -5,6 +5,19 @@ | |||
5 | #include "components/ponderable.h" | 5 | #include "components/ponderable.h" |
6 | #include "direction.h" | 6 | #include "direction.h" |
7 | 7 | ||
8 | struct CollisionResult | ||
9 | { | ||
10 | double newX; | ||
11 | double newY; | ||
12 | bool stopProcessing = false; | ||
13 | bool touchedWall = false; | ||
14 | bool adjacentlyWarping = false; | ||
15 | Direction adjWarpDir; | ||
16 | size_t adjWarpMapId; | ||
17 | bool grounded = false; | ||
18 | EntityManager::id_type groundEntity; | ||
19 | }; | ||
20 | |||
8 | class PonderingSystem : public System { | 21 | class PonderingSystem : public System { |
9 | public: | 22 | public: |
10 | 23 | ||
@@ -34,23 +47,28 @@ public: | |||
34 | 47 | ||
35 | private: | 48 | private: |
36 | 49 | ||
37 | struct CollisionResult | 50 | |
38 | { | ||
39 | double newX; | ||
40 | double newY; | ||
41 | bool stopProcessing = false; | ||
42 | bool touchedWall = false; | ||
43 | bool adjacentlyWarping = false; | ||
44 | Direction adjWarpDir; | ||
45 | size_t adjWarpMapId; | ||
46 | id_type groundEntity; | ||
47 | }; | ||
48 | 51 | ||
49 | void tickBody( | 52 | void tickBody( |
50 | id_type entity, | 53 | id_type entity, |
51 | double dt, | 54 | double dt, |
52 | const std::set<id_type>& entities); | 55 | const std::set<id_type>& entities); |
53 | 56 | ||
57 | CollisionResult moveBody( | ||
58 | id_type entity, | ||
59 | double x, | ||
60 | double y); | ||
61 | |||
62 | CollisionResult detectCollisions( | ||
63 | id_type entity, | ||
64 | double x, | ||
65 | double y); | ||
66 | |||
67 | template <typename Param> | ||
68 | void detectCollisionsInDirection( | ||
69 | id_type entity, | ||
70 | CollisionResult& result); | ||
71 | |||
54 | void processCollision( | 72 | void processCollision( |
55 | id_type entity, | 73 | id_type entity, |
56 | id_type collider, | 74 | id_type collider, |