0
\$\begingroup\$

Im making a "catcher" style game in python, where the player has to collect "Gems", and avoid the "Spikes". However the games collision detection is off by quite a lot. When I run the game, everything is going well. The player is moving, the objects are falling from the correct positions. But when the collision is detected it is detected early. How can I improve the collision detection so it can detect more accurate collisions with either of the objects? Here is part of the code including the Player, Gem, and Spike class. The full code, with graphics is in the github link below. Any help is appriciated!

class Player(pygame.sprite.Sprite):
    
    def __init__(self):
        global game_state
        
        # super().__init__()

        self.player_surf = pygame.image.load('game graphics/player_stand.png').convert_alpha()
        #scaled player image
        self.player_surf = pygame.transform.scale(self.player_surf,(500,500))
        self.rect = self.player_surf.get_rect(midbottom = (x,y))
        self.x_speed = 5
        self.health = 5
        self.score = 0

class Gem(pygame.sprite.Sprite):
    
    def __init__(self):
        self.x = random.randrange(0, screen_w)
        self.y = -20
        self.vel = 4
        self.gem_image = [pygame.image.load('gems & spikes/greenGem.png'),
                          pygame.image.load('gems & spikes/redGem.png'),
                          pygame.image.load('gems & spikes/blueGem.png'),
                          pygame.image.load('gems & spikes/yellowGem.png')]
        self.gem_image = [pygame.transform.scale(img, (90, 90)) for img in self.gem_image]
        self.counter = 0
        self.rect = self.gem_image[counter].get_rect(midbottom=(self.x, self.y))
        self.update_y()
        
    def update_y(self):
        self.y += self.vel
        self.rect = self.gem_image[self.counter].get_rect(midbottom=(self.x, self.y))
        self.collide = self.rect.colliderect(player.rect)
        if self.y > screen_h:
            self.y = -20
            self.x = random.randrange(0, screen_w)
            self.counter = (self.counter + 3) % len(self.gem_image)
        if self.collide:
            self.y = 0
            self.x = random.randrange(0, screen_w)
            player.score += 1
            
class Spike(pygame.sprite.Sprite):
    
    def __init__(self):
        self.x = random.randrange(0,screen_w)
        self.y = -20
        self.vel = 2
        self.spike_image = pygame.image.load('gems & spikes/spike.png')
        self.spike_image = pygame.transform.scale(self.spike_image,(120,120))
        self.rect = self.spike_image.get_rect(midbottom = (x,y))
        self.update_y()
        
    def update_y(self):
        self.y += self.vel
        self.rect = self.spike_image.get_rect(midbottom=(self.x, self.y))
        self.collide = self.rect.colliderect(player.rect)
        if self.y > screen_h:
            self.y = -20
            self.x = random.randrange(0, screen_w)
        if self.collide:
            player.health -= 1
            self.y = -20
            self.x = random.randrange(0, screen_w)

Github Repository: Cave-Of-Doom-

Here is a video of the problem on youtube: https://youtu.be/J5sGUwhVh4s

\$\endgroup\$
0

2 Answers 2

0
\$\begingroup\$

Separate the collision rectangles from the drawn sprites rectangles.

This is a common practise in games.

Reduce the size of the Spike collision by 2 or 3 pixels.

The following code would be an example for the reduced spike collision:

   self.collide = self.spike_image.get_rect(midbottom=(self.x + 1, self.y), width =  self.spike_image.width - 2, height = self.spike_image.height - 2)

Since the rectangle is defined from midbottom, the x needs to be adjusted by (+1) to center the new width, and the bottom stays the same with reduced height (-2).

\$\endgroup\$
3
  • \$\begingroup\$ Thanks for the help @Strom. For separating the collision rectangle from the drawn sprite rectangle to you mean, removing the .rect part in self.collide = self.rect.colliderect(player.rect)? Or moving it to a different location? And for reducing the size of the spike collision, is that the same as just reducing the size of the image? Ex: self.spike_image = pygame.transform.scale(self.spike_image,(90,90)). This is my first time doing this, so any clarification will help. Thank you. \$\endgroup\$ Feb 6 at 1:24
  • \$\begingroup\$ Thanks again for your help @Strom, my current problem is when I try to change the height & or width of the rect, pygame spits out the error message, 'Spike' object has no attribute 'width'/'height' I tried making a width or height attribute, but I don't know how to assign it to the rect size. I will try some other approaches, and do some reading on the problem. \$\endgroup\$ Feb 6 at 21:43
  • \$\begingroup\$ This has become a general Python issue. (creation of class members). That is beyond my Python skills. I could suggest inverting the translation to the player rect? \$\endgroup\$
    – user122973
    Feb 19 at 0:43
-1
\$\begingroup\$

Solution was found through asking the question on reddit. Here is the thread,

https://www.reddit.com/r/pygame/comments/10wis5l/how_to_change_the_size_of_a_rect_for_a_collision/

\$\endgroup\$
1
  • 5
    \$\begingroup\$ This answer won't help a future reader if the link rots, or reddit is inaccessible from their location. Please edit this answer to at least briefly summarize the solution itself. \$\endgroup\$
    – DMGregory
    Feb 21 at 5:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .