This question already has answers here:
How can I do a double jump in pygame?
(1 answer)
Pygame: Can someone help me implement double jumping?
(1 answer)
Closed 2 months ago.
When trying to create a double I either can jump infinitely or jump once, struggling to find a solution to only jump once.
I was able to create a double jump using two different keys however I want it to where I only have to use the 'W' key.
here's my code:
import pygame
pygame.init()
class Fighter:
def __init__(self, x, y):
self.x = x
self.y = y
self.rect = pygame.Rect((self.x, self.y, 70, 200))
self.x_vel = 0
self.y_vel = 0
self.jump = False
def draw_player(self, surface):
pygame.draw.rect(surface, "Red", self.rect)
def detect_collisions(self, platforms):
collisions = []
for platform in platforms:
if self.rect.colliderect(platform.rect):
collisions.append(platform)
return collisions
def movement(self, player, platforms, screen_height, screen_width):
self.x = 0
self.y = 0
self.y_vel = 0
pixel_move = 30
gravity = 5
# get key presses
key = pygame.key.get_pressed()
# player one inputs
if player == 1:
if key[pygame.K_a]:
self.x = -pixel_move
if key[pygame.K_d]:
self.x = pixel_move
if key[pygame.K_w] and self.jump == False:
self.y_vel = -pixel_move
self.jump = True
if key[pygame.K_s]:
self.y_vel = pixel_move
# player two inputs
elif player == 2:
if key[pygame.K_LEFT]:
self.x = -pixel_move
if key[pygame.K_RIGHT]:
self.x = pixel_move
# apply gravity
self.y_vel += gravity
# makes sure the player actually moves
self.rect.x += self.x
self.rect.y += self.y_vel
# make sure player stays onto the screen
self.platform_interaction(platforms)
# make sure the player doesn't fall off the screen
if self.rect.bottom + self.y_vel > screen_height:
self.y_vel = 0
self.rect.bottom = screen_height
self.jump = False
# make sure the player doesn't go above the screen
if self.rect.top < 0:
self.rect.top = 0
# make sure the player doesn't go across the right side of the screen
if self.rect.right > screen_width:
self.rect.right = screen_width
# makes sure the player doesn't go across the left side of the screen
if self.rect.left <= 0:
self.rect.left = 0
def platform_interaction(self, platforms):
collisions = self.detect_collisions(platforms)
for platform in collisions:
# player lands on top off platform
if abs(self.rect.bottom - platform.rect.top) < 10:
self.rect.bottom = platform.rect.top
self.jump = False
# player collides with the sides of the platform
elif abs(self.rect.right - platform.rect.left) < 20:
self.rect.right = platform.rect.left
elif abs(self.rect.left - platform.rect.right) < 20:
self.rect.left = platform.rect.right
elif platform.state:
# player hits themselves on bottom of the platform
if abs(self.rect.top + self.y_vel) < platform.rect.bottom:
self.rect.top = platform.rect.bottom
# make sure player doesn't fall through solid platforms
elif abs(self.rect.bottom + self.y_vel) > platform.rect.top:
self.rect.bottom = platform.rect.top
Related
This question already has answers here:
How do I detect collision in pygame?
(5 answers)
How do I prevent the player from moving through the walls in a maze?
(3 answers)
Closed 9 months ago.
I am trying to learn the basics of pygame by making a simple pacman game with a friend, but I have been having trouble figuring out how to make walls and check collision with the pacman and the wall, and also stopping the movement through a wall.
(this is a 2 player pacman, ghost is arrow keys, and pacman is wasd)
This is main.py
import pygame
import random
import time
from Pacman import pacman
from Ghost import ghost
#colors
Yellow = (255,255,0)
Blackish_Blue = (20,0,70)
Red = (255,0,0)
P_B = (40,60,100)
clock = pygame.time.Clock()
#display
pygame.init()
pygame.display.set_caption(' Scuffed Pacman')
width, height = 640, 480
screen = pygame.display.set_mode((width, height))
pacman = pacman(screen)
ghost = ghost(screen)
font = pygame.font.Font('freesansbold.ttf', 32)
text = font.render('Game Over', True, Yellow)
textRect = text.get_rect()
textRect.center = (width / 2, height / 2)
run = True
while run == True:
pygame.time.delay(10)
clock.tick(60)
screen.fill(Blackish_Blue)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
color = (182,163,192)
#attempt to make wall and check collision
ghostspawn = pygame.Rect(0,0,60,40)
ghostspawn.center = (width / 2, (height / 2)-50)
pygame.draw.rect(screen, color, ghostspawn)
if ghostspawn.collidepoint(pacman.x, pacman.y):
print("collision detected")
pacman.draw(screen)
ghost.draw(screen)
ghost.update()
pacman.update()
distance = (((pacman.x - ghost.x)**2) + ((pacman.y - ghost.y)**2))**(1/2)
sumrad = pacman.radius + ghost.radius
if distance < sumrad:
while True:
screen.fill(P_B)
screen.blit(text, textRect)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
pygame.display.update()
pygame.quit()
This is the code for pacman:
import pygame
Yellow = (255,255,0)
class pacman(pygame.sprite.Sprite):
def __init__(self, mainscreen):
super().__init__()
self.x = 320
self.y = 240
self.radius = 15
self.velocity = 2
self.origin = (self.x, self.y)
def draw(self, mainscreen):
pygame.draw.circle(mainscreen, Yellow, (self.x, self.y), self.radius)
def update(self):
self.movement()
self.origin = (self.x, self.y)
def movement(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_d] and self.velocity > 0 and self.x < 625:
self.x += self.velocity
if keys[pygame.K_a] and self.velocity > 0 and self.x > 15:
self.x -= self.velocity
if keys[pygame.K_w] and self.velocity > 0 and self.y > 15:
self.y -= self.velocity
if keys[pygame.K_s] and self.velocity > 0 and self.y < 465:
self.y += self.velocity
and this is the code for ghost:
import pygame
Red = (255,0,0)
class ghost(pygame.sprite.Sprite):
def __init__(self, mainscreen):
super().__init__()
self.x = 213
self.y = 240
self.radius = 15
self.velocity = 2
self.origin = (self.x, self.y)
def draw(self, mainscreen):
pygame.draw.circle(mainscreen, Red, (self.x, self.y), self.radius)
def update(self):
self.movement()
self.origin = (self.x, self.y)
def movement(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT] and self.velocity > 0 and self.x < 625:
self.x += self.velocity
if keys[pygame.K_LEFT] and self.velocity > 0 and self.x > 15:
self.x -= self.velocity
if keys[pygame.K_UP] and self.velocity > 0 and self.y > 15:
self.y -= self.velocity
if keys[pygame.K_DOWN] and self.velocity > 0 and self.y < 465:
self.y += self.velocity
this is my attempt at checking for wall collision, but I am unaware of how to stop pacman and the ghost from going through. the collision also only checks the middle of the circle.
ghostspawn = pygame.Rect(0,0,60,40)
ghostspawn.center = (width / 2, (height / 2)-50)
pygame.draw.rect(screen, color, ghostspawn)
if ghostspawn.collidepoint(pacman.x, pacman.y):
print("allowed")
here is how i detect collision between pacman and ghost, as it may be helpful for doing it with walls, but i don't know how.
distance = (((pacman.x - ghost.x)**2) + ((pacman.y - ghost.y)**2))**(1/2)
sumrad = pacman.radius + ghost.radius
ive looked at a few similar questions on here, but I can't quite grasp how it works.
So, if anyone could help, I'm having trouble with checking collision between a circle and rectangle, and also preventing the circle/pacman from moving through.
To check if a circle is colliding with a wall you just need to do 2 things
Get the distance between the circle and the wall
Then check if the distance is smaller or equal to the circles distance
Pretty much like this:
distance = abs(circle.x - wall.x) + abs(circle.y - wall.y)
if distance <= circle.radius: # abs() Makes the value in the brackets positive
circle.x = oldX
circle.y = oldY
To implement this I with your game would first add a self.oldX and a self.oldY in the self.__init__() in both of the pacman and ghost classes. And I would set them to 0 for now.
Then I would update the oldX and oldY in the movement function before I move the object, like this:
def movement(self):
keys = pygame.key.get_pressed()
self.oldX = self.x
self.oldY = self.y
if keys[pygame.K_d] and self.velocity > 0 and self.x < 625:
self.x += self.velocity
if keys[pygame.K_a] and self.velocity > 0 and self.x > 15:
self.x -= self.velocity
if keys[pygame.K_w] and self.velocity > 0 and self.y > 15:
self.y -= self.velocity
if keys[pygame.K_s] and self.velocity > 0 and self.y < 465:
self.y += self.velocity
Then I would have a list that containes every wall in the game and a list that containes every ghost in the game (That's if you want to have more than one ghost).
Then I would go in the main loop (The while loop that you have) and I would add this After calling the movement function of the ghosts and the pacman:
for wall in walls:
distance = abs(pacman.x - wall.x) + abs(pacman.y - wall.y)
if distance <= pacman.radius:
pacman.x = pacman.oldX
pacman.y = pacman.oldY
for ghost in ghosts:
distance = abs(ghost.x - wall.x) + abs(ghost.y - wall.y)
if distance <= ghost.radius:
ghost.x = ghost.oldX
ghost.y = ghost.oldY
and that should work, Thanks.
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 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?