Collision response on two axis

Well, I implemented a AABB system of collision detection, and it works fine, but now I need to make it so that the player can move after he collides with an object. Right now, when the player collides with an object, he just gets stuck and you can’t move him. I realize why this is happening, I just don’t know how to solve it because of the algorithm I’m using. Here’s the player movement code:

public void update(Mob mob) {
		if (player) {
			if (pos.getX() < 0)
				pos.setX(0);
			if (pos.getX() > 1216)
				pos.setX(1216);
			if (pos.getY() < 0)
				pos.setY(0);
			if (pos.getY() > 656)
				pos.setY(656);
			if (Keyboard.isKeyDown(Keyboard.KEY_W) && !Collision.isColliding(box, mob.box)) {
				pos.setPosition(pos.getX(), pos.getY() + MOVESPEED);
			}
			if (Keyboard.isKeyDown(Keyboard.KEY_S) && !Collision.isColliding(box, mob.box)) {
				pos.setPosition(pos.getX(), pos.getY() - MOVESPEED);
			}
			if (Keyboard.isKeyDown(Keyboard.KEY_D) && !Collision.isColliding(box, mob.box)) {
				pos.setPosition(pos.getX() + MOVESPEED, pos.getY());
			}
			if (Keyboard.isKeyDown(Keyboard.KEY_A) && !Collision.isColliding(box, mob.box)) {
				pos.setPosition(pos.getX() - MOVESPEED, pos.getY());
			}
		}
	}

And then the collision detection algorithm:

public static boolean isColliding(AABB a, AABB b) {
		if (a.x + a.size < b.x || b.x + b.size < a.x || a.y + a.size < b.y || b.y + b.size < a.y) {
			return false;
		}
		return true;
	}

AABB is just a class that holds the position and size of the mob, nothing special. But, as you can see my code only checks if both axis are colliding or not, but I need to test it its colliding on only one at a time. How can I do this?

You cannot check one axis at a time. If it collides on the X axis, it collides on the Y axis. Below, pink is the area that’s overlapping.

The simplest way to check which side collided first is to check which axis has the biggest overlap (in the above case that would give the result of the bottom). However, that wouldn’t always produce the correct result.

Consider that the green arrow represents the direction of the objects movement. The purple box represents the previous position of the object. The object would be detected as colliding with the bottom, which is incorrect. So how would you solve this problem?

First, you’ll want to do a few checks to minimise further checks.

If the movement vector X is positive, it can only hit the left.
If the movement vector X is negative, it can only hit the right.
If the movement vector Y is positive, it can only hit the bottom (depending on which direction is positive).
If the movement vector Y is negative, it can only hit the top (depending on which direction is positive).

The above will determine which corner to check. However before you do the check (which I’ll explain later) you’ll want to do a few previous checks.

If the (previous X position + objectSize) > tile X position, it can’t hit the left.
If the previous X position < (tile X position + tile size), it can’t hit the right.
If the (previous Y position + objectSize) > tile Y position, it can’t hit the bottom (depending on which direction is positive).
If the previous Y position < (tile X position + tile size), it can’t hit the top (depending on which direction is positive).

So, if it’s still possible that it has hit two sides, you’ll need to check the slope of the vector and compare it to the slope of the corner of the object to the corner of the tile. Here’s a little example,

The light purple line represents the slope of the tile’s corner to the object’s corner and the dark purple line represents the movement vector of the object.

So, how do you calculate the size of the slopes?
It’s simple:

length / height

Just get the length of the vector and divide it by the height of the vector. Do the same with the other vector. When checking the top right corner, if the movement vector is steeper than the corner vector, which is the case with the dark purple line in the above example, then it will have hit the right, else it will have hit the top.

What should you do next?
You’ll need to calculate the displacement and set it to correct position.

Collision can be a pain but once it’s done, it’s a huge step forward. Good luck.

Wait… is simple collision detection like what I’m after seriously that hard to do? I’ve seen other people’s code where they only check one axis as a time, and it didn’t require any sort of slopes or anything. Not to say your way is bad, but it seems overly complicated for the simple thing I’m trying to do. I think what I should be doing is seeing if the “future” position is going to collide with another entity, and if so, don’t move in that direction. This means I’ll never be actually colliding with the tile, and I’ll be free to move on the other axis.

That’s called continuous collision detection (CCD), which I’ve personally never used. The “simplest” way I explained above may in some instances be good enough, however if the object is moving fast enough it will produce inaccurate results. I’ve yet to come across a simpler way to get 100% accurate results than the one I explained using vectors. It may seem difficult to implement but it’s actually pretty easy.

I already wrote this in my other post about collision somewhere.
This is Notch’s game called Minicraft. You can learn a lot from it.
http://www.ludumdare.com/compo/ludum-dare-22/?action=preview&uid=398

int xa = 0, ya = 0;

if(KEY_W.down) ya -= 1;
if(KEY_S.down) ya += 1;
if(KEY_A.down) xa -= 1;
if(KEY_D.down) xa += 1;

float speed = 2;

float xspeed = xa * speed;
float yspeed = ya * speed;

int xs = (int) xspeed;
int ys = (int) yspeed;

if(xs > 0) xs += 1;
if(xs < 0) xs -= 1;
if(ys > 0) ys += 1;
if(ys < 0) ys -= 1;

int checkx = (this.xpos + xs) / level.getTileSize();
int checky = (this.ypos + ys) / level.getTileSize();

if(level.getTile(checkx, ypos).canMove()) {
   xpos += xspeed;
}

if(level.getTile(xpos, checky).canMove()) {
   ypos += yspeed;
}

Going back to the OP’s original code; your ‘getting stuck’ problem is that you’re checking for collision using the object’s present coordinates.
You should be testing for collision with the coordinates that you intend to move into; the object’s future coordinates.

I won’t confuse the matter by touching on how complex proper collision detection & response is to do, as it clearly isn’t applicable to what you’re trying to achieve.

So you’re the OP? :clue:
As I understand it, the code he posted gets the speed of the object and calculates where it will be in the next frame, and if it will overlaps something that it shouldn’t, it won’t change the position. That’s okay in some instances, but it could leave a small gap between the tile and the object.

The method I explained in my previous post is, I believe, the easiest way to get 100% accurate results for collision detection; the only way it could fail is if the object moves further than the size of a tile in one frame, or if it hits the corner at a 45 degree angle, however that’s easy to solve.

No, I’m OP, I don’t know why opiop was responding like that ???

I’m not that great of a coder, and this is really just a simple project, so I’m not going to implement your method because it’s just far too advanced, and I don’t need perfect collision, just something that works easy.


public void update(Mob mob) {
		if (player) {
			if (pos.getX() < 0)
				pos.setX(0);
			if (pos.getX() > 1216)
				pos.setX(1216);
			if (pos.getY() < 0)
				pos.setY(0);
			if (pos.getY() > 656)
				pos.setY(656);
			if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
				fpos.setPosition(fpos.getX(), fpos.getY() + MOVESPEED);
				if (!Collision.isColliding(box, mob.box)) {
					pos.setPosition(fpos.getX(), fpos.getY());
				} else {
					pos.setPosition(pos.getX(), pos.getY());
				}
			}
			if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
				fpos.setPosition(fpos.getX(), fpos.getY() - MOVESPEED);
				if (!Collision.isColliding(box, mob.box)) {
					pos.setPosition(fpos.getX(), fpos.getY());
				} else {
					pos.setPosition(pos.getX(), pos.getY());
				}
			}
			if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
				fpos.setPosition(fpos.getX() + MOVESPEED, fpos.getY());
				if (!Collision.isColliding(box, mob.box)) {
					pos.setPosition(fpos.getX(), fpos.getY());
				} else {
					pos.setPosition(pos.getX(), pos.getY());
				}
			}
			if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
				fpos.setPosition(fpos.getX() - MOVESPEED, fpos.getY());
				if (!Collision.isColliding(box, mob.box)) {
					pos.setPosition(fpos.getX(), fpos.getY());
				} else {
					pos.setPosition(pos.getX(), pos.getY());
				}
			}
		}
	}

So, this code kind of works, except the player can pass through objects if he just sits there and keeps pressing the key down. For instance, say I’m colliding on the left. If I keep pressing ‘A’, the player will eventually move through the tile. I test the AABB with the fpos position, which is supposed to be the future position.

Hey guys, sorry for bumping the topic, but I solved part of my error, but now a new problem arose. So, I figured out how to move even when colliding, but now it seems that I get stuck in the entity I’m performing collision detection on at random times. For instance, sometimes I get stuck in the player when I hit him from above, but other times I don’t. I don’t know why this is happening though as I’m moving the player very slow (2 px), so it can’t be because I move too fast and the game has no time to calculate collisions! Here’s my collision detection code:

public static boolean isColliding(AABB a, AABB b){
		if(a.pos.getX() + a.size >= b.pos.getX() && a.pos.getX() <= b.pos.getX() + b.size && a.pos.getY() + a.size >= b.pos.getY() && a.pos.getY() <= b.pos.getY() + b.size){
			return true;
		}
		return false;
	}

And this is how I check for collisions in my entity class:

public void updatePlayer(Entity enemy) {
		if (isPlayer) {
			if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
				Vector2f fPos = new Vector2f(position.getX(), position.getY() - MOVESPEED);
				box.update(fPos, SIZE);
				if (!Collision.isColliding(box, enemy.box)) {
					position = fPos;
					move(0, -MOVESPEED);
				} else {
					box.update(position, SIZE);
					move(0, 0);
				}
			}
			if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
				Vector2f fPos = new Vector2f(position.getX(), position.getY() + MOVESPEED);
				box.update(fPos, SIZE);
				if (!Collision.isColliding(box, enemy.box)) {
					position = fPos;
					move(0, MOVESPEED);
				} else {
					box.update(position, SIZE);
					move(0, 0);
				}
				
			}
			if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
				Vector2f fPos = new Vector2f(position.getX() - MOVESPEED, position.getY());
				box.update(fPos, SIZE);
				if (!Collision.isColliding(box, enemy.box)) {
					position = fPos;
					move(-MOVESPEED, 0);
				} else {
					box.update(position, SIZE);
					move(0, 0);
				}
			}
			if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
				Vector2f fPos = new Vector2f(position.getX() + MOVESPEED, position.getY());
				box.update(fPos, SIZE);
				if (!Collision.isColliding(box, enemy.box)) {
					position = fPos;
					move(MOVESPEED, 0);
				} else {
					box.update(position, SIZE);
					move(0, 0);
				}
			}
		}
	}

This is my AABB class:

public class AABB {
	
	public Vector2f pos;
	public float size;
	
	public AABB(Vector2f pos, float size){
		this.pos = pos;
		this.size = size;
	}
	
	public void update(Vector2f pos, float size){
		this.pos = pos;
		this.size = size;
	}

}

You’ll notice I create a future position vector (fPos) every time a key is pressed, and then I update the AABB with it so I can perform collisions and see if they will happen. I then revert back to the old position if I don’t collide. The move method is this:

private void move(float x, float y) {
		this.position.setPosition(position.getX() + x, position.getY() + y);
		box.update(position, SIZE);
		glPushMatrix();
		glTranslatef(position.getX(), position.getY(), 0);
		glPopMatrix();
	}

Is this a common problem with AABBs? Why is this happening?

If you’re adding speed to your future location and it is a float, you need to do stuff with it.

float speed = 1.5f;
		if(speed > 0) speed += 1;
		if(speed < 0) speed -= 1;