Feat(Gamepad Support) - Implement joystick initialization and controls for player movement, jumping, and dashing; enhance input handling for both keyboard and joystick

This commit is contained in:
Félix MARQUET
2025-03-31 11:35:38 +02:00
parent 6dd45d771d
commit cb30ab52f6
2 changed files with 190 additions and 114 deletions

252
main.py
View File

@@ -54,121 +54,155 @@ def main():
clear_checkpoint_database()
projectiles = pygame.sprite.Group()
pygame.joystick.quit()
pygame.joystick.init()
joysticks = []
try:
for i in range(pygame.joystick.get_count()):
joystick = pygame.joystick.Joystick(i)
joystick.init()
joysticks.append(joystick)
print(f"Manette détectée: {joystick.get_name()}")
print(f"Nombre de boutons: {joystick.get_numbuttons()}")
print(f"Nombre d'axes: {joystick.get_numaxes()}")
except pygame.error:
print("Erreur lors de l'initialisation des manettes")
# 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()
elif event.key == K_F11:
fullscreen = not fullscreen
if fullscreen:
# Store current window size before going fullscreen
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = displaysurface.get_size()
displaysurface = pygame.display.set_mode(
(0, 0), pygame.FULLSCREEN
)
else:
# Return to windowed mode with previous size
displaysurface = pygame.display.set_mode(
(ORIGINAL_WIDTH, ORIGINAL_HEIGHT), pygame.RESIZABLE
)
elif (
event.type == VIDEORESIZE
): # Fixed indentation - moved out of K_F11 condition
if not fullscreen:
displaysurface = pygame.display.set_mode(
(event.w, event.h), pygame.RESIZABLE
)
# Update window dimensions
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = event.w, event.h
elif event.type == USEREVENT:
if event.action == "player_death":
db = CheckpointDB()
checkpoint_pos = db.get_checkpoint(level_file)
running = True
while running:
try:
events = []
try:
events = pygame.event.get()
except Exception as e:
print(f"Erreur lors de la récupération des événements: {e}")
pygame.joystick.quit()
pygame.joystick.init()
continue
if checkpoint_pos:
# Respawn player at checkpoint
P1, platforms, all_sprites, background, checkpoints = (
reset_game_with_checkpoint(level_file, game_resources)
for event in events:
if event.type == QUIT:
running = False
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()
elif event.key == K_F11:
fullscreen = not fullscreen
if fullscreen:
# Store current window size before going fullscreen
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = displaysurface.get_size()
displaysurface = pygame.display.set_mode(
(0, 0), pygame.FULLSCREEN
)
else:
# Return to windowed mode with previous size
displaysurface = pygame.display.set_mode(
(ORIGINAL_WIDTH, ORIGINAL_HEIGHT), pygame.RESIZABLE
)
elif (
event.type == VIDEORESIZE
): # Fixed indentation - moved out of K_F11 condition
if not fullscreen:
displaysurface = pygame.display.set_mode(
(event.w, event.h), pygame.RESIZABLE
)
projectiles.empty()
else:
# No checkpoint found, return to menu
current_state = MENU
if event.dict.get("action") == "create_projectile":
projectile = event.dict.get("projectile")
projectiles.add(projectile)
# Update window dimensions
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = event.w, event.h
elif event.type == USEREVENT:
if event.action == "player_death":
db = CheckpointDB()
checkpoint_pos = db.get_checkpoint(level_file)
# Handle menu events
if current_state == MENU:
if current_menu == "main":
action = main_menu.handle_event(event)
if action == "level_select":
level_select_menu = LevelSelectMenu(game_resources)
if checkpoint_pos:
# Respawn player at checkpoint
P1, platforms, all_sprites, background, checkpoints = (
reset_game_with_checkpoint(level_file, game_resources)
)
projectiles.empty()
else:
# No checkpoint found, return to menu
current_state = MENU
if event.dict.get("action") == "create_projectile":
projectile = event.dict.get("projectile")
projectiles.add(projectile)
# Handle menu events
if current_state == MENU:
if current_menu == "main":
action = main_menu.handle_event(event)
if action == "level_select":
level_select_menu = LevelSelectMenu(game_resources)
current_menu = "level_select"
elif action == "infinite":
current_state = INFINITE
elif action == "leaderboard":
current_state = LEADERBOARD
elif action == "quit":
pygame.quit()
sys.exit()
elif current_menu == "level_select":
action = level_select_menu.handle_event(event)
if action == "back_to_main":
current_menu = "main"
elif (
isinstance(action, dict)
and action.get("action") == "select_level"
):
level_file = action.get("level_file")
print(level_file)
(
P1,
PT1,
platforms,
all_sprites,
background,
checkpoints,
exits,
) = initialize_game(game_resources, level_file)
projectiles.empty()
current_state = PLAYING
elif action == "open_editor":
editor_select_menu = LevelEditorSelectionMenu(
game_resources
)
current_state = "editor_select"
# Handle leaderboard events
elif current_state == LEADERBOARD:
action = leaderboard.handle_event(event)
if action == "menu":
current_state = MENU
elif current_state == "editor_select":
action = editor_select_menu.handle_event(event)
if action == "back_to_levels":
current_state = MENU
current_menu = "level_select"
elif action == "infinite":
current_state = INFINITE
elif action == "leaderboard":
current_state = LEADERBOARD
elif action == "quit":
pygame.quit()
sys.exit()
elif current_menu == "level_select":
action = level_select_menu.handle_event(event)
if action == "back_to_main":
current_menu = "main"
elif (
isinstance(action, dict)
and action.get("action") == "select_level"
):
level_file = action.get("level_file")
print(level_file)
(
P1,
PT1,
platforms,
all_sprites,
background,
checkpoints,
exits,
) = initialize_game(game_resources, level_file)
projectiles.empty()
current_state = PLAYING
elif action == "open_editor":
editor_select_menu = LevelEditorSelectionMenu(game_resources)
elif isinstance(action, dict):
if action["action"] == "edit_level":
level_editor = LevelEditor(
game_resources, action["level_file"]
)
current_state = "level_editor"
elif action["action"] == "new_level":
level_editor = LevelEditor(game_resources)
current_state = "level_editor"
elif current_state == "level_editor":
result = level_editor.handle_event(event)
if result == "back_to_levels":
current_state = "editor_select"
# Handle leaderboard events
elif current_state == LEADERBOARD:
action = leaderboard.handle_event(event)
if action == "menu":
current_state = MENU
elif current_state == "editor_select":
action = editor_select_menu.handle_event(event)
if action == "back_to_levels":
current_state = MENU
current_menu = "level_select"
elif isinstance(action, dict):
if action["action"] == "edit_level":
level_editor = LevelEditor(game_resources, action["level_file"])
current_state = "level_editor"
elif action["action"] == "new_level":
level_editor = LevelEditor(game_resources)
current_state = "level_editor"
elif current_state == "level_editor":
result = level_editor.handle_event(event)
if result == "back_to_levels":
current_state = "editor_select"
except Exception as e:
print(f"Erreur lors du traitement de l'événement: {e}")
continue
# Clear screen
displaysurface.fill((0, 0, 0))

View File

@@ -11,6 +11,20 @@ class Player(Entity):
# Game ressources
self.game_resources = game_resources
self.has_joystick = False
self.joystick = None
self.jump_button = 0
self.dash_button = 1
try:
if pygame.joystick.get_count() > 0:
self.joystick = pygame.joystick.Joystick(0)
self.joystick.init()
self.has_joystick = True
except pygame.error:
self.has_joystick = False
# Animation variables
self.animation_frames = []
self.jump_frames = []
@@ -171,18 +185,46 @@ class Player(Entity):
# Reset flags
self.moving = False
# Keyboard controls
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_q]:
move_left = pressed_keys[K_q]
move_right = pressed_keys[K_d]
jump = pressed_keys[K_SPACE]
dash_key = pressed_keys[K_a]
if self.has_joystick and self.joystick:
try:
# Joystick gauche pour mouvement
if self.joystick.get_numaxes() > 0:
joystick_x = self.joystick.get_axis(0)
if abs(joystick_x) > 0.2:
if joystick_x < 0:
move_left = True
elif joystick_x > 0:
move_right = True
# Boutons pour sauter/dasher
if self.joystick.get_numbuttons() > self.jump_button:
if self.joystick.get_button(self.jump_button):
jump = True
if self.joystick.get_numbuttons() > self.dash_button:
if self.joystick.get_button(self.dash_button):
dash_key = True
except pygame.error:
pass # Ignorer les erreurs de manette
if move_left:
# Check if X is > 0 to prevent player from going off screen
if self.pos.x > 0:
self.acc.x = -self.game_resources.ACC
self.moving = True
if pressed_keys[K_a]:
if dash_key:
self.dash(-self.game_resources.ACC)
if pressed_keys[K_d]:
if move_right:
self.acc.x = self.game_resources.ACC
self.moving = True
if pressed_keys[K_a]:
if dash_key:
self.dash(self.game_resources.ACC)
# Also consider the player moving if they have significant horizontal velocity
@@ -190,7 +232,7 @@ class Player(Entity):
self.moving = True
# Jumping logic
if pressed_keys[K_SPACE] and not self.jumping:
if jump and not self.jumping:
self.vel.y = -30
self.jumping = True