Reputation: 116
I'm practicing with Pygame trying to learn some basic usage of classes, objects etc.
Right now I'm making a basic platformer with the help of http://programarcadegames.com/
I have a sprite Player and sprites Platforms. The platforms can also be MovingPlatforms which are supposed to move the player on collision, however when the player gets moved into other platforms some weird teleports happen and I do not manage to see the issues right now.
Basically the player is not being moved logically (to me at least) when pushed.
Very thankful for any help! Code below of Player sprite and Platform sprites. They should contain any form of collision. Also full code if anyone wants to run it (just need any "background.jpg" image-file)
Player Sprite:
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.player_width = 25
self.player_height = 50
self.image = pygame.Surface([self.player_width,self.player_height])
self.image.fill([255,0,0])
self.rect = self.image.get_rect()
self.change_x = 0
self.change_y = 0
self.double_jump = 0
self.level = None
def update(self):
self.calc_gravity()
self.rect.x += self.change_x
collision_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for platform in collision_list:
if self.change_x > 0:
self.rect.right = platform.rect.left
elif self.change_x < 0:
self.rect.left = platform.rect.right
self.rect.y += self.change_y
collision_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for platform in collision_list:
if self.change_y > 0:
self.rect.bottom = platform.rect.top
self.double_jump = 0
elif self.change_y < 0:
self.rect.top = platform.rect.bottom
self.change_y = 0
def calc_gravity(self):
if self.change_y == 0:
self.change_y = 2
else:
self.change_y += 0.35
if self.rect.y >= screen_height - self.player_height - 32 and self.change_y >= 0:
self.change_y = 0
self.rect.y = screen_height - self.player_height - 32
Platform and MovingPlatform Sprites:
class Platform(pygame.sprite.Sprite):
def __init__(self, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill([173,255,47])
self.rect = self.image.get_rect()
class MovingPlatform(Platform):
change_x = 0
change_y = 0
boundary_top = 0
boundary_bottom = 0
boundary_left = 0
boundary_right = 0
def update(self):
self.rect.x += self.change_x
hit = pygame.sprite.collide_rect(self, self.player)
if hit:
if self.change_x < 0:
self.player.rect.right = self.rect.left
elif self.change_x > 0:
self.player.rect.left = self.rect.right
self.rect.y += self.change_y
hit = pygame.sprite.collide_rect(self, self.player)
if hit:
if self.change_y < 0:
self.player.rect.bottom = self.rect.top
else:
self.player.rect.top = self.rect.bottom
if self.rect.bottom >= self.boundary_bottom or self.rect.top <= self.boundary_top:
self.change_y *= -1
cur_pos = self.rect.x - self.level.world_shift
if cur_pos < self.boundary_left or cur_pos > self.boundary_right:
self.change_x *= -1
Full code:
import pygame
screen_width = 800
screen_height = 600
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.player_width = 25
self.player_height = 50
self.image = pygame.Surface([self.player_width,self.player_height])
self.image.fill([255,0,0])
self.rect = self.image.get_rect()
self.change_x = 0
self.change_y = 0
self.double_jump = 0
self.level = None
def update(self):
self.calc_gravity()
self.rect.x += self.change_x
collision_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for platform in collision_list:
if self.change_x > 0:
self.rect.right = platform.rect.left
elif self.change_x < 0:
self.rect.left = platform.rect.right
self.rect.y += self.change_y
collision_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
for platform in collision_list:
if self.change_y > 0:
self.rect.bottom = platform.rect.top
self.double_jump = 0
elif self.change_y < 0:
self.rect.top = platform.rect.bottom
self.change_y = 0
def calc_gravity(self):
if self.change_y == 0:
self.change_y = 2
else:
self.change_y += 0.35
if self.rect.y >= screen_height - self.player_height - 32 and self.change_y >= 0:
self.change_y = 0
self.rect.y = screen_height - self.player_height - 32
self.double_jump = 0
def jump(self):
self.rect.y += 2
collision_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
self.rect.y -= 2
if len(collision_list) > 0 or self.rect.bottom >= screen_height - 32:
self.change_y = -10
elif self.double_jump == 0:
self.change_y = -10
self.double_jump = 1
def go_left(self):
self.change_x = -5
def go_right(self):
self.change_x = 5
def stop(self):
self.change_x = 0
class Platform(pygame.sprite.Sprite):
def __init__(self, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill([173,255,47])
self.rect = self.image.get_rect()
class MovingPlatform(Platform):
change_x = 0
change_y = 0
boundary_top = 0
boundary_bottom = 0
boundary_left = 0
boundary_right = 0
def update(self):
self.rect.x += self.change_x
hit = pygame.sprite.collide_rect(self, self.player)
if hit:
if self.change_x < 0:
self.player.rect.right = self.rect.left
elif self.change_x > 0:
self.player.rect.left = self.rect.right
self.rect.y += self.change_y
hit = pygame.sprite.collide_rect(self, self.player)
if hit:
if self.change_y < 0:
self.player.rect.bottom = self.rect.top
else:
self.player.rect.top = self.rect.bottom
if self.rect.bottom >= self.boundary_bottom or self.rect.top <= self.boundary_top:
self.change_y *= -1
cur_pos = self.rect.x - self.level.world_shift
if cur_pos < self.boundary_left or cur_pos > self.boundary_right:
self.change_x *= -1
class Level():
def __init__(self, player):
self.platform_list = pygame.sprite.Group()
self.player = player
self.world_shift = 0
def update(self):
self.platform_list.update()
def draw(self, screen, background):
screen.blit(background, [0,0])
self.platform_list.draw(screen)
def shift_world(self, shift_x):
self.world_shift += shift_x
for platform in self.platform_list:
platform.rect.x += shift_x
class Level_01(Level):
def __init__(self, player):
Level.__init__(self, player)
self.level_limit = -140
level = [[200, 25, 500, 500],
[100, 25, 100, 300]
]
for platform in level:
block = Platform(platform[0], platform[1])
block.rect.x = platform[2]
block.rect.y = platform[3]
block.player = self.player
self.platform_list.add(block)
block = MovingPlatform(150, 25)
block.rect.x = 200
block.rect.y = 300
block.boundary_top = 0
block.boundary_bottom = screen_height - 30
block.change_y = 1
block.player = self.player
block.level = self
self.platform_list.add(block)
block = MovingPlatform(150, 25)
block.rect.x = 100
block.rect.y = 350
block.boundary_left = 0
block.boundary_right = 600
block.change_x = 2
block.player = self.player
block.level = self
self.platform_list.add(block)
class Level_02(Level):
def __init__(self, player):
Level.__init__(self, player)
self.level_limit = -1000
level = [[200, 25, 500, 500],
[100, 25, 100, 100]
]
for platform in level:
block = Platform(platform[0], platform[1])
block.rect.x = platform[2]
block.rect.y = platform[3]
block.player = self.player
self.platform_list.add(block)
def main():
screen = pygame.display.set_mode([screen_width,screen_height])
clock = pygame.time.Clock()
done = False
background = pygame.image.load("background.jpg")
background = pygame.transform.scale(background, [800,600])
camera_left = 250
camera_right = 550
active_sprite_list = pygame.sprite.Group()
player = Player()
level_list =[]
level_list.append(Level_01(player))
level_list.append(Level_02(player))
current_level_no = 0
current_level = level_list[current_level_no]
player.level = current_level
player.rect.x = 100
player.rect.y = screen_height - player.player_height - 25
active_sprite_list.add(player)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#---Key presses---
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
player.go_left()
elif event.key == pygame.K_d:
player.go_right()
elif event.key == pygame.K_SPACE:
player.jump()
elif event.type == pygame.KEYUP:
if event.key == pygame.K_a and player.change_x < 0:
player.stop()
elif event.key == pygame.K_d and player.change_x > 0:
player.stop()
#---Move camera---
if player.rect.left <= camera_left:
diff = camera_left - player.rect.left
player.rect.left = camera_left
current_level.shift_world(diff)
if player.rect.right >= camera_right:
diff = player.rect.right - camera_right
player.rect.right = camera_right
current_level.shift_world(-diff)
#---Change level---
current_position = player.rect.x + current_level.world_shift
if current_position < current_level.level_limit:
player.rect.x = 120
if current_level_no < len(level_list)-1:
current_level_no += 1
current_level = level_list[current_level_no]
player.level = current_level
#---Update sprites---
current_level.update()
active_sprite_list.update()
#---Draw sprites---
current_level.draw(screen, background)
active_sprite_list.draw(screen)
clock.tick(60)
pygame.display.flip()
pygame.quit()
main()
Upvotes: 1
Views: 2260
Reputation: 210928
The problem is related to MovingPlatform.update
:
class MovingPlatform(Platform): # [...] def update(self): self.rect.x += self.change_x hit = pygame.sprite.collide_rect(self, self.player) if hit: if self.change_x < 0: self.player.rect.right = self.rect.left elif self.change_x > 0: self.player.rect.left = self.rect.right # [...]
When the player stands on a platform moving up and down and hits another platform, the player is moved on either side of that platform.
Before asking how to solve the problem, one must ask what is supposed to happen in this particular situation.
One possible solution is to simply remove the code above:
Alternatively, you can add an additional condition that checks if the player is to the left or right of the platform, but not in the middle:
class MovingPlatform(Platform):
# [...]
def update(self):
self.rect.x += self.change_x
hit = pygame.sprite.collide_rect(self, self.player)
if hit:
if self.change_x < 0 and self.player.rect.left < self.rect.left:
self.player.rect.right = self.rect.left
elif self.change_x > 0 and self.player.rect.right > self.rect.right:
self.player.rect.left = self.rect.right
# [...]
However, this will not solve the problem if the player is squeezed by 2 platforms.
Upvotes: 1