Okay. So let’s strip out slopes, inertia gravity, etc, and work with just a bounding box, a central point, and some squarish tiles, and only worry about moving to the right.Now that is a sweet bit of msPaint if I do say so myself.
So here’s a tiny tile map, only 5x4 tiles.Each tile has a width and height of 1. Meaning anything where x >= 0 and y >= 0 is in tile [0, 0].
The player’s current position is a point, noted by where the dark green diamond roughly in his center.
He has a bounding box to define how ‘large’ he is for world physics. To make it simple, let’s say he’s 1.5 blocks tall, and 1 block wide. The bounds are relative to the entity’s true ‘position:” so let’s say:
(left = -.5) (right = .5), (top = .75), and (bottom = -.75).
His current position is: (1, 1.75). That way he’s effectively on the ground, even though we’re not fiddling with gravity at the moment.
Now for actual movement.
We’ve got a velocity of +3 blocks. Normally, depending on update rate, we wouldn’t be moving all that at once, but let’s do it in 1 fps. So delta X is +3.
since ( dx > 0) we know we’re moving right.
First I find my box in real space. My right edge should be my X position, plus my right Bound: 1 + .5 = 1.5. Likewise, left bound is x + leftBound: 1 + (-.5) = .5. Top and bottom follow suit so I get:
(left = .5) (right = 1.5), (top = 2.5), and (bottom = 1).
Now to figure out where I’m trying to get to: destination. Since we’re moving right, his is the move-box’s right edge + delta 1.5 + 3 = 4.5.
We now effectively have a second box: a box of the space we’re moving through. Left = (movebox.right), right = (destination), top = (movebox.top), and bottom (movebox.bottom). Now it’s just a matter to iterate through these tiles and see if there are any issues when move right through them.
Now, looking at that new box, it’s pretty easy to tell which tiles we have to try and move through to get to our destination.
I don’t just ‘move through’ the tiles. I actually ask the tiles a question: “Assuming I have a bounding box this size, do I hit anything if I try to move to here.” Tiles are dumb. They just link to a rule (solid, sloped, empty, etc) They don’t even keep track of where they are. in a real world sense.
public CollisionObject testRight(left, top, right, bottom, to, tileX, tileY)
left, top, right, bottom are the pauper’s box, to is the destination, tileX and tileY are the tile’s coordinates.
This test returns a collisionObject. This sucker holds all the useful information like “was there a hit” if so “what type” and x and y coordinates of said hit, etc.
Iterate through each of the colums of tiles, going from left to right (since if we get stopped early, there’s no point testing all the way to the end).
If the returned object says, no hit, all’s good. I don’t even store it. If it is a hit, I put it into a list, ordered by which ever’s hit at is closest (in this case, the one with the smallest X).
So, what’s going to happen here?
well, first I test against 1,1 and 1,2. Any issues moving through you to 4.5? my feet are at y=1 and y =2.5. Both tests say no collisions. On to the next column:
2,1 and 2,2. Clear here too.
3,1 and 3,1: ah, here something new is happening. We have some walls.
3,1 is fully solid. It knows that if you’re trying to move through it, you are going to hit. But what is the math behind it.
It’s fully solid, so effectively it has a bounding box of 0,0,1,1 locally. It knows it’s tileX and tileY (because it was passed in the test) of 3 and 1. So it’s real world bounds is (x1 =3, y1 = 1, x2 = 4, y2 = 2). It’s left edge is 3, and the destination is 4.5! Hit! The returned collisionObject is set to true, hit type is ‘wall’, hitX is 3.
Now we’re going to test against tile 3,2 as well, and it’s going to count a hit as well, but at hitX = 3.5. When collecting up the collisions, 3,1 is closer, so while I would have hit it, the lower tile blocks me from ever having struck it.
I have a collision at X=3. That means I can only move up to that. My resulting position will be hitX - rightBound. x = 2.5. That’s how far I get to move.
I also have all the information about which tile I hit, how fast I was going, where exactly I hit on it, etc) to do whatever else I want to do. I could play a sound effect, set my velocity to 0, spray some particles from the hit location. Sky’s the limit on how responsive you want that collision to be.
Also, and just as importantly, you don’t throw any false hits from your potential hit in 3,2, or any of the potential hits behind it.
The tiles themselves don’t do an action reflexively when you ‘hit’ them. You figure out what ‘will happen’ then determine what ‘will happen first’, then respond accordingly.
And to bounce back to talking about your ‘8 possible results’ there’s really just 2. You either hit moving this direction, or you don’t. It doesn’t matter if you stub your toe not jumping high enough over a tile, or catch your head going through too small of a hole, or smack bodily into a wall. If you catch on something moving horizontally, your horizontal movement is cut short.