0

I'm having a glitch with this simple collision algorithm. I'm doing the rpg game tutorial from codingmadeeasy, and the collision is much alike any algorithm collision I've done in previous platformer style games. I've had this problem with previous games as well, just don't remember the fix. I have a map, it has lots of tiles, some are flagged as solid. their Update method checks for collision with the player. If the tile is solid, it should stop the player and move him to a valid location.

Problem is when I go up or down, stick to the tile, and press a left or right movement key.
For example:

I'm pressing the Down key, I'm on top of the solid tile, and I press to go Left. at that moment, the player is relocated to to the Right of the tile.

This wouldn't happen if I go left or right and press up or down because of the if order in the collision method.

public void Update(GameTime gameTime, ref Player player)
{
    if (State != TileState.Solid) return;
    Rectangle tileRect = 
        new Rectangle((int)Position.X, (int)Position.Y,
            SourceRect.Width, SourceRect.Height);
    Rectangle playerRect = 
        new Rectangle((int)player.Sprite.position.X, (int)player.Sprite.position.Y,
            player.Sprite.SourceRect.Width, player.Sprite.SourceRect.Height);

    HandleCollisionWithTile(player, playerRect, tileRect);
}

/// <summary>
/// Repositions the player at the current position after collision with tile
/// </summary>
/// <param name="player">the player intersecting with the tile</param>
/// <param name="playerRect">the rectangle of the player</param>
/// <param name="tileRect">the rectangle of the tile</param>
private static void HandleCollisionWithTile(Player player, Rectangle playerRect, Rectangle tileRect)
{
    // Make sure there's even collosion
    if (!playerRect.Intersects(tileRect)) return;

    // Left
    if (player.Velocity.X < 0)
        player.Sprite.position.X = tileRect.Right;

    // Right
    else if (player.Velocity.X > 0)
        player.Sprite.position.X = tileRect.Left - player.Sprite.SourceRect.Width;

    // Up
    else if (player.Velocity.Y < 0)
        player.Sprite.position.Y = tileRect.Bottom;

    // Down
    else if (player.Velocity.Y > 0)
        player.Sprite.position.Y = tileRect.Top - player.Sprite.SourceRect.Height;

    // Reset velocity
    player.Velocity = Vector2.Zero;
}

Obviously, If I switch the two first ifs with the last, that would happen if I come from the side and press up or down.

What exactly am I missing?

Giora Guttsait
  • 1,279
  • 15
  • 29

1 Answers1

0

As you've noted, this issue occurs because of the order in which you check the condition for collision. In particular, while visually your player sprite appears to be above a tile, your code is detecting the sprite as intersecting, and since the first check is for horizontal collisions, the player sprite's position is moved to the right of the tile's sprite (above and to the right, actually, so you only adjust the coordinate in the axis of the detected collision).

It seems to me that this exposes a fundamental flaw in the code: while one might argue that having resolved the collision that occurred in the vertical direction, by placing the player sprite at the top of the tile sprite, now the player sprite should not be in a collision state with the tile sprite, instead your code detects this condition as a collision.

Without a better code example, it's hard to know what the best fix might be. But an obvious quick-and-dirty solution would be to ensure that when you resolve a collision, you are not putting the player sprite in a position that intersects with the tile sprite. For example:

private static void HandleCollisionWithTile(Player player, Rectangle playerRect, Rectangle tileRect)
{
    // Make sure there's even collosion
    if (!playerRect.Intersects(tileRect)) return;

    // Left
    if (player.Velocity.X < 0)
        player.Sprite.position.X = tileRect.Right + 1;

    // Right
    else if (player.Velocity.X > 0)
        player.Sprite.position.X = tileRect.Left - player.Sprite.SourceRect.Width - 1;

    // Up
    else if (player.Velocity.Y < 0)
        player.Sprite.position.Y = tileRect.Bottom + 1;

    // Down
    else if (player.Velocity.Y > 0)
        player.Sprite.position.Y = tileRect.Top - player.Sprite.SourceRect.Height - 1;

    // Reset velocity
    player.Velocity = Vector2.Zero;
}

In other words, give yourself a 1 pixel margin around the tile into which the player sprite may not enter.

An alternative would be to "deflate" the tile rectangle (i.e. call Inflate() with negative values) by 1 pixel before checking for the intersection that defines a collision. Then when a collision does occur, the placement will be as before (i.e. without the +1 and -1 adjustments shown above), but that placement will not itself be detected as a collision.

There are many other ways to handle collision detection, but a) without more specifics about your scenario it's hard to know whether and which of those might be better choices, and b) given the apparently simple nature of your current implementation, a relatively simple fix as I've proposed above seems more likely to be warranted and useful.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • It's not it. The bug is when you're having collision, and because you are colliding vertically, and the horizontal checks are done first, it's going to fix the horizontal collision first. The fix should be around knowing which collision is more important. That's what I'm testing out right now, but nothing seems to do it. – Giora Guttsait Oct 09 '15 at 22:33
  • _"knowing which collision is more important"_ -- I don't even know what that means. In any case, in the scenario you describe, **there should be no collision at all**. I.e. it's not a question of prioritizing collisions; your code _should_ be (after the collision in the vertical direction) placing the player sprite in a non-colliding location, above the tile. If the player then moves to the left, why should that then cause another collision? The tile isn't to the left of the player, and so moving in that direction should cause no collision at all. – Peter Duniho Oct 09 '15 at 22:39
  • I got the point of the +1 and -1. It fixed the collision except for the cases the sprite is in a corner of the tile. I'm fixing that now – Giora Guttsait Oct 09 '15 at 23:01