Reputation: 89
I am trying to develop a platformer, but when I started implementing gravity and a jump mechanic the sprites of my terrain get misaligned on the y axis and I really don't know what is wrong.
My code consist of tree main classes:
Player
: This class has all the logic of the player.Level
: This one have the logic of the level and this is probably where my error is coming from.Tile
: This class generates all the tiles of the terrain and could also be part of the issue.There is also a class named Game
but the issue is 100% not coming from there as all this class does for now is initialize the level class.
import pygame, sys
pygame.init()
# Settings
tile_size = 64
fov = 10
screen_width = 1200
screen_height = tile_size * fov
level_data = [
' ',
' ',
' XX ',
'XX XXX ',
'XX XX',
'XXXX XX ',
'XXXX P XX ',
'XX X XXX XX X ',
' X XXX XX XX ',
' XXXX XXXXX XX XXX',
'XXXXXXX XXXXX XX XXX']
# Classes
class Game():
def __init__(self):
self.level = Level(screen)
self.status = 'level'
def run(self):
if self.status == 'level':
self.level.run()
class Level:
def __init__(self,display_surface):
# Basic setup
self.setup_level()
self.display_surface = display_surface
# Movement
self.x_shift = 0
self.y_shift = 0
def setup_level(self):
self.tiles = pygame.sprite.Group()
self.player = pygame.sprite.GroupSingle()
for row_index,row in enumerate(level_data):
for col_index,col in enumerate(row):
x = col_index * tile_size
y = (row_index * tile_size) - ((len(level_data) - fov) * tile_size)
if col == 'X':
tile = Tile((x,y))
self.tiles.add(tile)
if col == 'P':
player = Player((x,y))
self.player.add(player)
def apply_gravity(self):
player = self.player.sprite
self.y_shift = player.direction.y
def run(self):
# Tiles
self.tiles.update(self.x_shift,self.y_shift)
self.tiles.draw(self.display_surface)
# Player
self.player.update()
self.apply_gravity()
self.player.draw(self.display_surface)
class Tile(pygame.sprite.Sprite):
def __init__(self,pos):
super().__init__()
self.image = pygame.Surface((tile_size,tile_size))
self.image.fill('grey')
self.rect = self.image.get_rect(topleft = pos)
def update(self,x_shift,y_shift):
self.rect.x += x_shift
self.rect.y -= y_shift
class Player(pygame.sprite.Sprite):
def __init__(self,pos):
super().__init__()
# Image
self.image = pygame.Surface((32,64))
self.image.fill('red')
self.rect = self.image.get_rect(topleft = pos)
# Movement
self.direction = pygame.math.Vector2(0,0)
self.gravity = 0.8
def get_inputs(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
self.direction.y = -20
def apply_gravity(self):
self.direction.y += self.gravity
def update(self):
self.get_inputs()
self.apply_gravity()
# Game setup
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption('Platformer')
clock = pygame.time.Clock()
game = Game()
# Main
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill('black')
game.run()
pygame.display.update()
clock.tick(60)
Upvotes: 1
Views: 82
Reputation: 210909
This is a very common problem and is related to pygame.Rect
. Since pygame.Rect
is supposed to represent an area on the screen, a pygame.Rect
object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fraction part of the coordinates gets lost when the new offset is added to the position of the Rect object. If this is done every frame, the position error will accumulate over time.
self.rect.x += x_shift self.rect.y -= y_shift
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect
object. round
the coordinates and assign it to the location (e.g. .topleft
) of the rectangle:
class Tile(pygame.sprite.Sprite):
def __init__(self,pos):
super().__init__()
self.image = pygame.Surface((tile_size,tile_size))
self.image.fill('grey')
self.rect = self.image.get_rect(topleft = pos)
self.x = self.rect.x
self.y = self.rect.y
def update(self,x_shift,y_shift):
self.x += x_shift
self.y -= y_shift
self.rect.topleft = round(self.x), round(self.y)
Upvotes: 1