pythonpygamecollision-detectioncollision

pygame.colliderect returning True despite not touching each other


I was tinkering with PyGame, reading articles, and I came across this weird phenomenon. For some reason, pygame.colliderect returns True when the two sprites aren't touching.Code:

import pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
running = True
spriteX = 100
spriteY = 100
spriteVY = 3
clock = pygame.time.Clock()
class Sprite(pygame.sprite.Sprite):
    def __init__(self):
        super(Sprite, self).__init__()
        self.vel = (0, 0)
        self.surf = pygame.Surface((100, 200))
        self.surf.fill((255, 255, 255))
        self.rectangle = self.surf.get_rect()
class Platform(pygame.sprite.Sprite):
    def __init__(self):
        super(Platform, self).__init__()
        self.surf = pygame.Surface((200, 10))
        self.surf.fill((142, 212, 25))
        self.rectangle = self.surf.get_rect()
on_ground = True
while running:
    platform_group = pygame.sprite.Group()
    platform_group.add(Platform())
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    if pygame.key.get_pressed()[pygame.K_SPACE]: spriteY -= 10
    if pygame.key.get_pressed()[pygame.K_d]: spriteX += 10
    if pygame.key.get_pressed()[pygame.K_a]: spriteX -= 10
    print((Platform().rectangle.colliderect(Sprite().rectangle)))
    screen.fill((0, 0, 0))
    if spriteX >= 300:
        spriteX = 300
    if spriteX <= 0:
        spriteX = 0
    if not on_ground:
        spriteY += spriteVY
        spriteVY -= 3
    screen.blit(Sprite().surf, ((spriteX, spriteY)))
    screen.blit(Platform().surf, ((50, 20)))
    pygame.display.flip()
    clock.tick(24)

Solution

  • A pygame.Surface has no position. Hence the position of the pygame.Rect object which is returned by get_rect() is (0, 0).
    Note you have to specify the position when the Surface is blit().

    Specify the position by an keyword argument, when the the rectangle is get from the surface. Create instances of the objects before the main application loop (see Instance Objects) and add them to a pygame.sprite.Group. Each pygame.sprite.Sprite should have the attributes .rect and .image which are used by the method draw() of the group.
    Get ride of the global variables spriteX and spriteY and use the .rect attribute of the sprites.

    For instance:

    import pygame
    pygame.init()
    screen = pygame.display.set_mode((400, 300))
    running = True
    spriteVY = 3
    clock = pygame.time.Clock()
    
    class MySprite(pygame.sprite.Sprite):
        def __init__(self, x, y):
            super().__init__()
            self.vel = (0, 0)
            self.image = pygame.Surface((100, 200))
            self.image.fill((255, 255, 255))
            self.rect = self.image.get_rect(topleft = (x, y))
    
    class Platform(pygame.sprite.Sprite):
        def __init__(self, x, y):
            super().__init__()
            self.image = pygame.Surface((200, 10))
            self.image.fill((142, 212, 25))
            self.rect = self.image.get_rect(topleft = (x, y))
    
    sprite = MySprite(100, 100)
    platform = Platform(50, 20)
    group = pygame.sprite.Group([sprite, platform])
    
    on_ground = True
    while running:
    
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
    
        if pygame.key.get_pressed()[pygame.K_SPACE]: sprite.rect.y -= 10
        if pygame.key.get_pressed()[pygame.K_d]: sprite.rect.x += 10
        if pygame.key.get_pressed()[pygame.K_a]: sprite.rect.x -= 10
    
        if platform.rect.colliderect(sprite.rect):
            print("hit")
    
        if sprite.rect.x >= 300:
            sprite.rect.x = 300
        if sprite.rect.x <= 0:
            sprite.rect.x = 0
        if not on_ground:
            sprite.rect.y += spriteVY
            spriteVY -= 3
    
        screen.fill((0, 0, 0))
        group.draw(screen)
        pygame.display.flip()
        clock.tick(24)