# Why is my collision resolution failing on the corners?

I'm tearing my hair out trying to work out how to handle my collision resolution at the corners of the rectangles that I'm colliding with.

The code that I'm working with is:

class Player(object):
def move(self):
# Calculate velocity based on gravity, etc
# ...

# Check collisions
for obj in self.controller.collidable():
sides = self.get_intersect(obj)

if sides & TOP:
self.mover.vy = 0
self.mover.y = obj.coord.y + obj.size.y
elif sides & BOTTOM:
self.mover.vy = 0
self.mover.y = obj.coord.y - self.size.y
elif sides & LEFT:
self.mover.vx = 0
self.mover.x = obj.coord.x - self.size.x
elif sides & RIGHT:
self.mover.vx = 0
self.mover.x = obj.coord.x + obj.size.x

self.coord.x = self.mover.x
self.coord.y = self.mover.y
self.velocity.x = self.mover.vx
self.velocity.y = self.mover.vy

def is_intersecting(self, other):
return (
(
self.coord.x + self.size.x > other.coord.x
and
self.coord.x < other.coord.x + other.size.x
)
and
(
self.coord.y + self.size.y > other.coord.y
and
self.coord.y < other.coord.y + other.size.y
)
)

def get_intersect(self, other):
if not self.is_intersecting(other):
return 0

pos = 0

# Are we overlapping the bottom edge?
if self.coord.y < other.coord.y < (self.coord.y + self.size.y) > other.coord.y:
pos |= BOTTOM
# Are we overlapping the top edge?
if self.coord.y < (other.coord.y + other.size.y) < (self.coord.y + self.size.y):
pos |= TOP

# Are we overlapping the left edge?
if self.coord.x < other.coord.x < (self.coord.x + self.size.x):
pos |= LEFT
# Are we overlapping the right edge?
if self.coord.x < (other.coord.x + other.size.x) < (self.coord.x + self.size.x):
pos |= RIGHT

if pos == 0:
pos = INSIDE

return pos


The problem I'm experiencing is during vertical movement. If you push into a wall and jump, as you reach the top corner, it will push you vertically. This is due to the sides & TOP being the first check. Moving the horizontal checks first fixes this when jumping up, but then falling onto a corner moves the player horizontally.

I've tried doing checking based on what the horizontal/vertical velocities are, but this falls apart, since the vertical velocity is always non-zero due to gravity (and is corrected by this check if standing on a platform). Any other answers I've found fail to take this into account.

Maybe this:

       elif sides & LEFT:


Should actually be this:

       if sides & LEFT:


Otherwise the left/right branches are never visited when there is a top/bottom collision.

The first part of the get_intersect tests also seem incorrect. This is what would make sense to me:

    # Are we overlapping the bottom edge?
if self.coord.y < (other.coord.y + other.size.y) < (self.coord.y + self.size.y):
pos |= BOTTOM
# Are we overlapping the top edge?
if self.coord.y < other.coord.y < (self.coord.y + self.size.y):
pos |= TOP

• This actually exacerbates the problem, as it makes it jump to the corner when falling from above too. – Ryan McCue Dec 20 '12 at 13:07
• @RyanMcCue I added another note about a piece of code that seemed wrong; I don't know whether it'll fix the issues but it's not worth discussing an algorithm if the implementation has bugs. – sam hocevar Dec 20 '12 at 13:57
• These positions are based on a coordinate system with the origin at the bottom left, so self.coord.y is the bottom of the player and other.coord.y is the bottom of the object it's intersecting with. self.coord.y < other.coord.y < (self.coord.y + self.size.y) is checking that the bottom edge is below the bottom of the other object, and also checking that the top edge is above the bottom (so it's intersecting the bottom edge). Similar for the top edge. Your change would flip the coordinate system for the objects, I think? – Ryan McCue Dec 20 '12 at 15:02
• Ah, you're right. Sorry, I'm glad you found a solution because I think I wasted your time here (but still the elif should really be an if). – sam hocevar Dec 20 '12 at 15:08
• All good! You hinted me in the right sort of direction, but more importantly got me thinking about it :) I agree that the elif should be an if, but that causes problems when falling from above. I'm trying to think through why that happens more, but in the mean time, it works with the elif so I'm tempted to leave it there. – Ryan McCue Dec 20 '12 at 15:13

I managed to fix this by using the qualifying the top and bottom checks with statements checking for vertical movement:

if self.mover.vy <= 0 and sides & primatives.TOP:
self.mover.vy = 0
self.mover.y = obj.coord.y + obj.size.y
elif self.mover.vy >= 0 and sides & primatives.BOTTOM:
self.mover.vy = 0
self.mover.y = obj.coord.y - self.size.y