pygame sprite wall collision

2019-01-04 15:11发布

I am working on a platform game in python and pygame. The entire code can be found at "https://github.com/C-Kimber/FBLA_Game". The issue I am having is with the collision between the player sprite and wall sprites, specifically the corners. When the player is pressing a x movement key and they jump, the player either does not move, or gets stuck. Here is the collision sample:

def wallCollisions(self):

    block_hit_list = pygame.sprite.spritecollide(self, self.walls, False)

    for block in block_hit_list:


        if self.rect.bottom >= block.rect.top and self.rect.bottom <= block.rect.top + 15:  # Moving down; Hit the top side of the wall
            if self.rect.right > block.rect.left:
                self.rect.bottom = block.rect.top
                self.yvel = 0
                self.onGround = True
                self.jumps = 1
        elif self.rect.top <= block.rect.bottom and self.rect.top >= block.rect.bottom - 15:  # Moving up; Hit the bottom side of the wall
            self.rect.top = block.rect.bottom
            self.yvel = 0
        if self.rect.right >= block.rect.left and self.rect.right <= block.rect.left + 15:  # Moving right; Hit the left side of the wall
            if self.rect.bottom > block.rect.top+15:
                self.rect.right = block.rect.left#+1
                self.xvel = 0
        elif self.rect.left <= block.rect.right and self.rect.left >= block.rect.right - 15:  # Moving left; Hit the right side of the wall
            self.rect.left = block.rect.right#-1
            self.xvel = 0 = block.rect.right#-1
            self.xvel = 0

I've included images on what is happening and what I want. enter image description here

I have attempted other methods, such as using velocity as determining factos for collision, but this is what is working the best so far. If you could provide a solution it would be greatly appreciated.

3条回答
2楼-- · 2019-01-04 15:18
elif self.rect.top <= block.rect.bottom and self.rect.top >= block.rect.bottom - 15:  # Moving up; Hit the bottom side of the wall
        self.rect.top = block.rect.bottom
        self.yvel = 0

Shouldn't yvel be set to a positive number to make it go up?

查看更多
Deceive 欺骗
3楼-- · 2019-01-04 15:22

The easiest way to handle collisions with walls is to move the player rect or sprite along the x-axis first, check if it collides with a wall and then set its self.rect.right = wall.rect.left if it's moving to the right or self.rect.left = wall.rect.right if it's moving to the left. Afterwards you do the same with the y-axis. You have to do the movement separately, otherwise you wouldn't know the direction and how to reset the position of the rect.

import pygame as pg
from pygame.math import Vector2


class Player(pg.sprite.Sprite):

    def __init__(self, x, y, walls):
        super().__init__()
        self.image = pg.Surface((30, 50))
        self.image.fill(pg.Color('dodgerblue1'))
        self.rect = self.image.get_rect(center=(x, y))
        self.pos = Vector2(x, y)  # Position vector.
        self.vel = Vector2(0, 0)  # Velocity vector.
        self.walls = walls  # A reference to the wall group.

    def update(self):
        self.pos += self.vel
        self.wall_collisions()

    def wall_collisions(self):
        """Handle collisions with walls."""
        self.rect.centerx = self.pos.x
        for wall in pg.sprite.spritecollide(self, self.walls, False):
            if self.vel.x > 0:
                self.rect.right = wall.rect.left
            elif self.vel.x < 0:
                self.rect.left = wall.rect.right
            self.pos.x = self.rect.centerx

        self.rect.centery = self.pos.y
        for wall in pg.sprite.spritecollide(self, self.walls, False):
            if self.vel.y > 0:
                self.rect.bottom = wall.rect.top
            elif self.vel.y < 0:
                self.rect.top = wall.rect.bottom
            self.pos.y = self.rect.centery


class Wall(pg.sprite.Sprite):

    def __init__(self, x, y, w, h):
        super().__init__()
        self.image = pg.Surface((w, h))
        self.image.fill(pg.Color('sienna1'))
        self.rect = self.image.get_rect(topleft=(x, y))


def main():
    screen = pg.display.set_mode((640, 480))
    clock = pg.time.Clock()

    all_sprites = pg.sprite.Group()
    walls = pg.sprite.Group()

    wall = Wall(100, 200, 300, 30)
    wall2 = Wall(230, 70, 30, 300)
    walls.add(wall, wall2)
    all_sprites.add(wall, wall2)

    player = Player(300, 300, walls)
    all_sprites.add(player)

    done = False

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True

        keys = pg.key.get_pressed()
        if keys[pg.K_w]:
            player.vel.y = -3
        elif keys[pg.K_s]:
            player.vel.y = 3
        else:
            player.vel.y = 0
        if keys[pg.K_a]:
            player.vel.x = -3
        elif keys[pg.K_d]:
            player.vel.x = 3
        else:
            player.vel.x = 0

        all_sprites.update()
        screen.fill((30, 30, 30))
        all_sprites.draw(screen)

        pg.display.flip()
        clock.tick(30)


if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()
查看更多
祖国的老花朵
4楼-- · 2019-01-04 15:25

Thank you to user sloth! The question he linked gave me some much needed clarity. It took me a bit but I implemented it. I created a function for the collision.

def wallColl(self, xvel, yvel, colliders):
    for collider in colliders:
        if pygame.sprite.collide_rect(self, collider):
            if xvel > 0:
                self.rect.right = collider.rect.left
                self.xvel = 0
            if xvel < 0:
                self.rect.left = collider.rect.right
                self.xvel = 0
            if yvel < 0:
                self.rect.bottom = collider.rect.top
                self.onGround = True
                self.jumps = 3
                self.yvel = 0
            if yvel > 0:
                self.yvel = 0
                self.rect.top = collider.rect.bottom

And then I call them in my update function.

def update(self):
    self.rect.x += self.xvel
    # self.walls is an array of sprites.
    self.wallColl(self.xvel, 0, self.walls) 

    self.rect.y -= self.yvel
    self.onGround = False
    self.wallColl(0, self.yvel, self.walls)

    self.wallCollisions()
    if self.otherplayers != None:
        self.playerCollisions()

    # Gravity
    if self.onGround == False:
        self.yvel-=.0066*self.mass

    self.boundries(highbound, lowbound, leftbound, rightbound)
    self.down = False

The actual useage in my game makes usability near perfect. Not 100% though, this is not a perfect answer.

查看更多
登录 后发表回答