3
votes

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
One assumption you're making in your code is that you can collide the player against each block independently. But should the collision properties of a black depend on other blocks that it's next to? For example, should you be able to land on a block (onGround = true) that has another block on top of it?samgak
Take a look at this question. While the topic is actually different, it's a working example of how you could do the collision detection (look at the collide function). The idea is to check for collision twice, once for the x, and once for the y axis.sloth

3 Answers

3
votes

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.

1
votes

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()
0
votes
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?