2
\$\begingroup\$

I'm currently trying to handle collisions between entities and a tilemap, but I've hit a problem. My tiles are stored simply as integer tile IDs in a multidimensional array, and I wanted to perform a quick collision check by working out which tile the player is on (or will be on if the new position is valid), by simply dividing the player's position by the tile size to get the x,y of the tile in question. After this, I'd perform a simple SDL_Rect collision check between the tile's rect and the entity's.

The problem of course is that depending on which direction I'm moving in, I may want to add the entity's width/height to the x,y position of the entity when working out which tile I'm on, otherwise the collision would only apply to the top left corner of the entity. I tried to do this myself, but couldn't produce consistent results. I didn't think it would be difficult, but I'm obviously missing something.

In my situation, my entity sprites are 32x32, while tiles are 16x16. Is it possible that using tiles that are smaller than my entity is making it more of a problem than it might otherwise be?

Thanks in advance for any help you can offer in suggesting a working solution.

\$\endgroup\$
  • \$\begingroup\$ Its easy when doing this sort of thing to make a mistake due to it being fairly complex. Assuming you haven't already done this, I'd make a function that tells you whether a passed in rectangle overlaps non passable tiles. Then, when doing collision detection, pass the players new (proposed) position + size to this function to decide whether or not to allow the movement. It might sound ridiculous, but dividing complex processes into simpler ones is a good way to solve mysterious problems and get code working. \$\endgroup\$ – Alan Wolfe May 31 '15 at 5:30
  • \$\begingroup\$ Thanks for your comment. This is what I was trying, but to no avail. The problem is that the player could be overlapping one of several tiles, and I'm not sure how to determine which ones they are so I can check collision on them all. \$\endgroup\$ – Triforcer May 31 '15 at 11:09
  • 1
    \$\begingroup\$ For that, you just get the player's min and max x and y and convert that to tile min and max x and y. You then loop across x, within a loop looping across y to test each of those tiles (: \$\endgroup\$ – Alan Wolfe May 31 '15 at 14:56
2
\$\begingroup\$

Here's a small pseudocode example of something kinda similar to the system I'm using for my game. Basically the important trick here is to check both the topleft of your player, and the bottomright of your player, which is enough as your player is rectangular and of the fitting size. If the player character would be bigger than two tiles, you would have to check more tiles.

class Player
{
    Vector2 Position, Velocity, Acceleration;
    Vector2 PlayerSize;

    void Update(time)
    {
        // Calculate the new position for the player.
        Velocity = Velocity + (Acceleration * time);
        Vector2 newPosition = Position + (Velocity * time);

        // Check the dimensions separately. 
        // Maybe even let the functions return a valid position?
        newPosition = CheckCollision(TVector2(0, newPosition.Y))
        newPosition = CheckCollision(TVector2(newPosition.X, 0))

        // Now we should be able to safely move to the new position.
        Position = newPosition;
    }   

    Vector2 CheckCollision(position)
    {
        // Calculate the tile coordinates for the player. 
        // We need to check both the player's min position (topleft), 
        // and player's max position (bottomright).
        // I'm assuming that Vector2 / Vector2 performs component-wise division.
        Vector2 minPos = position / TileSize;
        Vector2 maxPos = (position + PlayerSize) / TileSize;

        // Get the tile values here. 
        // This code assumes that '0' will indicate an empty tile.
        bool tileAtMin = TileMap[minPos.Y][minPos.X];
        bool tileAtMax = TileMap[maxPos.Y][maxPos.X];

        // Then we need to check if the player collides.
        if (tileAtMin OR tileAtMax)
        {
            // Insert your own collision resolving here, 
            // as that is way out of the scope of this question.

            return Vector2() // Some fixed position.
        }

        return position;
    }
}

Sorry for the complicated (?) example, but that should outline clearly what the important trick here actually is. This might not be the most effecient way, but already the fact that the level "geometry" is laid out in a grid makes the code a lot faster and simpler, as you don't need to actually go to look for collisions, you can just check a couple of positions.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.