I am working on a platformer and have a weird issue with the collision. So far, I have a player and some platforms and the collision does work but the horizontal collision leaves some space between the player and the level and I don't understand why.
Here is the code for the player:
import pygame
from pygame.math import Vector2
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32,64))
self.image.fill('red')
self.rect = self.image.get_rect(center = (350,250))
self.direction = Vector2(0,0)
self.speed = 8
self.gravity = 0.8
self.jump_speed = -16
self.on_ground = False
def get_input(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]: self.direction.x = 1
elif keys[pygame.K_LEFT]: self.direction.x = -1
else: self.direction.x = 0
if keys[pygame.K_SPACE] and self.on_ground:
self.jump()
self.on_ground = False
def jump(self):
self.direction.y = self.jump_speed
def apply_gravity(self):
self.direction.y += self.gravity
def apply_movement(self):
# horizontal movement
self.rect.x += self.direction.x * self.speed
# vertical movement
self.rect.y += self.direction.y
def update(self):
# movement methods
self.get_input()
self.apply_gravity()
self.apply_movement()
and here the code for the level
import pygame, sys
from player import Player
basic = [
'0000000000000',
'0000000000000',
'0010011000010',
'0111000010110',
'0111000110111',
'1111111111111',
'1111111111111',]
class Level:
def __init__(self):
self.player = pygame.sprite.GroupSingle(Player())
self.tiles = pygame.sprite.Group()
self.setup_level()
def setup_level(self):
for row_index, row in enumerate(basic):
for col_index,cell in enumerate(row):
if cell == '1':
x = col_index * 100
y = row_index * 100
self.tiles.add(Tile((x,y)))
def player_collision(self):
player = self.player.sprite
player_size = player.rect.size
# vertical collision check
future_y = player.rect.top + player.direction.y
future_player_y = pygame.Rect((player.rect.left,future_y),player_size)
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(future_player_y):
if player.direction.y >= 0:
player.on_ground = True
player.rect.bottom = sprite.rect.top
player.direction.y = 0
else:
player.rect.top = sprite.rect.bottom
player.direction.y = 0
# horizontal collision check
future_x = self.player.sprite.rect.left + (self.player.sprite.direction.x * self.player.sprite.speed)
future_player_x = pygame.Rect((future_x,self.player.sprite.rect.top),player_size)
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(future_player_x):
if player.direction.x < 0:
player.rect.left = sprite.rect.right
player.rect.right += player.speed
elif player.direction.x > 0:
player.rect.right = sprite.rect.left
player.rect.right -= player.speed
def run(self):
self.tiles.draw(screen)
self.player.update()
self.player_collision()
self.player.draw(screen)
class Tile(pygame.sprite.Sprite):
def __init__(self,pos):
super().__init__()
self.image = pygame.Surface((100,100))
self.image.fill('green')
self.rect = self.image.get_rect(topleft = pos)
if __name__ == '__main__':
pygame.init()
screen = pygame.display.set_mode((1000,640))
clock = pygame.time.Clock()
debug_font = pygame.font.Font(None,40)
level = Level()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill('black')
level.run()
pygame.display.update()
clock.tick(60)
I am using future_player_y because I had some earlier problems with animation frames and detecting whether the player was on the ground or not. I applied the same logic to the horizontal axis but I guess it wouldn't be strictly needed there.
The result looks like this and the collisions do work (kinda) but the red rectangle cannot move any closer to the tile to its left.
Just for clarity, here is a zoomed in look at the area I am talking about:
You need to separate the movement in the x and y direction.
Remove the apply_movement
method:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
# movement methods
self.get_input()
self.apply_gravity()
# self.apply_movement() <--- DELETE
Apply the movement separately before the collision test:
class Level:
# [...]
def player_collision(self):
player = self.player.sprite
# vertical collision check
player.rect.y += player.direction.y
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(player.rect):
if player.direction.y >= 0:
player.on_ground = True
player.rect.bottom = sprite.rect.top
player.direction.y = 0
else:
player.rect.top = sprite.rect.bottom
player.direction.y = 0
# horizontal collision check
player.rect.x += player.direction.x * player.speed
for sprite in self.tiles.sprites():
if sprite.rect.colliderect(player.rect):
if player.direction.x < 0:
player.rect.left = sprite.rect.right
elif player.direction.x > 0:
player.rect.right = sprite.rect.left
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.