Code for How to Create a Platformer Game in Python Tutorial


View on Github

game.py

import pygame from settings import HEIGHT, WIDTH pygame.font.init() class Game:	def __init__(self, screen):	self.screen = screen	self.font = pygame.font.SysFont("impact", 70)	self.message_color = pygame.Color("darkorange")	# if player ran out of life or fell below the platform	def _game_lose(self, player):	player.game_over = True	message = self.font.render('You Lose...', True, self.message_color)	self.screen.blit(message,(WIDTH // 3 + 70, 70))	# if player reach the goal	def _game_win(self, player):	player.game_over = True	player.win = True	message = self.font.render('You Win!!', True, self.message_color)	self.screen.blit(message,(WIDTH // 3, 70))	# checks if the game is over or not, and if win or lose	def game_state(self, player, goal):	if player.life <= 0 or player.rect.y >= HEIGHT:	self._game_lose(player)	elif player.rect.colliderect(goal.rect):	self._game_win(player)	else:	None	def show_life(self, player):	life_size = 30	img_path = "assets/life/life.png"	life_image = pygame.image.load(img_path)	life_image = pygame.transform.scale(life_image, (life_size, life_size))	# life_rect = life_image.get_rect(topleft = pos)	indent = 0	for life in range(player.life):	indent += life_size	self.screen.blit(life_image, (indent, life_size))

player.py

import pygame from support import import_sprite class Player(pygame.sprite.Sprite):	def __init__(self, pos):	super().__init__()	self._import_character_assets()	self.frame_index = 0	self.animation_speed = 0.15	self.image = self.animations["idle"][self.frame_index]	self.rect = self.image.get_rect(topleft = pos)	self.mask = pygame.mask.from_surface(self.image)	# player movement	self.direction = pygame.math.Vector2(0, 0)	self.speed = 5	self.jump_move = -16	# player status	self.life = 5	self.game_over = False	self.win = False	self.status = "idle"	self.facing_right = True	self.on_ground = False	self.on_ceiling = False	self.on_left = False	self.on_right = False	# gets all the image needed for animating specific player action	def _import_character_assets(self):	character_path = "assets/player/"	self.animations = {	"idle": [],	"walk": [],	"jump": [],	"fall": [],	"lose": [],	"win": []	}	for animation in self.animations.keys():	full_path = character_path + animation	self.animations[animation] = import_sprite(full_path)	# animates the player actions	def _animate(self):	animation = self.animations[self.status]	# loop over frame index	self.frame_index += self.animation_speed	if self.frame_index >= len(animation):	self.frame_index = 0	image = animation[int(self.frame_index)]	image = pygame.transform.scale(image, (35, 50))	if self.facing_right:	self.image = image	else:	flipped_image = pygame.transform.flip(image, True, False)	self.image = flipped_image	# set the rect	if self.on_ground and self.on_right:	self.rect = self.image.get_rect(bottomright = self.rect.bottomright)	elif self.on_ground and self.on_left:	self.rect = self.image.get_rect(bottomleft = self.rect.bottomleft)	elif self.on_ground:	self.rect = self.image.get_rect(midbottom = self.rect.midbottom)	elif self.on_ceiling and self.on_right:	self.rect = self.image.get_rect(topright = self.rect.topright)	elif self.on_ceiling and self.on_left:	self.rect = self.image.get_rect(bottomleft = self.rect.topleft)	elif self.on_ceiling:	self.rect = self.image.get_rect(midtop = self.rect.midtop)	# checks if the player is moving towards left or right or not moving	def _get_input(self, player_event):	if player_event != False:	if player_event == "right":	self.direction.x = 1	self.facing_right = True	elif player_event == "left":	self.direction.x = -1	self.facing_right = False	else:	self.direction.x = 0	def _jump(self):	self.direction.y = self.jump_move	# identifies player action	def _get_status(self):	if self.direction.y < 0:	self.status = "jump"	elif self.direction.y > 1:	self.status = "fall"	elif self.direction.x != 0:	self.status = "walk"	else:	self.status = "idle"	# update the player's state	def update(self, player_event):	self._get_status()	if self.life > 0 and not self.game_over:	if player_event == "space" and self.on_ground:	self._jump()	else:	self._get_input(player_event)	elif self.game_over and self.win:	self.direction.x = 0	self.status = "win"	else:	self.direction.x = 0	self.status = "lose"	self._animate() 

world.py

import pygame from settings import tile_size, WIDTH from tile import Tile from trap import Trap from goal import Goal from player import Player from game import Game class World:	def __init__(self, world_data, screen):	self.screen = screen	self.world_data = world_data	self._setup_world(world_data)	self.world_shift = 0	self.current_x = 0	self.gravity = 0.7	self.game = Game(self.screen)	# generates the world	def _setup_world(self, layout):	self.tiles = pygame.sprite.Group()	self.traps = pygame.sprite.Group()	self.player = pygame.sprite.GroupSingle()	self.goal = pygame.sprite.GroupSingle()	for row_index, row in enumerate(layout):	for col_index, cell in enumerate(row):	x, y = col_index * tile_size, row_index * tile_size	if cell == "X":	tile = Tile((x, y), tile_size)	self.tiles.add(tile)	elif cell == "t":	tile = Trap((x + (tile_size // 4), y + (tile_size // 4)), tile_size // 2)	self.traps.add(tile)	elif cell == "P":	player_sprite = Player((x, y))	self.player.add(player_sprite)	elif cell == "G":	goal_sprite = Goal((x, y), tile_size)	self.goal.add(goal_sprite)	# world scroll when the player is walking towards left/right	def _scroll_x(self):	player = self.player.sprite	player_x = player.rect.centerx	direction_x = player.direction.x	if player_x < WIDTH // 3 and direction_x < 0:	self.world_shift = 8	player.speed = 0	elif player_x > WIDTH - (WIDTH // 3) and direction_x > 0:	self.world_shift = -8	player.speed = 0	else:	self.world_shift = 0	player.speed = 3	# add gravity for player to fall	def _apply_gravity(self, player):	player.direction.y += self.gravity	player.rect.y += player.direction.y	# prevents player to pass through objects horizontally	def _horizontal_movement_collision(self):	player = self.player.sprite	player.rect.x += player.direction.x * player.speed	for sprite in self.tiles.sprites():	if sprite.rect.colliderect(player.rect):	# checks if moving towards left	if player.direction.x < 0:	player.rect.left = sprite.rect.right	player.on_left = True	self.current_x = player.rect.left	# checks if moving towards right	elif player.direction.x > 0:	player.rect.right = sprite.rect.left	player.on_right = True	self.current_x = player.rect.right	if player.on_left and (player.rect.left < self.current_x or player.direction.x >= 0):	player.on_left = False	if player.on_right and (player.rect.right > self.current_x or player.direction.x <= 0):	player.on_right = False	# prevents player to pass through objects vertically	def _vertical_movement_collision(self):	player = self.player.sprite	self._apply_gravity(player)	for sprite in self.tiles.sprites():	if sprite.rect.colliderect(player.rect):	# checks if moving towards bottom	if player.direction.y > 0:	player.rect.bottom = sprite.rect.top	player.direction.y = 0	player.on_ground = True	# checks if moving towards up	elif player.direction.y < 0:	player.rect.top = sprite.rect.bottom	player.direction.y = 0	player.on_ceiling = True	if player.on_ground and player.direction.y < 0 or player.direction.y > 1:	player.on_ground = False	if player.on_ceiling and player.direction.y > 0:	player.on_ceiling = False	# add consequences when player run through traps	def _handle_traps(self):	player = self.player.sprite	for sprite in self.traps.sprites():	if sprite.rect.colliderect(player.rect):	if player.direction.x < 0 or player.direction.y > 0:	player.rect.x += tile_size	elif player.direction.x > 0 or player.direction.y > 0:	player.rect.x -= tile_size	player.life -= 1	# updating the game world from all changes commited	def update(self, player_event):	# for tile	self.tiles.update(self.world_shift)	self.tiles.draw(self.screen)	# for trap	self.traps.update(self.world_shift)	self.traps.draw(self.screen)	# for goal	self.goal.update(self.world_shift)	self.goal.draw(self.screen)	self._scroll_x()	# for player	self._horizontal_movement_collision()	self._vertical_movement_collision()	self._handle_traps()	self.player.update(player_event)	self.game.show_life(self.player.sprite)	self.player.draw(self.screen)	self.game.game_state(self.player.sprite, self.goal.sprite) 

settings.py

world_map = [	' ',	' ',	' t t ',	' X XXXXXXXXXs XX X ',	' tXXXt XX XX XXXX tt XX ',	' XX XX XXXXX ',	' Xt t t t X G ',	' XXXXXX XXXXs XXXXXXXXXXX XX tt t XXX',	' P XX X XX X X XXXt X XX XX XXX XXXXXXXXs XXXXXX ',	'XXXXXXX X X X X XXXXXXXXX XX XX XXX XX XX XXXXXXX X ', ] tile_size = 50 WIDTH, HEIGHT = 1000, len(world_map) * tile_size

trap.py

import pygame from support import import_sprite class Trap(pygame.sprite.Sprite):	def __init__(self, pos, size):	super().__init__()	self.blade_img = import_sprite("assets/trap/blade")	self.frame_index = 0	self.animation_delay = 3	self.image = self.blade_img[self.frame_index]	self.image = pygame.transform.scale(self.image, (size, size))	self.mask = pygame.mask.from_surface(self.image)	self.rect = self.image.get_rect(topleft = pos)	# adds the spinning effect to the Blade trap	def _animate(self):	sprites = self.blade_img	sprite_index = (self.frame_index // self.animation_delay) % len(sprites)	self.image = sprites[sprite_index]	self.frame_index += 1	self.rect = self.image.get_rect(topleft=(self.rect.x, self.rect.y))	self.mask = pygame.mask.from_surface(self.image)	if self.frame_index // self.animation_delay > len(sprites):	self.frame_index = 0	# update object position due to world scroll	def update(self, x_shift):	self._animate()	self.rect.x += x_shift 

goal.py

import pygame class Goal(pygame.sprite.Sprite):	def __init__(self, pos, size):	super().__init__()	img_path = 'assets/goal/gate.png'	self.image = pygame.image.load(img_path)	self.image = pygame.transform.scale(self.image, (size, size))	self.rect = self.image.get_rect(topleft = pos)	# update object position due to world scroll	def update(self, x_shift):	self.rect.x += x_shift

tile.py

import pygame class Tile(pygame.sprite.Sprite):	def __init__(self, pos, size):	super().__init__()	img_path = 'assets/terrain/stone.jpg'	self.image = pygame.image.load(img_path)	self.image = pygame.transform.scale(self.image, (size, size))	self.rect = self.image.get_rect(topleft = pos)	# update object position due to world scroll	def update(self, x_shift):	self.rect.x += x_shift

main.py

import pygame, sys from settings import * from world import World pygame.init() screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Platformer") class Platformer:	def __init__(self, screen, width, height):	self.screen = screen	self.clock = pygame.time.Clock()	self.player_event = False	self.bg_img = pygame.image.load('assets/terrain/bg.jpg')	self.bg_img = pygame.transform.scale(self.bg_img, (width, height))	def main(self):	world = World(world_map, self.screen)	while True:	self.screen.blit(self.bg_img, (0, 0))	for event in pygame.event.get():	if event.type == pygame.QUIT:	pygame.quit()	sys.exit()	elif event.type == pygame.KEYDOWN:	if event.key == pygame.K_LEFT:	self.player_event = "left"	if event.key == pygame.K_RIGHT:	self.player_event = "right"	if event.key == pygame.K_SPACE:	self.player_event = "space"	elif event.type == pygame.KEYUP:	self.player_event = False	world.update(self.player_event)	pygame.display.update()	self.clock.tick(60) if __name__ == "__main__":	play = Platformer(screen, WIDTH, HEIGHT)	play.main()

support.py

from os import walk import pygame def import_sprite(path):	surface_list = []	for _, __, img_file in walk(path):	for image in img_file:	full_path = f"{path}/{image}"	img_surface = pygame.image.load(full_path).convert_alpha()	surface_list.append(img_surface)	return surface_list