import pygame from pygame.locals import * from pygame import mixer import pickle from os import path # Initialize the mixer for sound pygame.mixer.pre_init(44100, -16, 2, 512) mixer.init() # Initialize pygame pygame.init() # Set up the game clock and frame rate clock = pygame.time.Clock() fps = 60 # Define the screen dimensions screen_width = 1000 screen_height = 1000 # Create the game screen screen = pygame.display.set_mode((screen_width, screen_height)) pygame.display.set_caption('Platformer') # Define fonts for text rendering font = pygame.font.SysFont('Bauhaus 93', 70) font_score = pygame.font.SysFont('Bauhaus 93', 30) # Define game variables tile_size = 50 # Size of tiles in the game game_over = 0 # Game over flag (-1 for game over, 0 for playing, 1 for level complete) main_menu = True # Flag to show main menu level = 1 # Current level max_levels = 7 # Total number of levels score = 0 # Player's score #define colours white = (255, 255, 255) blue = (0, 0, 255) #load images bg_img = pygame.image.load('assets/img/background.png') restart_img = pygame.image.load('assets/img/restart_btn.png') start_img = pygame.image.load('assets/img/start_btn.png') exit_img = pygame.image.load('assets/img/exit_btn.png') #load sounds pygame.mixer.music.load('assets/img/music.wav') pygame.mixer.music.play(-1, 0.0, 5000) coin_fx = pygame.mixer.Sound('assets/img/coin.wav') coin_fx.set_volume(0.5) jump_fx = pygame.mixer.Sound('assets/img/jump.wav') jump_fx.set_volume(0.5) game_over_fx = pygame.mixer.Sound('assets/img/game_over.wav') game_over_fx.set_volume(0.5) # Function to draw text on the screen def draw_text(text, font, text_col, x, y): img = font.render(text, True, text_col) screen.blit(img, (x, y)) #function to reset level def reset_level(level): player.reset(100, screen_height - 130) # Reset player position blob_group.empty() # Clear platform_group.empty() # Clear coin_group.empty() # Clear lava_group.empty() # Clear exit_group.empty() # Clear #load in level data and create world if path.exists(f'level{level}_data'): pickle_in = open(f'level{level}_data', 'rb') # Open level data file world_data = pickle.load(pickle_in) # Load level data from pickle file world = World(world_data) # Create world object based on loaded data #create dummy coin for showing the score score_coin = Coin(tile_size // 2, tile_size // 2) coin_group.add(score_coin) return world class Button(): def __init__(self, x, y, image): self.image = image # Button image self.rect = self.image.get_rect() # Button rectangle self.rect.x = x # Button x position self.rect.y = y # Button y position self.clicked = False # Clicked flag for button def draw(self): action = False # Action flag for button click #get mouse position pos = pygame.mouse.get_pos() #check mouseover and clicked conditions if self.rect.collidepoint(pos): if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False: action = True self.clicked = True if pygame.mouse.get_pressed()[0] == 0: self.clicked = False #draw button screen.blit(self.image, self.rect) return action # Return action flag class Player(): def __init__(self, x, y): self.reset(x, y) # Initialize player attributes def update(self, game_over): dx = 0 # Movement in x direction dy = 0 # Movement in y direction walk_cooldown = 5 # Cooldown for walking animation col_thresh = 20 # Collision threshold if game_over == 0: # If game is not over #get keypresses key = pygame.key.get_pressed() if key[pygame.K_SPACE] and self.jumped == False and self.in_air == False: jump_fx.play() # Play jump sound self.vel_y = -15 # Set vertical velocity for jump self.jumped = True # Set jumped flag if key[pygame.K_SPACE] == False: self.jumped = False # Reset jumped flag if key[pygame.K_LEFT]: dx -= 5 # Move left self.counter += 1 # Increment counter for animation self.direction = -1 # Set direction to left if key[pygame.K_RIGHT]: dx += 5 # Move right self.counter += 1 # Increment counter for animation self.direction = 1 # Set direction to right if key[pygame.K_LEFT] == False and key[pygame.K_RIGHT] == False: self.counter = 0 # Reset animation counter self.index = 0 # Reset animation index if self.direction == 1: self.image = self.images_right[self.index] # Set image to face right if self.direction == -1: self.image = self.images_left[self.index] # Set image to face left #handle animation if self.counter > walk_cooldown: self.counter = 0 # Reset counter self.index += 1 # Increment index for animation if self.index >= len(self.images_right): self.index = 0 if self.direction == 1: self.image = self.images_right[self.index] # Set image to face right if self.direction == -1: self.image = self.images_left[self.index] # Set image to face left #add gravity self.vel_y += 1 # Add gravity to vertical velocity if self.vel_y > 10: self.vel_y = 10 # Limit falling speed dy += self.vel_y # Adjust vertical position #check for collision self.in_air = True # Assume player is in the air for tile in world.tile_list: #check for collision in x direction if tile[1].colliderect(self.rect.x + dx, self.rect.y, self.width, self.height): dx = 0 # Reset horizontal movement #check for collision in y direction if tile[1].colliderect(self.rect.x, self.rect.y + dy, self.width, self.height): #check if below the ground i.e. jumping if self.vel_y < 0: dy = tile[1].bottom - self.rect.top self.vel_y = 0 # Reset vertical velocity #check if above the ground i.e. falling elif self.vel_y >= 0: dy = tile[1].top - self.rect.bottom # Adjust position self.vel_y = 0 # Reset vertical velocity self.in_air = False # Player is no longer in the air #check for collision with enemies if pygame.sprite.spritecollide(self, blob_group, False): game_over = -1 # Set game over flag game_over_fx.play() #check for collision with lava if pygame.sprite.spritecollide(self, lava_group, False): game_over = -1 # Set game over flag game_over_fx.play() #check for collision with exit if pygame.sprite.spritecollide(self, exit_group, False): game_over = 1 # Set game over flag #check for collision with platforms for platform in platform_group: #collision in the x direction if platform.rect.colliderect(self.rect.x + dx, self.rect.y, self.width, self.height): dx = 0 # Reset horizontal movement #collision in the y direction if platform.rect.colliderect(self.rect.x, self.rect.y + dy, self.width, self.height): #check if below platform if abs((self.rect.top + dy) - platform.rect.bottom) < col_thresh: self.vel_y = 0 # Reset vertical velocity dy = platform.rect.bottom - self.rect.top # Adjust position #check if above platform elif abs((self.rect.bottom + dy) - platform.rect.top) < col_thresh: self.rect.bottom = platform.rect.top - 1 # Adjust position self.in_air = False dy = 0 #move sideways with the platform if platform.move_x != 0: self.rect.x += platform.move_direction #update player coordinates self.rect.x += dx # Update horizontal position self.rect.y += dy # Update vertical position elif game_over == -1: self.image = self.dead_image draw_text('GAME OVER!', font, blue, (screen_width // 2) - 200, screen_height // 2) if self.rect.y > 200: self.rect.y -= 5 #draw player onto screen screen.blit(self.image, self.rect) # Draw player image return game_over # Return game over flag def reset(self, x, y): self.images_right = [] # List of images for facing right self.images_left = [] # List of images for facing left self.index = 0 # Animation index self.counter = 0 # Animation counter for num in range(1, 5): img_right = pygame.image.load(f'assets/img/guy{num}.png') # Load image for facing right img_right = pygame.transform.scale(img_right, (40, 80)) # Scale image img_left = pygame.transform.flip(img_right, True, False) # Flip image for facing left self.images_right.append(img_right) # Append image to list self.images_left.append(img_left) # Append image to list self.dead_image = pygame.image.load('assets/img/ghost.png') self.image = self.images_right[self.index] # Set initial image to face right self.rect = self.image.get_rect() # Get rectangle for image self.rect.x = x # Set initial x position self.rect.y = y # Set initial y position self.width = self.image.get_width() # Get width of image self.height = self.image.get_height() # Get height of image self.vel_y = 0 # Initial vertical velocity self.jumped = False self.direction = 0 # Direction (0 for none, 1 for right, -1 for left) self.in_air = True class World(): def __init__(self, data): self.tile_list = [] # List of tiles #load images dirt_img = pygame.image.load('assets/img/dirt.png') grass_img = pygame.image.load('assets/img/grass.png') row_count = 0 # Row count for tiles for row in data: # Loop through data rows col_count = 0 # Column count for tiles for tile in row: # Loop through data columns if tile == 1:# If tile is 1 (dirt) img = pygame.transform.scale(dirt_img, (tile_size, tile_size)) # Scale dirt image img_rect = img.get_rect() # Get rectangle for image img_rect.x = col_count * tile_size # Set x position img_rect.y = row_count * tile_size # Set y position tile = (img, img_rect) # Create tile tuple self.tile_list.append(tile) # Append tile to list if tile == 2: img = pygame.transform.scale(grass_img, (tile_size, tile_size)) img_rect = img.get_rect() img_rect.x = col_count * tile_size img_rect.y = row_count * tile_size tile = (img, img_rect) self.tile_list.append(tile) if tile == 3: blob = Enemy(col_count * tile_size, row_count * tile_size + 15) blob_group.add(blob) if tile == 4: platform = Platform(col_count * tile_size, row_count * tile_size, 1, 0) platform_group.add(platform) if tile == 5: platform = Platform(col_count * tile_size, row_count * tile_size, 0, 1) platform_group.add(platform) if tile == 6: lava = Lava(col_count * tile_size, row_count * tile_size + (tile_size // 2)) lava_group.add(lava) if tile == 7: coin = Coin(col_count * tile_size + (tile_size // 2), row_count * tile_size + (tile_size // 2)) coin_group.add(coin) if tile == 8: exit = Exit(col_count * tile_size, row_count * tile_size - (tile_size // 2)) exit_group.add(exit) col_count += 1 # Increment column count row_count += 1 # Increment row count # Function to draw world tiles def draw(self): for tile in self.tile_list: # Loop through tile list screen.blit(tile[0], tile[1]) # Draw tile on screen # Crreate Enemy Class class Enemy(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) # Initialize sprite self.image = pygame.image.load('assets/img/blob.png') self.rect = self.image.get_rect() # Get rectangle for image self.rect.x = x # Set x position self.rect.y = y # Set y position self.move_direction = 1 # Movement direction self.move_counter = 0 # Movement counter # Function to update enemy position def update(self): self.rect.x += self.move_direction # Update x position self.move_counter += 1 # Increment movement counter if abs(self.move_counter) > 50: # If movement counter exceeds tile size * 2 self.move_direction *= -1 # Reverse movement direction self.move_counter *= -1 # Update movement counter # Create Platform Class class Platform(pygame.sprite.Sprite): def __init__(self, x, y, move_x, move_y): pygame.sprite.Sprite.__init__(self) img = pygame.image.load('assets/img/platform.png') self.image = pygame.transform.scale(img, (tile_size, tile_size // 2)) self.rect = self.image.get_rect() self.rect.x = x # Set the x position of the rectangle self.rect.y = y # Set the y position of the rectangle self.move_counter = 0 self.move_direction = 1 self.move_x = move_x # Set the horizontal movement self.move_y = move_y # Set the vertical movement def update(self): self.rect.x += self.move_direction * self.move_x # Update the x position of the rectangle self.rect.y += self.move_direction * self.move_y # Update the y position of the rectangle self.move_counter += 1 # Increment the move counter if abs(self.move_counter) > 50: # Check if the move counter exceeds the threshold self.move_direction *= -1 # Reverse the move direction self.move_counter *= -1 # Reverse the move counter to keep it within bounds # Ctreate Lava Class class Lava(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) img = pygame.image.load('assets/img/lava.png') self.image = pygame.transform.scale(img, (tile_size, tile_size // 2)) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y # Create Coin Class class Coin(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) img = pygame.image.load('assets/img/coin.png') self.image = pygame.transform.scale(img, (tile_size // 2, tile_size // 2)) self.rect = self.image.get_rect() self.rect.center = (x, y) # Set the center position of the rectangle class Exit(pygame.sprite.Sprite): def __init__(self, x, y): pygame.sprite.Sprite.__init__(self) img = pygame.image.load('assets/img/exit.png') self.image = pygame.transform.scale(img, (tile_size, int(tile_size * 1.5))) self.rect = self.image.get_rect() self.rect.x = x self.rect.y = y player = Player(100, screen_height - 130) # Create a Player object at position (100, screen_height - 130) blob_group = pygame.sprite.Group() # Create a group to hold all blob (enemy) sprites platform_group = pygame.sprite.Group() # Create a group to hold all platform sprites lava_group = pygame.sprite.Group() # Create a group to hold all lava sprites coin_group = pygame.sprite.Group() # Create a group to hold all coin sprites exit_group = pygame.sprite.Group() # Create a group to hold all exit sprites # Create dummy coin for showing the score score_coin = Coin(tile_size // 2, tile_size // 2) # Create a Coin object at the center of the first tile coin_group.add(score_coin) # Add the dummy coin to the coin group # Load in level data and create world if path.exists(f'level{level}_data'): # Check if the level data file exists pickle_in = open(f'level{level}_data', 'rb') # Open the level data file in binary read mode world_data = pickle.load(pickle_in) # Load the level data using pickle world = World(world_data) # Create a World object using the loaded world data # Create buttons restart_button = Button(screen_width // 2 - 50, screen_height // 2 + 100, restart_img) # Create a restart button. # The x position is centered horizontally (screen_width // 2) and shifted left by 50 pixels. # The y position is centered vertically (screen_height // 2) and shifted down by 100 pixels. start_button = Button(screen_width // 2 - 350, screen_height // 2, start_img) # Create a start button. # The x position is centered horizontally (screen_width // 2) and shifted left by 350 pixels. # The y position is centered vertically (screen_height // 2) without any vertical offset. exit_button = Button(screen_width // 2 + 150, screen_height // 2, exit_img) # Create an exit button. # The x position is centered horizontally (screen_width // 2) and shifted right by 150 pixels. # The y position is centered vertically (screen_height // 2) without any vertical offset. run = True # Variable to control the main game loop while run: # Start of the main game loop clock.tick(fps) # Limit the frame rate to the defined frames per second (fps) screen.blit(bg_img, (0, 0)) # Draw the background image at the top-left corner (0, 0) if main_menu == True: # If the game is in the main menu state if exit_button.draw(): # If the exit button is clicked run = False # Exit the game loop if start_button.draw(): # If the start button is clicked main_menu = False # Exit the main menu and start the game else: # If the game is not in the main menu state world.draw() # Draw the game world if game_over == 0: # If the game is ongoing (not over) blob_group.update() # Update the positions of all blobs (enemies) platform_group.update() # Update the positions of all platforms # Update score and check if a coin has been collected if pygame.sprite.spritecollide(player, coin_group, True): # Check for collision between player and coins score += 1 # Increment score by 1 coin_fx.play() # Play coin collection sound draw_text('X ' + str(score), font_score, white, tile_size - 10, 10) # Draw the score on the screen blob_group.draw(screen) # Draw all blobs (enemies) on the screen platform_group.draw(screen) # Draw all platforms on the screen lava_group.draw(screen) # Draw all lava on the screen coin_group.draw(screen) # Draw all coins on the screen exit_group.draw(screen) # Draw all exits on the screen game_over = player.update(game_over) # Update the player and check if the game is over # If the player has died if game_over == -1: if restart_button.draw(): # If the restart button is clicked world_data = [] # Clear the world data world = reset_level(level) # Reset the level game_over = 0 # Reset game over state score = 0 # Reset score # If the player has completed the level if game_over == 1: # Reset game and go to next level level += 1 # Increment the level by 1 if level <= max_levels: # If the new level is within the maximum levels # Reset level world_data = [] # Clear the world data world = reset_level(level) # Reset the level game_over = 0 # Reset game over state else: draw_text('YOU WIN!', font, blue, (screen_width // 2) - 140, screen_height // 2) # Display win message if restart_button.draw(): # If the restart button is clicked level = 1 # Reset level to 1 # Reset level world_data = [] # Clear the world data world = reset_level(level) # Reset the level game_over = 0 # Reset game over state score = 0 # Reset score for event in pygame.event.get(): # Event handling loop if event.type == pygame.QUIT: # If the quit event is triggered run = False # Exit the game loop pygame.display.update() # Update the display pygame.quit() # Quit pygame