Search code examples
pythonpygamegame-physicscollisionpygame-surface

Collision on air


I'm making a platform game and I've got collisions working, however with the platforms there seems to be an extra cube area on the left side which the player can walk on. I can't seem to figure out how to remove it. Below is some of the code I believe is the cause of it however I'm not sure exactly. This here is an image of what it looks like. I've trimmed down the image for the platforms etc but the problem persists.


background_image = pygame.image.load("JungleBackground.png")
done = False
clock = pygame.time.Clock()
black = ( 0, 0, 0)
white = ( 255, 255, 255)
x = 300
y = 88

class Character(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((100,100))
        self.image.set_colorkey(black)

        self.rect = self.image.get_rect(center=(50, 300))
        self.rect.x = 50
        self.rect.y = 300
        self.pos = vec(50, 300)
        self.vel = vec(0,0)
        self.acc = vec(0,0)

        self.image.blit(pygame.image.load("TheoHillsS.png"),(0,0))

    def characterJump(self):
        self.rect.y += 1
        hits = pygame.sprite.spritecollide(self, platforms, False)
        self.rect.y -= 1
        if hits:
            self.vel.y = -13
            
    def update(self):
        self.acc = vec(0, 0.5)
        keys = pygame.key.get_pressed()
        if keys[pygame.K_a]:
            self.acc.x = -PLAYER_ACC
        if keys[pygame.K_d]:
            self.acc.x = PLAYER_ACC

        # apply friction
        self.vel.x *= PLAYER_FRICTION
        self.vel += self.acc
        self.pos += self.vel

        self.rect.midbottom = self.pos



class Platform(pygame.sprite.Sprite):
    def __init__(self, x, y, w, h):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((100, 88))
        self.image.fill(black)
        self.image = pygame.image.load("Platform1.png")
        self.rect = self.image.get_rect(topleft=(x, y))
        
        

all_sprites = pygame.sprite.Group()
platforms = pygame.sprite.Group()

character = Character()
all_sprites.add(character)

p1 = Platform(-80, 350, WIDTH - 400, HEIGHT - 10)
p2 = Platform(175, 220, WIDTH - 400, HEIGHT - 10)
p3 = Platform(500, 350, WIDTH - 400, HEIGHT - 10)
all_sprites.add(p1, p2, p3)
platforms.add(p1, p2, p3)


# Main Game

running = True
while running:
    clock.tick(FPS)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                character.characterJump()
    
    all_sprites.update()
    
    hits = pygame.sprite.spritecollide(character, platforms, False)
    for platform in hits:
        if character.vel.y > 0:
            character.rect.bottom = platform.rect.top
            character.vel.y = 0
        elif character.vel.y < 0:
            character.rect.top = platform.rect.bottom
            character.vel.y = 3
            
        character.pos.y = character.rect.bottom

    
    screen.blit(background_image,[0,0])
    all_sprites.draw(screen)
    pygame.display.flip()

game_intro()
game_loop()
pygame.quit()
quit()

Solution

  • The collision is tested against the bounding rectangle of the image, not the area drawn on the image. Make sure the platforms and player fill almost the entire area of the pygame.Surface.
    Instead of drawing the player on a transparent Surface that is much larger than the player, use the Surface created by pygame.image.load directly:

    class Character(pygame.sprite.Sprite):
        def __init__(self):
            pygame.sprite.Sprite.__init__(self)
    
            self.image = pygame.image.load("TheoHillsS.png")
            self.rect = self.image.get_rect(topleft = (50, 300))
            
            self.pos = vec(50, 300)
            self.vel = vec(0,0)
            self.acc = vec(0,0)
    
        # [...]