I am currently trying to get accustomed with pygame and therefore building a platformer game. I have searched for this problem, but I couldn't find anything related. The problem is that my player teleports on top of my platform rectangle when they collide on either the left or right. I've tried to implement multiple fixes, such as alternating the players y-position, which creates other problems, as well as only triggering the collision effect if the player hits either side, not the top, but nothing seems to forego the teleportation of the player. This leads to the collision not triggering or the player colliding with an invisible wall on either the top left or the top right of the platform. Here is the code of the relevant file (pg = pygame):
from pygame.locals import *
from settings import *
vec = pg.math.Vector2
platforms = pg.sprite.Group()
grounds = pg.sprite.Group()
all_except_player = pg.sprite.Group()
class Player(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((20, 20))
self.image.fill(blue)
self.rect = self.image.get_rect()
self.pos = vec((10, 780))
self.vel = vec(0, 0)
self.acc = vec(0, 0)
def update(self):
platforms.add(plat1)
grounds.add(ground)
all_except_player.add(plat1, ground)
self.acc = vec(0, 0.5)
hits = pg.sprite.spritecollide(p, all_except_player, False)
if p.vel.y > 0:
if hits:
self.pos.y = hits[0].rect.top + 1
self.vel.y = 0
# this is where I assume the problem lies
wall_hits = pg.sprite.spritecollide(p, platforms, False)
if p.vel.x > 0:
if wall_hits:
self.pos.x = wall_hits[0].rect.left - 10
self.vel.x = 0
if p.vel.x < 0:
if wall_hits:
self.pos.x = wall_hits[0].rect.right + 10
self.vel.x = 0
keys = pg.key.get_pressed()
if keys[pg.K_a]:
self.acc.x -= acc
if keys[pg.K_d]:
self.acc.x += acc
if keys[pg.K_SPACE]:
self.jump()
self.acc.x += self.vel.x * fric
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
if self.pos.x > width:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = width
self.rect.midbottom = self.pos
def jump(self):
hits = pg.sprite.spritecollide(self, all_except_player, False)
if hits:
self.vel.y -= 15
class Ground(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((width, 20))
self.image.fill(green)
self.rect = self.image.get_rect(center=(width/2, height - 10))
class Platform(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((100, 300))
self.image.fill(gray)
self.rect = self.image.get_rect(center=(width/2, height - 10))
plat1 = Platform()
p = Player()
ground = Ground()
I would greatly appreciate if someone could help me and point out my mistake.
Split it in 2 separate problems. First handle the collision with the sides of the platform.
The player collides with the left side of the platform when moving to the right, colliding but the left side of the player does not collide with the platform.
It collides with the right side of the platform when moving to the right, colliding but the right side does not collide with the platform.
If the player collides sideways, correct his position so that he does not collide further.
After that, do a new collision test and handle the collision with the top of the platform:
# handle sideways collision
self.rect.midbottom = self.pos
hits = pg.sprite.spritecollide(p, all_except_player, False)
if hits:
if p.vel.x > 0 and self.rect.left < hits[0].rect.left:
self.rect.right = hits[0].rect.left
self.pos = self.rect.midbottom
self.vel.y = 0
elif p.vel.x < 0 and self.rect.right > hits[0].rect.right:
self.rect.left = hits[0].rect.right
self.pos = self.rect.midbottom
self.vel.y = 0
# handel vertical collision
hits = pg.sprite.spritecollide(p, all_except_player, False)
if hits and p.vel.y > 0:
self.rect.bottom = hits[0].rect.top + 1
self.rect.midbottom = self.pos
self.vel.y = 0
Related
This question already has answers here:
How do I detect collision in pygame?
(5 answers)
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
Closed 2 years ago.
I am currently trying to make my own platformer on pygame. I have coded the collisions for the top and bottom of the platform so that the player can only jump on top of the platform if they are falling onto it. However, if the player walks into the side of the platform, he snaps on top of it. How can I make it so the sides of a platform act as a border?
This is my update section in the main game loop:
def update(self):
self.all_sprites.update()
hits = p.sprite.spritecollide(self.player, self.platforms, False)
if self.player.vel.y > 0:
if hits:
self.player.rect.bottom = hits[0].rect.top + 1
self.player.pos = self.player.rect.midbottom
self.player.vel.y = 0
if self.player.vel.y < 0:
if hits:
self.player.rect.top = hits[0].rect.bottom
self.player.pos = self.player.rect.midbottom
self.player.vel.y = 0
I have tried using this, but it didnt work:
if self.player.vel.x > 0:
if hits:
self.player.rect.right = hits[0].rect.left
self.player.pos = self.player.rect.midleft
self.player.vel.x = 0
if self.player.vel.x < 0:
if hits:
self.player.rect.left = hits[0].rect.right
self.player.pos = self.player.rect.midright
self.player.vel.x = 0
This is my player class:
vec = p.math.Vector2
class Player(p.sprite.Sprite):
def __init__(self, game):
p.sprite.Sprite.__init__(self)
self.game = game
self.image = p.Surface((p_width, p_height))
self.image.fill(p_colour)
self.rect = self.image.get_rect()
self.rect.center = (0, height - 30)
self.pos = vec(0, height - 30)
self.vel = vec(0, 0)
self.acc = vec(0, 0)
def jump(self):
self.rect.y += 1
hits = p.sprite.spritecollide(self, self.game.platforms, False)
self.rect.y -= 1
if hits:
self.vel.y = p_jump
def update(self):
self.acc = vec(0, p_grav)
keys = p.key.get_pressed()
if keys[p.K_LEFT]:
self.acc.x = -p_acc
if keys[p.K_RIGHT]:
self.acc.x = p_acc
self.acc.x += self.vel.x * p_friction
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
if self.rect.right > width - 1:
self.rect.right = width - 1
self.pos = self.rect.midbottom
if self.rect.left < 1:
self.rect.left = 1
self.pos = self.rect.midbottom
self.rect.midbottom = self.pos
Any help is appreciated. I tried keeping the code minimal
I am making a tilemap platformer with pygame ad I defined the X and Y movement for the player and also a Y force that represents the gravity force to make it jump correctly. However, when my player falls off to the ground and the game begins, it doesn't move correctly neither to the right nor to the left direction due to the gravity force that keeps pushing him to the down. In addition to that, when the player jumps and i move it to the right, it's like it doesn't recognize the collision with the bottom rect of the wall (see gif below for a visual explanation of that).
I tried disabling that gravity force when the player hits the ground and enable it when the player jumps but it didn't work as expected and I've got problems managing collisions (also, it doesn't make sense to disable gravity, right?)
Here's my Player class:
class Player(pg.sprite.Sprite):
def __init__(self, game, x, y):
self.groups = game.sprites
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = pg.Surface((TILESIZE, TILESIZE))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.move_left = self.move_right = False
self.pos = pg.math.Vector2(x, y)
self.acc = pg.math.Vector2(0, GRAVITY)
self.vel = pg.math.Vector2(0, 0)
def jump(self):
self.vel.y -= 3
def update(self):
self.vel.x = 0
if self.move_left:
self.vel.x = -PLAYER_SPEED
if self.move_right:
self.vel.x = PLAYER_SPEED
self.vel += self.acc
self.pos += self.vel
self.rect.x = self.pos.x * TILESIZE
self.rect.y = self.pos.y * TILESIZE
if self.vel.y > 0.5:
self.vel.y = 0.5
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
if self.vel.x > 0:
self.pos -= self.vel
self.rect.right = hits[0].rect.left
self.vel.x = 0
if self.vel.x < 0:
self.pos -= self.vel
self.rect.left = hits[0].rect.right
self.vel.x = 0
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
if self.vel.y > 0:
self.pos -= self.vel
self.rect.bottom = hits[0].rect.top
self.vel.y = 0
if self.vel.y < 0:
self.pos -= self.vel
self.rect.top = hits[0].rect.bottom
self.vel.y = 0
The problem is calling spritecollide twice, combined with updating the position:
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
# If one of these conditions are true, the position will be updated,
# continuing the previous velocity, i.e. going through the roof.
if self.vel.x > 0:
self.pos -= self.vel
# [...]
if self.vel.x < 0:
self.pos -= self.vel
# [...]
# After you go through the roof, there's no more collision...
hits = pg.sprite.spritecollide(self, self.game.walls, False)
if hits:
if self.vel.y > 0:
self.pos -= self.vel
# [...]
self.vel.y = 0
Two cases, with the current code:
If you jump without x-speed, you do not update the position (aka go through the roof). So the second call of spritecollide does return a non empty hits and you will bounce on the roof.
If the x-speed is non null, you update the position go through the roof, and the second if hits: is False.
Solution: Call hits = pg.sprite.spritecollide(self, self.game.walls, False) only once, and do not change the position before all the velocities are updated.
I am trying to stop the player passing through blocks. I would like them to be able to land on the blocks but bounce away if there is a collision on the other sides of the block
I have previously tried changing the distance the player is reset to when they hit a block
Run every frame to check if there is a collision:
hits = pygame.sprite.spritecollide(player, walls, False)
if hits:
player.checkCollisionWall(hits)
Player Class
import pygame, time, Settings
from pygame.locals import *
vec = pygame.math.Vector2
pygame.init()
class player(pygame.sprite.Sprite):
ACCEL = 0.5 # Acceleration
GFRICTION = vec(-0.2, 0) # Ground Friction
AFRICTION = vec(-0.2, 0) # Air Friction
GRAVITY = 0.8 # must be greater than 0.6
JUMP_HEIGHT = 10
START_X = 25
START_Y = 600
WIDTH = 10
HEIGHT = 10
START_POS = vec(START_X + WIDTH/2, START_Y + HEIGHT) # point at bottom middle
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.Pos = self.START_POS
self.image = pygame.surface.Surface((self.WIDTH, self.HEIGHT))
self.image.fill((0, 255, 0))
self.rect = self.image.get_rect()
self.rect.center = (self.Pos.x, self.Pos.y)
self.vel = vec(0,0) # set velocity as a vector
self.acc = vec(0,0) # set acceleration as a vector
self.inJump = False
self.tryingToJump = False
def update(self):
self.tryingToJump = False
self.acc = vec(0, self.GRAVITY)
self.move()
def draw(self):
# draw the rectangle
self.Pos += self.vel + 0.5 *self.acc
self.rect.center = self.Pos
def move(self):
# identify which keys are pressed
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_LEFT]:
self.changeX("left")
elif pressed_keys[K_RIGHT]:
self.changeX("right")
if pressed_keys[K_UP]:
self.jump()
# check player is on screen and place player where it should be if neccessary
if self.Pos.y > Settings.WINDOW_HEIGHT:
self.Pos.x = self.START_POS.x
self.Pos.y = self.START_POS.y
if self.Pos.x < 0:
self.vel.x = 2
if self.Pos.x > Settings.WINDOW_WIDTH:
self.vel.x = -2
# apply friction
if self.inJump: #in the air
self.acc.x += self.vel.x * self.AFRICTION.x
else: #touching the ground
self.acc.x += self.vel.x * self.GFRICTION.x
# move the player
self.vel += self.acc
def changeX(self, direction):
# move left or right
if direction == "right":
self.acc.x = self.ACCEL
elif direction == "left":
self.acc.x = -self.ACCEL
def jump(self):
# jump only if standing on a platform
if self.inJump == False:
self.tryingToJump = True
self.inJump = True
self.vel.y -= self.JUMP_HEIGHT
def checkCollisionWall(self, hits):
self.lowestWall = self.highestWall = hits[0]
for i in hits:
if i.rect.bottom > self.lowestWall.rect.bottom:
self.lowestWall = i # find the lowest wall that the player is touching
if i.rect.top < self.highestWall.rect.top:
self.highestWall = i # find the highest wall that the player is touching
if self.vel.y > 0: # check if a block is below
print("below")
self.rect.bottom = self.lowestWall.rect.top
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
self.inJump = False
if self.vel.y < 0: # check if a block is above
if not self.tryingToJump: # if the block isn't trying to jump (I have this in otherwise player doesn't jump)
print("above")
self.rect.top = self.highestWall.rect.bottom
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
if self.highestWall.rect.top < self.lowestWall.rect.top and self.rect.bottom == self.lowestWall.rect.top: # I have this line in too make sure that the player does not snap to the side of the block it is in when it moves side to side
if self.vel.x > 0:
print("right")
self.rect.right = self.highestWall.rect.left
self.acc.x = self.vel.x = -self.ACCEL # set acceleration and velocity to -0.5 on the x axis
if self.vel.x < 0:
print("left")
self.rect.left = self.highestWall.rect.right
self.acc.x = self.vel.x = self.ACCEL # set acceleration and velocity to 0.5 on the x axis
When I tried this the player would land on blocks fine, but when they touch the bottom of a block the player would snap to the top of the block instead of bouncing off it. When the player jumps while moving into the block the player snaps to the top of the higher block.
In my (not so vast) experience this kind of problems may arise when the player movement is not tested separatately for each dimension.
I suggest you to:
Separate x and y movements and collision tests, so that you can move your player along x, test for collision and fix x position if needed, and repeat the process for y.
Edit the checkCollisionWall method so that it takes a single block, not a list of blocks. If you separate x and y movements, player will collide with at most one block. So no need to seach for lowest walls and highest walls. Your code will be simpler.
Here I suggest you some edits. move has been split into move_x and move_y. checkCollisionWall into checkCollision_x and checkCollision_y.
I've also edited the update method: now it does the whole process described in point 1, so calling update will check for collision too.
def update(self):
self.tryingToJump = False
self.acc = vec(0, self.GRAVITY)
self.move_x()
hits = pygame.sprite.spritecollide(player, walls, False)
for bhit in hits:
player.checkCollision_x(bhit)
self.move_y()
hits = pygame.sprite.spritecollide(player, walls, False)
for bhit in hits:
player.checkCollision_y(bhit)
def move_x(self):
# identify which keys are pressed
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_LEFT]:
self.changeX("left")
elif pressed_keys[K_RIGHT]:
self.changeX("right")
# check player is on screen and place player where it should be if neccessary
if self.Pos.x < 0:
self.vel.x = 2
if self.Pos.x > Settings.WINDOW_WIDTH:
self.vel.x = -2
# apply friction
if self.inJump: #in the air
self.acc.x += self.vel.x * self.AFRICTION.x
else: #touching the ground
self.acc.x += self.vel.x * self.GFRICTION.x
# move the player
self.vel.x += self.acc.x
def move_y(self):
# identify which keys are pressed
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_UP]:
self.jump()
# check player is on screen and place player where it should be if neccessary
if self.Pos.y > Settings.WINDOW_HEIGHT:
self.Pos.x = self.START_POS.x
self.Pos.y = self.START_POS.y
# move the player
self.vel.y += self.acc.y
def checkCollision_x(self, coll_block):
if self.vel.x > 0:
print("right")
self.rect.right = coll_block.rect.left
self.acc.x = self.vel.x = -self.ACCEL # set acceleration and velocity to -0.5 on the x axis
elif self.vel.x < 0:
print("left")
self.rect.left = coll_block.rect.right
self.acc.x = self.vel.x = self.ACCEL # set acceleration and velocity to 0.5 on the x axis
def checkCollision_y(self, coll_block):
if self.vel.y > 0: # check if a block is below
print("below")
self.rect.bottom = coll_block.rect.top
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
self.inJump = False
elif self.vel.y < 0: # check if a block is above
if not self.tryingToJump: # if the block isn't trying to jump (I have this in otherwise player doesn't jump)
print("above")
self.rect.top = coll_block.rect.bottom
self.acc.y = self.vel.y = 0 # set acceleration and velocity to 0 on the y axis
This code of course is not tested, but even if doesn't solve your problem as it is, I think should put you in the correct direction.
I'm trying to find a way to make perfect collision in my platformer game for school. Most of it works but there is a slight problem. When I stand on a platform above ground level and move left or right off of it, the player continues to float in midair at the same height of the platform that I got off of. This can be fixed by jumping and the collision reverts to normal. I am using a state system to track if the player is standing or not by having a folder of possible player states and switching between them. This is set to "Falling" by default because the player starts in midair when the game runs. The code for the game is divided into three separate files below (main.py, obj.py and settings.py). Please tell me how I can fix this glitch.
Main.py
import pygame
import random
from settings import *
from obj import *
pygame.init()
pygame.mixer.init()
pygame.font.init()
pygame.display.set_caption(TITLE)
screen = pygame.display.set_mode([WIDTH,HEIGHT])
clock = pygame.time.Clock()
me = Player()
all_sprites.add(me)
platforms = []
pf = Wall(20,40,500,480, 0)
pf2 = Wall(WIDTH,40, 400,500, 0)
platforms.append(pf)
platforms.append(pf2)
for i in platforms:
wall_sprites.add(i)
running = True
while running:
clock.tick(FPS)
all_sprites.update()
wall_sprites.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(GREY)
all_sprites.draw(screen)
wall_sprites.draw(screen)
pygame.display.update()
pygame.quit()
obj.py
import pygame
import math
from settings import *
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((40,40))
self.image.fill(BLACK)
self.rect = self.image.get_rect()
self.rect.x = WIDTH / 2
self.rect.y = 70
self.vx = 0
self.vy = 0
self.SW = False # Can you screen wrap?
self.player_states = ["Standing","Falling"]
self.state = self.player_states[1]
def update(self):
self.vx = 0 # X speed set to 0 if no input is received
if self.state == self.player_states[1]:
self.vy += GRAVITY # Gravity only added while falling
else:
self.vy = 0
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.vx = -SPEED
if keys[pygame.K_RIGHT]:
self.vx = SPEED
if keys[pygame.K_SPACE] and self.state == self.player_states[0]:
self.vy -= JUMP_SPEED
self.state = self.player_states[1]
self.rect.left += self.vx # X and Y positions are updated
self.collide(self.vx, 0, wall_sprites) # Collision is checked. Second param is 0 b/c we aren't checking for vertical collision here
self.rect.top += self.vy
self.collide(0, self.vy, wall_sprites)
if self.SW:
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.bottom = 0
if self.rect.bottom < 0:
self.rect.top = HEIGHT
def collide(self, xDif, yDif, platform_list):
for i in platform_list: # Shuffle through list of platforms
if pygame.sprite.collide_rect(self, i): # If there is a collision between the player and a platform...
if xDif > 0: # And our x (horizontal) speed is greater than 0...
self.rect.right = i.rect.left # That means that we are moving right,
if xDif < 0: # So our right bounding box becomes equal to the left bounding box of all platforms and we don't collide
self.rect.left = i.rect.right
if yDif > 0:
self.rect.bottom = i.rect.top
self.state = self.player_states[0]
if yDif < 0:
self.rect.top = i.rect.bottom
class Wall(pygame.sprite.Sprite): # Collision is added for platforms just in case that they are moving. If they move to you, they push you
def __init__(self, width, height, xpos, ypos, speed):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((width,height))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.rect.centerx = xpos
self.rect.centery = ypos
self.speed = speed
def update(self):
self.rect.left += self.speed
self.collide(self.speed, all_sprites) # Collision only for platforms moving left and right. Not up and down yet
def collide(self, xDif, player_list):
for i in player_list:
if pygame.sprite.collide_rect(self, i):
if xDif > 0: # If the platform is moving right... (has positive speed)
i.rect.left += self.speed # Platform pushes player
self.rect.right = i.rect.left # Player sticks to the wall and is pushed
if xDif < 0:
i.rect.right -= self.speed
self.rect.left = i.rect.right
settings.py
import pygame
FPS = 60
WIDTH = 800
HEIGHT = 600
TITLE = "Perfect collision"
GREY = (150,150,150)
BLACK = (0,0,0)
BLUE = (0,0,255)
SPEED = 5
JUMP_SPEED = 9
GRAVITY = 0.3
all_sprites = pygame.sprite.Group()
wall_sprites = pygame.sprite.Group()
Just add the GRAVITY to self.vy in every frame and set self.vy to 0 when the sprite touches the ground:
def update(self):
self.vx = 0
self.vy += GRAVITY
def collide(self, xDif, yDif, platform_list):
for i in platform_list:
if pygame.sprite.collide_rect(self, i):
# Code omitted.
if yDif > 0:
self.rect.bottom = i.rect.top
self.state = self.player_states[0]
self.vy = 0 # Set vy to 0 if the sprite touches the ground.
This question already has answers here:
How do I detect collision in pygame?
(5 answers)
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
Adding collision to maze walls
(1 answer)
How to implement barriers to stop the player moving through walls
(1 answer)
Closed 2 years ago.
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.
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.
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.
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?