I've struggled with collision for a long time, but I've discovered/learned two collision types that can be implemented into just about any game genre.
This is my first time writing a tutorial so please any advice or mistakes a must.
Some background:
PROBLEMS AND SOLUTIONS OF THESE COLLISIONS WILL BE LOCATED AT THE BOTTOM
OBJECT-TO-OBJECT COLLISION
This collision type involves four to eight parameters.
- A first X value (
x1
) - A first Y value (
y1
) - A second X value (
x2
) - A second Y value (
y2
)
Optionally
- A first Width value (
w1
) - A first Height value (
h1
) - A second Width value (
w2
) - A second Height value (
h2
)
As you may of guessed x1, y1
is related to w1, h1
,
and x2, y2
is related to w2, h2
.
-- Collision error found by Soupster function object_to_object(x1, y1, x2, y2, w1, h1, w2, h2) return x1 + w1 >= x2 and y1 + h1 >= y2 and x1 <= x2 + w2 and y1 <= y2 + h2 end |
OBJECT-TO-TILE COLLISION
This collision type involves two to three parameters.
- A X value (
x
) - A Y value (
y
)
Optionally
- A Flag value (
flag
)
function object_to_tile(x, y, flag) local x1 = x / 8 local y1 = y / 8 local x2 = (x + 7) / 8 local y2 = (y + 7) / 8 return fget(mget(x1, y1), flag) or fget(mget(x1, y2), flag) or fget(mget(x2, y2), flag) or fget(mget(x2, y1), flag) end |
What's next?
After you made one of these collisions go into the _update()
function or a function that's connected to the _update()
and make two local variables.
local lastX = object.x local lastY = object.y |
After this make an if
statement and call the collision function and enter the parameters.
if myCollision(/* parameters */) then end |
Once you're done with this all you need to do is just call the object.x
and equal it to the lastX
.
And object.y
is equaled with lastY
.
if myCollision(/* parameters */) then object.x = lastX object.y = lastY end |
Problems and Solutions
If you've made these collisions, you may notice that when colliding with a block, and you continue to hold the key that is putting the player in the direction of the block, try pressing left or right, up or down. You may notice that you're unable to move until you let go of the key putting you into that predicament. This can really mess up flow of the players, so how do we fix it? All you need to do is separate the collision function into two.
So for example:
This is object-to-object collision without the extra parameters:
if myCollision(object.x, lastY, object1.x, object1.y) then object.x = lastX end if myCollision(lastX, object.y, object1.x, object1.y) then object.y = lastY end |
Warning: Do not put lastX or lastY for every parameter that mentions X or Y only do it with the object that has the movement code i.e the player
This is object-to-tile collision:
if myCollision(object.x, lastY) then object.x = lastX end if myCollision(lastX, object.y) then object.y = lastY end |
The top warning does not apply to object-to-tile collsion
I think you made an error in your object to object collision (a.k.a AABB, axis aligned bounding box collision), x2 + w2 >= x2 and y2 + h2 >= y2
will always evaluate to true.
This should work better:
function object_to_object(x1, y1, x2, y2, w1, h1, w2, h2) return x1 + w1 >= x2 and y1 + h1 >= y2 and x1 <= x2 + w2 and y1 <= y2 + h2 end |
Oh sorry you're right, based off further testing somehow the one I typed up also works but that is the more formal way which I will be changing.
This is good enough if your movement is grid-based or if your objects have a maximum speed of 1 pixel. However, most games I've seen don't use those limitations. If you just place the object at its last x and y position on collision without those limitations, then you may run into issues. The main one that comes to mind is a character falling in a platformer, where a maximum fall speed above 1 would stop the object from hitting the ground
If you're going to use that simple of collision detection, I would recommend adding a loop for each axis to move the object 1 pixel at a time (or 1 cell size in the case of grid-based movement that allows more than 1 cell for speed) until the object is actually touching the thing that is in the way.
Also, it'd be good to mention that when doing object to object collisions, the width and height should be 1 less than the width and height of the sprites used for those objects.
There's a couple other common cases that might be difficult with these methods, but those are more in-depth than this tutorial merits I guess. Instead, the last thing I think would be good to mention is that using the map for tile-based collisions isn't always a good idea. That ties the level geometry to the visuals which can often be a hindrance. It also makes it so the game's grid must be a specific size and must use a specific portion of the engine's memory. In those cases using either a 2d table or a flat table with 2d-array indexing to store the relevant information works better.
There are multiple work arounds when using a method such as storing the last position, it works good not just for grid-based games or moving at one pixel per frame. I'm using both of these collision examples in my actual game and they work beautifully. My game also runs at 60 FPS with physics, and is a platformer. These collisions honestly depend on how computationally expensive your program is. I am also completely aware that there are issues with these collision types, but if you're just making a platformer all the way to a ray-caster, you should be completely fine.
[Please log in to post a comment]