So I'm making a small project and I can't understand why my player keeps going up and down when it collides with my platform even when I want it to be static. The problem is in the code below and I can't find a fix.
def Collision_Manager():
if player.rect.colliderect(platform.rect):
if abs(player.rect.bottom >= platform.rect.top):
player.y = platform.y - player.height
I cant understand why it's not a seamless animation like my border system which I did by doing this.
def player_borders():
if player.x <= 0:
player.x = 0
elif player.x >= 870:
player.x = 870
if player.y <= 0:
player.y = 0
elif player.y >= 570:
player.y = 570
player.y
and platform.y
are floating point coordinates. pygame.Rect
objects however can just store integral coordinates.
The issue is caused when the .rect
attribute is updated with the player's position and the coordinates are truncated. e.g.:
self.rect.topleft = int(self.x), int(self.y)
A possible solution might be to round
the coordinates:
self.rect.topleft = round(self.x), round(self.y)
Or round up the y
coordinate:
self.rect.topleft = round(self.x), int(math.ceil(self.y))
Depending on the control flow of your code, it may also be necessary to update the .rect
attribute when a collision is detected:
if player.rect.colliderect(platform.rect):
if player.rect.bottom >= platform.rect.top:
platform.rect.bottom = platform.rect.top
player.y = platform.rect.y
By the way, abs()
is completely useless here:
if abs(player.rect.bottom >= platform.rect.top):
if player.rect.bottom >= platform.rect.top:
It doesn't any harm, however, since it's abs(True)
or abs(False)
, which results in 1 or 0.
Minimal example (see also Platform collision):
import pygame
import random
pygame.init()
window = pygame.display.set_mode((500, 500))
clock = pygame.time.Clock()
class Player(pygame.sprite.Sprite):
JUMP_ACCELERATION = -7
def __init__(self, midbottom_pos):
super().__init__()
self.image = pygame.Surface((30, 30), pygame.SRCALPHA)
self.image.fill("red")
self.rect = self.image.get_rect(midbottom = midbottom_pos)
self.y = self.rect.bottom
self.vel_y = 0
self.acc_y = 0
def jump(self):
self.acc_y = Player.JUMP_ACCELERATION
def update(self, platforms):
self.vel_y += GRAVITY
self.y += self.vel_y
self.rect.bottom = round(self.y + 1)
colliders = pygame.sprite.spritecollide(self, platforms, False)
if colliders:
self.y = colliders[0].rect.top
self.vel_y = self.acc_y
self.y += self.vel_y
elif self.y > window.get_height():
self.y = 100
self.acc_y = 0
self.rect.bottom = round(self.y)
class Platform(pygame.sprite.Sprite):
PLATFORM_SHIFT = 1.5
def __init__(self, topleft_pos):
super().__init__()
self.image = pygame.Surface((200, 10), pygame.SRCALPHA)
self.image.fill("gray")
self.rect = self.image.get_rect(topleft = topleft_pos)
self.x = self.rect.x
def update(self):
self.x -= Platform.PLATFORM_SHIFT
self.rect.x = round(self.x)
if self.rect.right <= 0:
self.kill()
GRAVITY = 0.2
platfrom_group = pygame.sprite.Group(Platform((0, 250)))
player = Player((50, 100))
plyer_sprites = pygame.sprite.GroupSingle(player)
run = True
while run:
clock.tick(100)
acc_y = GRAVITY
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.jump()
platfrom_group.update()
while platfrom_group.sprites()[-1].rect.right < window.get_width():
last_platform_rect = platfrom_group.sprites()[-1].rect
if last_platform_rect.top - 50 < 150:
new_y = last_platform_rect.top + 50
elif last_platform_rect.top + 50 > window.get_height() - 50:
new_y = last_platform_rect.top - 50
else:
new_y = last_platform_rect.top + (50 if random.random() > 0.5 else -50)
platfrom_group.add(Platform((last_platform_rect.right, new_y)))
print(len(platfrom_group.sprites()))
player.update(platfrom_group)
window.fill((0, 0, 64))
platfrom_group.draw(window)
plyer_sprites.draw(window)
pygame.display.flip()
pygame.quit()
exit()