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
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()
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?
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.