Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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.

share|improve this question

2 Answers 2

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
share|improve this answer
    
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
1  
@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
1  
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
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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