2
\$\begingroup\$

I have been making a 2d platformer using Python with PyGame. It has all be going good, but I have stumbled accross many problems with collision detection. Here they are:

  1. You can't go through objects that have the exact space for you to go through
  2. You can go through the ceiling (very weird)

The code I have is very big and I think that, since it involves multiple classes, I should post it on GitHub, so here is the link to my repository: https://github.com/pta2002/SuperBros.

But just for reference, here is my colliion detection method:

def detectCollisions(self, x1, y1, w1, h1, x2, y2, w2, h2):

    if x2 + w2 >= x1 >= x2 and y2 + h2 >= y1 >= y2:
        return 1

    elif x2 + w2 >= x1 + w1 >= x2 and y2 + h2 >= y1 >= y2:
        return 2

    elif x2 + w2 >= x1 >= x2 and y2 + h2 >= y1 + h1 >= y2:
        return 3

    elif x2 + w2 >= x1 + w1 >= x2 and y2 + h2 >= y1 + h1 >= y2:
        return 4

    else:
        return 0

and here is my update method:

def update(self, gravity, block_list):
    if self.velocity < 0:
        self.falling = True

    collision = 0
    blockX, blockY = 0, 0
    self.canMoveRight = True
    self.canMoveLeft = True

    for block in block_list:
        collision = self.detectCollisions(self.x, self.y, self.width, self.height,
         block.x, block.y, block.width, block.height)

        if not collision == 0:
            blockX = block.x
            blockY = block.y
            break

    if not collision == 0:
        if self.falling:
            self.falling = False
            self.onGround = True
            self.velocity = 0
            self.y = block.y - self.height
        else:
            if collision == 2:
                self.canMoveRight = False

            if collision == 1:
                self.canMoveLeft = False
    else:
        self.falling = True
        self.onGround = False

    if not self.onGround:
        self.velocity += gravity

    self.y -= self.velocity
    if (self.canMoveRight and self.xvel > 0) or (self.canMoveLeft and self.xvel < 0):
        self.x += self.xvel

    if self.jumping:
        if self.onGround:
            self.velocity = 8
            self.onGround = False

    if self.cur_image >= self.num_images - 1:
        self.cur_image = 0
    else:
        self.cur_image += 1
\$\endgroup\$
4
\$\begingroup\$

The Pygame Rect already comes with a few collision detection functions that may just do what you need:

  • pygame.Rect.contains: test if one rectangle is inside another
  • pygame.Rect.collidepoint: test if a point is inside a rectangle
  • pygame.Rect.colliderect: test if two rectangles overlap
  • pygame.Rect.collidelist: test if one rectangle in a list intersects
  • pygame.Rect.collidelistall: test if all rectangles in a list intersect
  • pygame.Rect.collidedict: test if one rectangle in a dictionary intersects
  • pygame.Rect.collidedictall: test if all rectangles in a dictionary intersect

Instead of having a detectCollisionsmethod that takes x1, y1, w1, h1, x2, y2, w2 and h2, at the sprite initialization you could set its Rect, move and blit the sprite using this same Rect as argument, and check for collisions with something as simple as:

sprite1.rect.colliderect(sprite2.rect)

From a A Newbie Guide to pygame:

Rects are your friends.

[...] I have to admit my favorite part of pygame is the lowly Rect class. A rect is simply a rectangle – defined only by the position of its top left corner, its width, and its height. Many pygame functions take rects as arguments, and they also take ‘rectstyles’, a sequence that has the same values as a rect. So if I need a rectangle that defines the area between 10, 20 and 40, 50, I can do any of the following:

rect = pygame.Rect(10, 20, 30, 30)
rect = pygame.Rect((10, 20, 30, 30))
rect = pygame.Rect((10, 20), (30, 30))
rect = (10, 20, 30, 30)
rect = ((10, 20, 30, 30))

If you use any of the first three versions, however, you get access to Rect’s utility functions. These include functions to move, shrink and inflate rects, find the union of two rects, and a variety of collision-detection functions.

For example, suppose I’d like to get a list of all the sprites that contain a point (x, y) – maybe the player clicked there, or maybe that’s the current location of a bullet. It’s simple if each sprite has a .rect member – I just do:

sprites_clicked = [sprite for sprite in all_my_sprites_list
                   if sprite.rect.collidepoint(x, y)]

Rects have no other relation to surfaces or graphics functions, other than the fact that you can use them as arguments. You can also use them in places that have nothing to do with graphics, but still need to be defined as rectangles. Every project I discover a few new places to use rects where I never thought I’d need them.

So unless you have a specific reason not to rely on Pygame Rect and its functions, my suggestion would be: don't reinvent the wheel, stick with it.

They might even make use of some internal c code that provides better performance then implementing a pure python approach, although I'm not sure about this specific point.

\$\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.