Added a couple files
This commit is contained in:
@@ -0,0 +1,125 @@
|
|||||||
|
Abstract:
|
||||||
|
|
||||||
|
The goal of this pseudocode is to create a collision system that brings several
|
||||||
|
colliding bodies to a near-contact state (no space between the bodies, but not
|
||||||
|
overlapping), while preserving the motion of said bodies. For simplicity, I've
|
||||||
|
decided to use a square box (BoundingBox, or BBox for short) for the collidable
|
||||||
|
bodies.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
My current game has a tiled-map, where the tiles are arranged on a 2D grid. The
|
||||||
|
tiles each have a flag indicating if they are solid (i.e. collidable) or not. I
|
||||||
|
also have characters walking around on these tile maps, that must not intersect
|
||||||
|
with solid tiles. For stylistic reasons, I want characters moving at an angle
|
||||||
|
to "slide" along these walls, and continue along with their original velocity
|
||||||
|
when they've circumvented the obstacles.
|
||||||
|
|
||||||
|
I'd also like to keep the possibility of non-grid collisions using this logic
|
||||||
|
open (for objects like trees, etc.) if the code allows it.
|
||||||
|
|
||||||
|
Obviously, there are many in depth issues that I will need to take into
|
||||||
|
account when writing this logic, that have been glossed over or omitted in this
|
||||||
|
article.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
```
|
||||||
|
velocity = motion + speed
|
||||||
|
if (collision(position + velocity)) then
|
||||||
|
if (collision(position + {velocity.x, 0})) then
|
||||||
|
velocity.x = 0
|
||||||
|
end
|
||||||
|
if (collision(position + {0, velocity.y})) then
|
||||||
|
velocity.y = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
position = position + velocity
|
||||||
|
```
|
||||||
|
|
||||||
|
This code is a basic outline for a collision system that preserves the object's
|
||||||
|
motion, but it still leaves several pixels of space between the bounding boxes.
|
||||||
|
Notably, it also treats "collision" as an abstract concept, rather than as an
|
||||||
|
event that could happen multiple times per frame.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
```
|
||||||
|
velocity = motion + speed
|
||||||
|
if (collisionSimple(BOXSET, position + velocity)) then
|
||||||
|
velocity.x = collisionX(BOXSET, velocity.x)
|
||||||
|
velocity.y = collisionY(BOXSET, velocity.y)
|
||||||
|
end
|
||||||
|
position = position + velocity
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, collisions are still abstract, but "BOXSET" is defined externally
|
||||||
|
(probably as a set of solid boxes, and their positions). This does require more
|
||||||
|
in depth calculations, as well as three specialized utility functions, but the
|
||||||
|
results might be what I'm looking for.
|
||||||
|
|
||||||
|
If there are any collisions between the player object and the given box set,
|
||||||
|
then collisionX() and collisionY() are called to calculate the new distance
|
||||||
|
that the character will move.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
```
|
||||||
|
bool collisionSimple(BOXSET, newPos):
|
||||||
|
for_each box in BOXSET do
|
||||||
|
if (box.overlap(PLAYER.box + newPos)) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
collisionSimple() first runs through the BOXSET, checking if any of the given
|
||||||
|
bounding boxes would collide with the player object's new position (just
|
||||||
|
pretend PLAYER is accessible). In this case, non-tile bounding boxes can be
|
||||||
|
included as part of BOXSET; they're treated just the same. The optimal outcome
|
||||||
|
is that there are no collisions.
|
||||||
|
|
||||||
|
A possible, but flawed, optimization that could be preformed here is to remove
|
||||||
|
any elements from BOXSET that do not collide with newPos, and let the other
|
||||||
|
utility functions operate only on what remains. However, if there are any
|
||||||
|
collisions, than newPos is not the algorithm's final result, therefore any
|
||||||
|
final result that the algorithm would calculate based on the remaining elements
|
||||||
|
would not have been checked against the removed elements.
|
||||||
|
|
||||||
|
Just something to note.
|
||||||
|
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
```
|
||||||
|
var collisionX(BOXSET, velocityX):
|
||||||
|
var ret = velocityX
|
||||||
|
|
||||||
|
for_each box in BOXSET do
|
||||||
|
if (box.overlap(PLAYER.box + PLAYER.position + {velocityX,0})) then
|
||||||
|
if (velocityX > 0) then
|
||||||
|
ret = min(ret, box.west - PLAYER.position.x)
|
||||||
|
else
|
||||||
|
ret = max(ret, box.east - PLAYER.position.x)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Two things: 1. collisionX() and collisionY() should be identical except for the
|
||||||
|
axis of operation and 2. if a player object is "sliding" along a wall (or
|
||||||
|
stuck), then these functions will be called every frame.
|
||||||
|
|
||||||
|
collisionX() and collisionY() check the sides of the elements in BOXSET, and if
|
||||||
|
there's a box that the player would collide with, given the current distance to
|
||||||
|
move, than the distance is reduced, based on if the character is moving left or
|
||||||
|
right (or up or down).
|
||||||
|
|
||||||
|
An unfortunate bug I can already see is that this logic doesn't check corners;
|
||||||
|
it might be possible to get stuck on a corner of a wall, but if this becomes an
|
||||||
|
issue in my implementation I will update this article with that information,
|
||||||
|
and you can promptly ignore it.
|
||||||
Reference in New Issue
Block a user