Implement game state management with menu and leaderboard functionality

This commit is contained in:
Félix MARQUET
2025-03-25 12:06:54 +01:00
parent 4f45b831e5
commit 9eb9fec690
5 changed files with 395 additions and 62 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 KiB

101
main.py
View File

@@ -1,5 +1,104 @@
import pygame
import sys
from pygame.locals import *
# Import from pygame_basics
from src.pygame_basics import (
WIDTH,
HEIGHT,
FPS,
displaysurface,
FramePerSec,
font,
initialize_game,
)
# Import from menu
from src.menu import Menu, Leaderboard
def main():
print("Hello World!")
# Game states
MENU = 0
PLAYING = 1
INFINITE = 2
LEADERBOARD = 3
# Initialize game state and objects
current_state = MENU
menu = Menu()
leaderboard = Leaderboard()
# Initialize game components
P1, PT1, platforms, all_sprites = initialize_game()
# Main game loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
if current_state in [PLAYING, INFINITE]:
current_state = MENU
else:
pygame.quit()
sys.exit()
# Handle menu events
if current_state == MENU:
action = menu.handle_event(event)
if action == "play":
current_state = PLAYING
elif action == "infinite":
current_state = INFINITE
elif action == "leaderboard":
current_state = LEADERBOARD
elif action == "quit":
pygame.quit()
sys.exit()
# Handle leaderboard events
elif current_state == LEADERBOARD:
action = leaderboard.handle_event(event)
if action == "menu":
current_state = MENU
# Clear screen
displaysurface.fill((0, 0, 0))
# Draw appropriate screen based on state
if current_state == MENU:
menu.draw(displaysurface)
elif current_state == PLAYING:
# Regular game code
P1.move()
P1.update()
for entity in all_sprites:
displaysurface.blit(entity.surf, entity.rect)
# Display FPS and coordinates
fps = int(FramePerSec.get_fps())
fps_text = font.render(f"FPS: {fps}", True, (255, 255, 255))
displaysurface.blit(fps_text, (10, 10))
pos_text = font.render(
f"X: {int(P1.pos.x)}, Y: {int(P1.pos.y)}", True, (255, 255, 255)
)
displaysurface.blit(pos_text, (10, 40))
elif current_state == INFINITE:
# Placeholder for infinite mode
text = font.render("Mode Infini - À implémenter", True, (255, 255, 255))
displaysurface.blit(text, (WIDTH // 2 - text.get_width() // 2, HEIGHT // 2))
elif current_state == LEADERBOARD:
leaderboard.draw(displaysurface)
pygame.display.update()
FramePerSec.tick(FPS)
if __name__ == "__main__":

176
src/menu.py Normal file
View File

@@ -0,0 +1,176 @@
import pygame
from pygame.locals import *
from src.pygame_basics import WIDTH, HEIGHT, font
class Menu:
def __init__(self):
self.buttons = []
button_width = 250
button_height = 60
button_spacing = 20
start_y = HEIGHT // 2 - 100
# Create buttons centered horizontally
self.buttons.append(
Button(
"Jouer",
WIDTH // 2 - button_width // 2,
start_y,
button_width,
button_height,
"play",
)
)
start_y += button_height + button_spacing
self.buttons.append(
Button(
"Jouer en mode infini",
WIDTH // 2 - button_width // 2,
start_y,
button_width,
button_height,
"infinite",
)
)
start_y += button_height + button_spacing
self.buttons.append(
Button(
"Classement",
WIDTH // 2 - button_width // 2,
start_y,
button_width,
button_height,
"leaderboard",
)
)
start_y += button_height + button_spacing
self.buttons.append(
Button(
"Quitter",
WIDTH // 2 - button_width // 2,
start_y,
button_width,
button_height,
"quit",
)
)
def draw(self, surface):
# Draw title
title = pygame.font.SysFont("Arial", 72).render(
"Project Sanic", True, (0, 191, 255)
)
title_rect = title.get_rect(center=(WIDTH // 2, HEIGHT // 4))
surface.blit(title, title_rect)
# Draw buttons
for button in self.buttons:
button.draw(surface)
def handle_event(self, event):
for button in self.buttons:
action = button.handle_event(event)
if action:
return action
return None
class Leaderboard:
def __init__(self):
self.tabs = ["Mode Normal", "Mode Infini"]
self.current_tab = 0
self.scores = {
0: [("Player1", 1000), ("Player2", 800), ("Player3", 600)],
1: [("Player1", 2000), ("Player2", 1500), ("Player3", 1200)],
}
self.back_button = Button("Retour", 20, HEIGHT - 70, 120, 50, "menu")
tab_width = 150
self.tab_buttons = [
Button(self.tabs[0], WIDTH // 2 - tab_width, 80, tab_width, 40, "tab_0"),
Button(self.tabs[1], WIDTH // 2, 80, tab_width, 40, "tab_1"),
]
def draw(self, surface):
# Draw title
title = pygame.font.SysFont("Arial", 48).render(
"Classement", True, (0, 191, 255)
)
title_rect = title.get_rect(center=(WIDTH // 2, 40))
surface.blit(title, title_rect)
# Draw tabs
for i, button in enumerate(self.tab_buttons):
if i == self.current_tab:
pygame.draw.rect(
surface,
(100, 149, 237),
(button.x, button.y, button.width, button.height),
)
button.draw(surface)
# Draw scores
y_pos = 150
for i, (name, score) in enumerate(self.scores[self.current_tab]):
rank_text = font.render(f"{i+1}. {name}: {score}", True, (255, 255, 255))
surface.blit(rank_text, (WIDTH // 2 - rank_text.get_width() // 2, y_pos))
y_pos += 40
self.back_button.draw(surface)
def handle_event(self, event):
action = self.back_button.handle_event(event)
if action:
return action
for i, button in enumerate(self.tab_buttons):
action = button.handle_event(event)
if action and action.startswith("tab_"):
self.current_tab = int(action.split("_")[1])
return None
class Button:
def __init__(self, text, x, y, width, height, action=None):
self.text = text
self.x = x
self.y = y
self.width = width
self.height = height
self.action = action
self.hover = False
def draw(self, surface):
# Button colors
color = (100, 149, 237) if self.hover else (65, 105, 225)
border_color = (255, 255, 255)
# Draw button with border
pygame.draw.rect(surface, color, (self.x, self.y, self.width, self.height))
pygame.draw.rect(
surface, border_color, (self.x, self.y, self.width, self.height), 2
)
# Draw text
text_surf = font.render(self.text, True, (255, 255, 255))
text_rect = text_surf.get_rect(
center=(self.x + self.width / 2, self.y + self.height / 2)
)
surface.blit(text_surf, text_rect)
def is_hover(self, pos):
return (
self.x <= pos[0] <= self.x + self.width
and self.y <= pos[1] <= self.y + self.height
)
def handle_event(self, event):
if event.type == MOUSEMOTION:
self.hover = self.is_hover(event.pos)
elif event.type == MOUSEBUTTONDOWN:
if self.hover and self.action:
return self.action
return None

View File

@@ -1,28 +1,32 @@
import pygame
from pygame.locals import *
import sys
import os
from pygame.locals import *
import time
import os
# Initialize Pygame
pygame.init()
vec = pygame.math.Vector2
HEIGHT = 450
WIDTH = 400
# Constants
WIDTH = 800
HEIGHT = 600
FPS = 60
VEC = pygame.math.Vector2
ACC = 0.5
FRIC = -0.12
FPS = 60
vec = pygame.math.Vector2
# Setup display
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Game")
FramePerSec = pygame.time.Clock()
displaysurface = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
WIDTH, HEIGHT = displaysurface.get_size()
pygame.display.set_caption("Project Sanic")
# Font setup
font = pygame.font.SysFont("Arial", 20)
# Initialize font for FPS counter
try:
font = pygame.font.SysFont("Arial", 24)
except:
font = pygame.font.Font(None, 24) # Default font if Arial is not available
# Global variables for access in other modules
platforms = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
class Player(pygame.sprite.Sprite):
@@ -31,11 +35,14 @@ class Player(pygame.sprite.Sprite):
# Animation variables
self.animation_frames = []
self.jump_frames = []
self.dash_frames = []
self.current_frame = 0
self.animation_speed = 0.1
self.last_update = time.time()
self.static_image = None
self.moving = False # Track if player is moving
self.moving = False
self.dashing = False
# Load static image and animation frames
self.load_images()
@@ -67,7 +74,7 @@ class Player(pygame.sprite.Sprite):
self.static_image, (120, 120)
)
# Load animation sprite sheet
# Load regular animation sprite sheet
if os.path.isfile("assets/player/Sanic Annimate.png"):
sprite_sheet = pygame.image.load(
"assets/player/Sanic Annimate.png"
@@ -85,44 +92,77 @@ class Player(pygame.sprite.Sprite):
frame = pygame.transform.scale(frame, (120, 120))
self.animation_frames.append(frame)
# Load jump animation sprite sheet
if os.path.isfile("assets/player/Sanic Boule.png"):
self.jump_frames.append(
pygame.transform.scale(
pygame.image.load(
"assets/player/Sanic Boule.png"
).convert_alpha(),
(120, 120),
)
)
# Load dash animation sprite sheet
if os.path.isfile("assets/player/Sanic Boule Annimate.png"):
dash_sheet = pygame.image.load(
"assets/player/Sanic Boule Annimate.png"
).convert_alpha()
# Extract the frames with 2000px gap
dash_frame_height = dash_sheet.get_height()
for i in range(4): # Assuming 4 frames
frame = dash_sheet.subsurface(
(i * 2000, 0, dash_frame_height, dash_frame_height)
)
frame = pygame.transform.scale(frame, (120, 120))
self.dash_frames.append(frame)
except Exception as e:
print(f"Error loading player images: {e}")
def update_animation(self):
if self.moving:
# Only animate when moving
if (
self.animation_frames
and time.time() - self.last_update > self.animation_speed
):
current_time = time.time()
# Priority: Dashing > Jumping > Moving > Static
if self.dashing and self.dash_frames:
if current_time - self.last_update > self.animation_speed:
self.current_frame = (self.current_frame + 1) % len(self.dash_frames)
self.surf = self.dash_frames[self.current_frame]
self.last_update = current_time
elif self.jumping and self.jump_frames:
self.surf = self.jump_frames[0] # Use jump frame
elif self.moving and self.animation_frames:
if current_time - self.last_update > self.animation_speed:
self.current_frame = (self.current_frame + 1) % len(
self.animation_frames
)
self.surf = self.animation_frames[self.current_frame]
self.last_update = time.time()
else:
# Use static image when not moving
if self.static_image:
self.surf = self.static_image
self.last_update = current_time
elif self.static_image:
self.surf = self.static_image
def dash(self, acc):
self.acc.x = 5 * acc
self.dashing = True # Set dashing flag
def move(self):
self.acc = vec(0, 1) # Gravity
# Reset moving flag
# Reset flags
self.moving = False
self.dashing = False
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_q]:
self.acc.x = -ACC
self.moving = True # Set moving to True when moving left
self.moving = True
if pressed_keys[K_a]:
self.dash(-ACC)
if pressed_keys[K_d]:
self.acc.x = ACC
self.moving = True # Set moving to True when moving right
self.moving = True
if pressed_keys[K_a]:
self.dash(ACC)
@@ -163,7 +203,7 @@ class Player(pygame.sprite.Sprite):
self.jumping = False
class platform(pygame.sprite.Sprite):
class Platform(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.surf = pygame.Surface((WIDTH, 20))
@@ -171,41 +211,59 @@ class platform(pygame.sprite.Sprite):
self.rect = self.surf.get_rect(center=(WIDTH / 2, HEIGHT - 10))
PT1 = platform()
P1 = Player()
platforms = pygame.sprite.Group()
platforms.add(PT1)
all_sprites = pygame.sprite.Group()
all_sprites.add(PT1)
all_sprites.add(P1)
def initialize_game():
global platforms, all_sprites
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
# Clear previous sprites if any
platforms.empty()
all_sprites.empty()
# Create new game objects
PT1 = Platform()
P1 = Player()
# Add them to the groups
platforms.add(PT1)
all_sprites.add(PT1)
all_sprites.add(P1)
return P1, PT1, platforms, all_sprites
def run_game(P1, all_sprites):
"""Run the main game loop without menu system"""
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
pygame.quit()
sys.exit()
displaysurface.fill((0, 0, 0))
displaysurface.fill((0, 0, 0))
P1.move()
P1.update()
for entity in all_sprites:
displaysurface.blit(entity.surf, entity.rect)
P1.move()
P1.update()
for entity in all_sprites:
displaysurface.blit(entity.surf, entity.rect)
# Display FPS
fps = int(FramePerSec.get_fps())
fps_text = font.render(f"FPS: {fps}", True, (255, 255, 255))
displaysurface.blit(fps_text, (10, 10))
# Display FPS
fps = int(FramePerSec.get_fps())
fps_text = font.render(f"FPS: {fps}", True, (255, 255, 255))
displaysurface.blit(fps_text, (10, 10))
# Display player coordinates
pos_text = font.render(
f"X: {int(P1.pos.x)}, Y: {int(P1.pos.y)}", True, (255, 255, 255)
)
displaysurface.blit(pos_text, (10, 40))
# Display player coordinates
pos_text = font.render(
f"X: {int(P1.pos.x)}, Y: {int(P1.pos.y)}", True, (255, 255, 255)
)
displaysurface.blit(pos_text, (10, 40))
pygame.display.update()
FramePerSec.tick(FPS)
pygame.display.update()
FramePerSec.tick(FPS)
if __name__ == "__main__":
P1, PT1, platforms, all_sprites = initialize_game()
run_game(P1, all_sprites)