2D Tile Based Collision Detection
- by MrPlosion1243
There are a lot of topics about this and it seems each one addresses a different problem, this topic does the same.
I was looking into tile collision detection and found this where David Gouveia explains a great way to get around the person's problem by separating the two axis. So I implemented the solution and it all worked perfectly from all the testes I through at it. Then I implemented more advanced platforming physics and the collision detection broke down. Unfortunately I have not been able to get it to work again which is where you guys come in :)!
I will present the code first:
public void Update(GameTime gameTime) {
if(Input.GetKeyDown(Keys.A)) {
velocity.X -= moveAcceleration;
}
else if(Input.GetKeyDown(Keys.D)) {
velocity.X += moveAcceleration;
}
if(Input.GetKeyDown(Keys.Space)) {
if((onGround && isPressable) || (!onGround && airTime <= maxAirTime && isPressable)) {
onGround = false;
airTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
velocity.Y = initialJumpVelocity * (1.0f - (float)Math.Pow(airTime / maxAirTime, Math.PI));
}
}
else if(Input.GetKeyReleased(Keys.Space)) {
isPressable = false;
}
if(onGround) {
velocity.X *= groundDrag;
velocity.Y = 0.0f;
}
else {
velocity.X *= airDrag;
velocity.Y += gravityAcceleration;
}
velocity.Y = MathHelper.Clamp(velocity.Y, -maxFallSpeed, maxFallSpeed);
velocity.X = MathHelper.Clamp(velocity.X, -maxMoveSpeed, maxMoveSpeed);
position += velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;
position = new Vector2((float)Math.Round(position.X), (float)Math.Round(position.Y));
if(Math.Round(velocity.X) != 0.0f) {
HandleCollisions2(Direction.Horizontal);
}
if(Math.Round(velocity.Y) != 0.0f) {
HandleCollisions2(Direction.Vertical);
}
}
private void HandleCollisions2(Direction direction) {
int topTile = (int)Math.Floor((float)Bounds.Top / Tile.PixelTileSize);
int bottomTile = (int)Math.Ceiling((float)Bounds.Bottom / Tile.PixelTileSize) - 1;
int leftTile = (int)Math.Floor((float)Bounds.Left / Tile.PixelTileSize);
int rightTile = (int)Math.Ceiling((float)Bounds.Right / Tile.PixelTileSize) - 1;
for(int x = leftTile; x <= rightTile; x++) {
for(int y = topTile; y <= bottomTile; y++) {
Rectangle tileBounds = new Rectangle(x * Tile.PixelTileSize, y * Tile.PixelTileSize, Tile.PixelTileSize, Tile.PixelTileSize);
Vector2 depth;
if(Tile.IsSolid(x, y) && Intersects(tileBounds, direction, out depth)) {
if(direction == Direction.Horizontal) {
position.X += depth.X;
}
else {
onGround = true;
isPressable = true;
airTime = 0.0f;
position.Y += depth.Y;
}
}
}
}
}
From the code you can see when velocity.X is not equal to zero the HandleCollisions() Method is called along the horizontal axis and likewise for the vertical axis. When velocity.X is not equal to zero and velocity.Y is equal to zero it works fine. When velocity.Y is not equal to zero and velocity.X is equal to zero everything also works fine. However when both axis are not equal to zero that's when it doesn't work and I don't know why. I basically teleport to the left side of a tile when both axis are not equal to zero and there is a air block next to me.
Hopefully someone can see the problem with this because I sure don't as far as I'm aware nothing has even changed from what I'm doing to what the linked post's solution is doing.
Thanks.