简体   繁体   中英

pygame platformer collision causes weird spacing issue

I am working on a platformer and have a weird issue with the collision. So far, I have a player and some platforms and the collision does work but the horizontal collision leaves some space between the player and the level and I don't understand why.

Here is the code for the player:

import pygame 
from pygame.math import Vector2

class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((32,64))
        self.image.fill('red')
        self.rect = self.image.get_rect(center = (350,250))
        self.direction = Vector2(0,0)
        self.speed = 8
        self.gravity = 0.8
        self.jump_speed = -16
        self.on_ground = False

    def get_input(self):
        keys = pygame.key.get_pressed()

        if keys[pygame.K_RIGHT]: self.direction.x = 1
        elif keys[pygame.K_LEFT]: self.direction.x = -1
        else: self.direction.x = 0

        if keys[pygame.K_SPACE] and self.on_ground:
            self.jump()
            self.on_ground = False

    def jump(self):
        self.direction.y = self.jump_speed

    def apply_gravity(self):
        self.direction.y += self.gravity
    
    def apply_movement(self):
        # horizontal movement
        self.rect.x += self.direction.x * self.speed

        # vertical movement 
        self.rect.y += self.direction.y

    def update(self):
        # movement methods 
        self.get_input()
        self.apply_gravity()
        self.apply_movement()

and here the code for the level

import pygame, sys
from player import Player


basic = [
'0000000000000',
'0000000000000',
'0010011000010',
'0111000010110',
'0111000110111',
'1111111111111',
'1111111111111',]

class Level:
    def __init__(self):
        self.player = pygame.sprite.GroupSingle(Player())
        self.tiles = pygame.sprite.Group()
        self.setup_level()

    def setup_level(self):
        for row_index, row in enumerate(basic):
            for col_index,cell in enumerate(row):
                if cell == '1':
                    x = col_index * 100
                    y = row_index * 100
                    self.tiles.add(Tile((x,y)))

    def player_collision(self):
        player = self.player.sprite
        player_size = player.rect.size
        
        # vertical collision check 
        future_y = player.rect.top + player.direction.y
        future_player_y = pygame.Rect((player.rect.left,future_y),player_size)

        for sprite in self.tiles.sprites():
            if sprite.rect.colliderect(future_player_y):
                if player.direction.y >= 0:
                    player.on_ground = True
                    player.rect.bottom = sprite.rect.top
                    player.direction.y = 0
                else:
                    player.rect.top = sprite.rect.bottom
                    player.direction.y = 0

        # horizontal collision check
        future_x = self.player.sprite.rect.left + (self.player.sprite.direction.x * self.player.sprite.speed)
        future_player_x = pygame.Rect((future_x,self.player.sprite.rect.top),player_size)

        for sprite in self.tiles.sprites():
            if sprite.rect.colliderect(future_player_x):
                if player.direction.x < 0:
                    player.rect.left = sprite.rect.right
                    player.rect.right += player.speed
                elif player.direction.x > 0:
                    player.rect.right = sprite.rect.left
                    player.rect.right -= player.speed

    def run(self):
        self.tiles.draw(screen)

        self.player.update()
        self.player_collision()
        self.player.draw(screen)

        

class Tile(pygame.sprite.Sprite):
    def __init__(self,pos):
        super().__init__()
        self.image = pygame.Surface((100,100))
        self.image.fill('green')
        self.rect = self.image.get_rect(topleft = pos)


if __name__ == '__main__':
    pygame.init()
    screen = pygame.display.set_mode((1000,640))
    clock = pygame.time.Clock()
    debug_font = pygame.font.Font(None,40)

    level = Level()

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        screen.fill('black')

        level.run()



        pygame.display.update()
        clock.tick(60)

I am using future_player_y because I had some earlier problems with animation frames and detecting whether the player was on the ground or not. I applied the same logic to the horizontal axis but I guess it wouldn't be strictly needed there.

The result looks like this and the collisions do work (kinda) but the red rectangle cannot move any closer to the tile to its left. 在此处输入图片说明

Just for clarity, here is a zoomed in look at the area I am talking about: 在此处输入图片说明

You need to separate the movement in the x and y direction.

Remove the apply_movement method:

class Player(pygame.sprite.Sprite):
    # [...]

    def update(self):
        # movement methods 
        self.get_input()
        self.apply_gravity()
        # self.apply_movement() <--- DELETE

Apply the movement separately before the collision test:

class Level:
    # [...]

    def player_collision(self):
        player = self.player.sprite
        
        # vertical collision check 
        player.rect.y += player.direction.y
        
        for sprite in self.tiles.sprites():
            if sprite.rect.colliderect(player.rect):
                if player.direction.y >= 0:
                    player.on_ground = True
                    player.rect.bottom = sprite.rect.top
                    player.direction.y = 0
                else:
                    player.rect.top = sprite.rect.bottom
                    player.direction.y = 0

        # horizontal collision check
        player.rect.x += player.direction.x * player.speed
        
        for sprite in self.tiles.sprites():
            if sprite.rect.colliderect(player.rect):
                if player.direction.x < 0:
                    player.rect.left = sprite.rect.right
                elif player.direction.x > 0:
                    player.rect.right = sprite.rect.left

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM