In order to fix this problem, I recommend looping in every directionsloop from 0 to the amount of pixels to move in whichever direction you entity is moving towards. On top of that, you need to loop from 0 to HEIGHT_OF_ENTITY[HEIGHT_OF_ENTITY] for horizontal movement and WIDTH_OF_ENTITY for vertical movement, to ensure that the entity does not get stuck inside blocked tiles. This will ensure correctly working collisions in any scenario, and should never fail.
Here is some old code I wrote a few months back. I cleaned it a little bit and documented it so it would be easier to understand. It was working properly if
It's my base collision code, I recall correctlyrewrite this every time I want tile based collisions and it works perfectly.
public bool resolveCollisions() {
if (xa == 0 && ya == 0) return false;
if (xa != 0 && ya != 0) throw new ArgumentException("Can only resolve collisions along one axis at a time");
if (xa != 0) { //horizontal movement
if (xa < 0) { //movement towards the left
for (int i = 0; i > xa; i--) {
for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
Vector2 cVec = new Vector2(Location.X + OffsetX + i, Location.Y + j) / Tile.Size;
Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
Velocity.X = i + 1;
return true; //COLLISION!
}
}
}
} else if (xa > 0) { //movement towards the right
for (int i = 0; i < xa; i++) {
for (int j = OffsetY; j < OffsetY + Height; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
Vector2 cVec = new Vector2(Location.X + OffsetX + Width + i, Location.Y + j) / Tile.Size;
Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
Velocity.X = i; //stop movement!
return true; //COLLISION!
}
}
}
}
return false;
} else if (ya != 0) { //vertical movement
if (ya < 0) { //up movement
for (int i = 0; i > ya; i--) {
for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
Vector2 cVec = new Vector2(Location.X + j, Location.Y + OffsetY + i) / Tile.Size;
Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
Velocity.Y = i + 1;
return true; //COLLISION!
}
}
}
InAir = true; //A boolean which indicts that the entity is not on ground!
} else if (ya > 0) { //down movement
for (int i = 0; i < ya; i++) {
for (int j = OffsetX; j < OffsetX + Width; j++) { //OffsetX and OffsetY is in case you want to adjust the hitbox of your entity a bit
Vector2 cVec = new Vector2((int) (Location.X + j), (int) (Location.Y + OffsetY + Height + i)) / Tile.Size;
Tile t = level.GetTile((int) cVec.X, (int) cVec.Y);
if (t.OnCollide(this)) { //t.OnCollide is the tile's handling when collided with an entity. (ie, damage if spikes etc.)
Velocity.Y = i;
if (InAir) //Is the player in the airs?
OnLand(); //Entity is landing, make sure you set InAir to false inside this method
return true; //COLLISION!
}
}
}
}
return false;
} else {
//sanity check
throw new ArgumentException("Can only resolve collisions along one axis at a time");
}
}
This code was written for a platformer, with gravity. But it can easily be edited to fit the needs of a top-down view game.
OffsetX and OffsetY are variables to help define a better hitbox in conjunction of the Width and Height variables
The tile classes should have a method OnCollide with a parameter of type Entity that will return true if this tile should block passage or false if not. You can also add special handling like damaging the entity if it's spikes or whatever.
I'm not entirely sure why you shouldn't
I'm not entirely sure why you shouldn't check collisions along both x and y axis at the same time but I remember I had problems when doing it that way, so I only check along x and y, separately.You can't check collisions along both x-axis and y axis at-axis both in the same time but I remember I had problems when doing it that way, so I only check along x and ymethod call because we can't return two booleans, separately.duh! (Unless we returned a Tuple of two booleans of course)