Search code examples
collision-detection2d-games

How to resolve a collision between two rectangles in a 2D grid?


Disclaimer: This is all (PSEUDOCODE)

I have a Tile Map in an array like so

{0,0,0,0,0,0,0,0,0,0,0
 0,0,0,0,0,0,0,0,0,0,0
 0,0,0,0,0,0,0,0,0,0,0}

I then use a for loop to create an individual tile for each element in the array like so

newTile(x,y)

Every tile has the same width and height and none of the tiles are moving.

While the player can move freely throughout the map without any fixed movement.

Now that I have covered some background I can continue with my actual problem. I'm having no issues detecting if there is a collision but with actually solving the collision and determining which side the collision was detected.

My current approach is as follows

function Tiles.collide(self,a,x,y,dt)
  if self:CheckCollide(a.x,a.y) and self.type == "cem" then
    deltaX = a.x - self.x
    deltaY = a.y - self.y
    if(math.abs(deltaX) > math.abs(deltaY))then
      if(deltaX > 0) then
        collText = "Left Wall"
        a.x = a.x + 5
      else
        collText = "Right Wall"
      end
    else
      if(deltaY > 0) then
        collText = "Bottom Wall"
        a.y = a.y + 5
      else
        collText = "North Wall"
      end
    end
  end
end

Then I use what is returned and proceed to actually solve the collision.

 if(checkSide() == rightSide)
     // What now???

The method that returns what side is being intersected doesn't work as intended. I also do not know how to resolve the collision. Which is why I ask

http://makeagif.com/i/S3dPR7

How to resolve a collision between two rectangles in a 2D grid?


Solution

  • Seeing as you already know that a collision has occurred, you don't need to worry about the widths of the player and the tile. You only care which side the player is on, so you could do something like this: (Assuming 0,0 is top left of the canvas)

    deltaX = playerX - tileX
    deltaY = playerY - tileY
    if (abs(deltaX) > abs(deltaY))
        if (deltaX > 0)
            return eastWall
        else
            return westWall
    else
        if (deltaY > 0)
            return southWall
        else
            return northWall
    end
    

    The problem is that although a collision has occurred, you don't know how far the player has moved since the collision (I assume you're stopping every X milliseconds and looking for collisions?). If the player is very close to a corner, it may look like they have collided with one side when they actually collided with the adjacent side. A literal corner case.

    There are solutions to this problem, but they are more complicated and involve calculating the time at which the collision occurred and then looking at the state of the field at the time the collision occurred.