/
ap1k
/
PyGame_Practice
Обзор
Документация
Войти
/
ap1k
/
PyGame_Practice
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/scenes/game_scene.py
355 строк
15 KB
Roman
The all game
27 май 2025, 15:26
27 май 2025, 15:26
4ea7de9
Код
Авторство
О чём код?
""" GameScene class - represents the main gameplay scene """ import pygame import random import time import math from src.core.scene import Scene from src.entities.player import Player from src.patterns.enemy_factory import EnemyFactory from src.utils.resource_loader import ResourceLoader class GameScene(Scene): """ Main gameplay scene Implements the Scene interface """ def __init__(self, game): super().__init__(game) # Create the player self.player = Player(game.width // 2 - 20, game.height - 60) # Lists to store game entities self.bullets = [] self.enemies = [] # Enemy spawning self.last_enemy_spawn = 0 self.enemy_spawn_delay = 2000 # milliseconds # Shooting control self.shooting = False self.last_auto_shoot_time = 0 # Background self.background = ResourceLoader().get_image("background") # Game over flag self.game_over = False self.game_over_time = 0 # Fonts self.font_small = ResourceLoader().get_font("small") self.font_medium = ResourceLoader().get_font("medium") self.font_large = ResourceLoader().get_font("large") def update(self): """Update the game state""" # Don't update if the game is over if self.game_over: current_time = time.time() # Wait 3 seconds before going to the leaderboard if current_time - self.game_over_time > 3: self.game.change_scene("leaderboard") return # Update the player self.player.update() # Handle continuous shooting if space is held down current_time = pygame.time.get_ticks() if self.shooting: # Get the cooldown for the current weapon weapon = self.player.weapons[self.player.current_weapon] if current_time - self.last_auto_shoot_time > weapon.cooldown: bullet = self.player.shoot() if bullet: self.bullets.append(bullet) self.last_auto_shoot_time = current_time # Update bullets for bullet in self.bullets[:]: bullet.update() # Remove inactive bullets if not bullet.active: self.bullets.remove(bullet) # Update enemies for enemy in self.enemies[:]: enemy.update() # Check for collisions with bullets for bullet in self.bullets[:]: if enemy.is_colliding(bullet): # Enemy takes damage if enemy.take_damage(bullet.damage): # Enemy was destroyed self.game.state.score += enemy.score_value self.game.state.enemies_destroyed += 1 # Remove the bullet bullet.active = False if bullet in self.bullets: self.bullets.remove(bullet) # Check for collisions with the player if self.player.is_colliding(enemy): # Player takes damage game_over = self.player.take_damage(enemy.damage) # Deactivate the enemy enemy.active = False # Check if the game is over if game_over: self.game_over = True self.game_over_time = time.time() self.game.state.time_played = self.game.elapsed_time # Remove inactive enemies if not enemy.active: self.enemies.remove(enemy) # Spawn new enemies current_time = pygame.time.get_ticks() if current_time - self.last_enemy_spawn > self.enemy_spawn_delay: self.spawn_enemy() self.last_enemy_spawn = current_time # Gradually decrease the spawn delay to increase difficulty self.enemy_spawn_delay = max(500, self.enemy_spawn_delay - 10) def render(self, screen): """Render the game scene""" # Draw the background screen.blit(self.background, (0, 0)) # Draw the player self.player.render(screen) # Draw bullets for bullet in self.bullets: bullet.render(screen) # Draw enemies for enemy in self.enemies: enemy.render(screen) # Draw the HUD (Heads-Up Display) self.render_hud(screen) # Draw game over message if the game is over if self.game_over: self.render_game_over(screen) def handle_event(self, event): """Handle pygame events""" if event.type == pygame.KEYDOWN: # Start shooting when space is pressed if event.key == pygame.K_SPACE: self.shooting = True # Shoot immediately when key is first pressed bullet = self.player.shoot() if bullet: self.bullets.append(bullet) self.last_auto_shoot_time = pygame.time.get_ticks() # Pause the game when escape is pressed elif event.key == pygame.K_ESCAPE: self.game.state.paused = not self.game.state.paused # Switch weapons with number keys elif event.key == pygame.K_1: self.player.switch_weapon(0) elif event.key == pygame.K_2: self.player.switch_weapon(1) elif event.key == pygame.K_3: self.player.switch_weapon(2) # Switch weapons with Q and E elif event.key == pygame.K_q: current = self.player.current_weapon self.player.switch_weapon((current - 1) % len(self.player.weapons)) elif event.key == pygame.K_e: current = self.player.current_weapon self.player.switch_weapon((current + 1) % len(self.player.weapons)) # Stop shooting when space is released elif event.type == pygame.KEYUP: if event.key == pygame.K_SPACE: self.shooting = False # Handle mouse events elif event.type == pygame.MOUSEBUTTONDOWN: # Left mouse button to shoot if event.button == 1: bullet = self.player.shoot() if bullet: self.bullets.append(bullet) # Mouse wheel to switch weapons elif event.button == 4: # Scroll up current = self.player.current_weapon self.player.switch_weapon((current - 1) % len(self.player.weapons)) elif event.button == 5: # Scroll down current = self.player.current_weapon self.player.switch_weapon((current + 1) % len(self.player.weapons)) def spawn_enemy(self): """Spawn a new enemy""" enemy = EnemyFactory.create_enemy() self.enemies.append(enemy) def render_hud(self, screen): """Render the Heads-Up Display""" config = self.game.config # Define HUD dimensions and position hud_width = 220 hud_height = 220 hud_x = 10 hud_y = 10 padding = 15 # Create a semi-transparent HUD background with a border hud_surface = pygame.Surface((hud_width, hud_height), pygame.SRCALPHA) hud_surface.fill((0, 0, 0, 160)) # Semi-transparent black screen.blit(hud_surface, (hud_x, hud_y)) pygame.draw.rect(screen, config.colors["light_blue"], (hud_x, hud_y, hud_width, hud_height), 1) # Current position for text elements text_x = hud_x + padding text_y = hud_y + padding line_height = 30 # Space between lines # Draw the score with a nice color score_text = self.font_small.render(f"Score: {self.game.state.score}", True, config.colors["yellow"]) screen.blit(score_text, (text_x, text_y)) text_y += line_height # Draw the enemies destroyed enemies_text = self.font_small.render(f"Enemies: {self.game.state.enemies_destroyed}", True, config.colors["orange"]) screen.blit(enemies_text, (text_x, text_y)) text_y += line_height # Draw the time played minutes = int(self.game.elapsed_time) // 60 seconds = int(self.game.elapsed_time) % 60 time_text = self.font_small.render(f"Time: {minutes:02d}:{seconds:02d}", True, config.colors["cyan"]) screen.blit(time_text, (text_x, text_y)) text_y += line_height # Draw the current weapon with its color weapon_name = config.weapon_names[self.player.current_weapon] weapon_color = config.weapon_colors[self.player.current_weapon] weapon_text = self.font_small.render(f"Weapon: {weapon_name}", True, weapon_color) screen.blit(weapon_text, (text_x, text_y)) text_y += line_height # Draw the player's health on a new line health_text = self.font_small.render(f"Health:", True, config.colors["green"]) screen.blit(health_text, (text_x, text_y)) text_y += line_height # Health bar background - full width for better visibility health_bar_x = text_x health_bar_y = text_y health_bar_width = hud_width - (padding * 2) # Full width minus padding health_bar_height = 20 # Taller for better visibility pygame.draw.rect(screen, config.colors["dark_gray"], (health_bar_x, health_bar_y, health_bar_width, health_bar_height)) # Health bar fill health_percent = self.player.health / 100 health_color = config.colors["green"] if health_percent < 0.3: health_color = config.colors["red"] elif health_percent < 0.6: health_color = config.colors["yellow"] pygame.draw.rect(screen, health_color, (health_bar_x, health_bar_y, int(health_bar_width * health_percent), health_bar_height)) # Show health value as text inside the health bar health_value_text = self.font_small.render(f"{self.player.health}/100", True, config.colors["white"]) health_value_rect = health_value_text.get_rect(center=(health_bar_x + health_bar_width // 2, health_bar_y + health_bar_height // 2)) screen.blit(health_value_text, health_value_rect) text_y += health_bar_height + 5 # Add a small gap after the health bar text_y += line_height # Draw the player's lives lives_text = self.font_small.render(f"Lives: ", True, config.colors["light_blue"]) screen.blit(lives_text, (text_x, text_y)) # Draw life icons life_icon_x = text_x + 70 for i in range(self.player.lives): pygame.draw.circle(screen, config.colors["light_blue"], (life_icon_x + i * 25, text_y + 10), 10) # Add a white border to the life icon pygame.draw.circle(screen, config.colors["white"], (life_icon_x + i * 25, text_y + 10), 10, 1) text_y += line_height # Draw weapon selection hint hint_text = self.font_small.render("[Q/E] Switch Weapon", True, config.colors["light_gray"]) screen.blit(hint_text, (text_x, text_y)) # Draw controls hint at the bottom of the screen controls_text = self.font_small.render("WASD/Arrows: Move | Space: Shoot | ESC: Pause", True, config.colors["white"]) controls_rect = controls_text.get_rect(center=(self.game.width // 2, self.game.height - 20)) screen.blit(controls_text, controls_rect) def render_game_over(self, screen): """Render the game over message""" config = self.game.config # Draw a semi-transparent overlay with a gradient effect overlay = pygame.Surface((self.game.width, self.game.height), pygame.SRCALPHA) for i in range(0, self.game.height, 2): alpha = min(200, 100 + i // 3) # Gradient alpha from top to bottom pygame.draw.line(overlay, (0, 0, 0, alpha), (0, i), (self.game.width, i)) screen.blit(overlay, (0, 0)) # Draw a decorative border around the game over text border_width = 400 border_height = 250 border_x = (self.game.width - border_width) // 2 border_y = (self.game.height - border_height) // 2 # Draw the border with a pulsating effect pulse = (math.sin(pygame.time.get_ticks() / 200) + 1) / 2 # Value between 0 and 1 border_color = (int(200 * pulse) + 55, 0, 0) # Pulsating red pygame.draw.rect(screen, border_color, (border_x, border_y, border_width, border_height), 3) # Draw the game over text with a shadow effect game_over_text = self.font_large.render("GAME OVER", True, config.colors["red"]) shadow_text = self.font_large.render("GAME OVER", True, config.colors["black"]) text_rect = game_over_text.get_rect(center=(self.game.width // 2, self.game.height // 2 - 70)) screen.blit(shadow_text, (text_rect.x + 3, text_rect.y + 3)) # Shadow offset screen.blit(game_over_text, text_rect) # Draw the final score with a glowing effect score_color = (255, int(200 + 55 * pulse), 0) # Pulsating yellow-orange score_text = self.font_medium.render(f"Final Score: {self.game.state.score}", True, score_color) score_rect = score_text.get_rect(center=(self.game.width // 2, self.game.height // 2 - 10)) screen.blit(score_text, score_rect) # Draw the stats enemies_text = self.font_small.render(f"Enemies Destroyed: {self.game.state.enemies_destroyed}", True, config.colors["light_blue"]) enemies_rect = enemies_text.get_rect(center=(self.game.width // 2, self.game.height // 2 + 30)) screen.blit(enemies_text, enemies_rect) minutes = int(self.game.elapsed_time) // 60 seconds = int(self.game.elapsed_time) % 60 time_text = self.font_small.render(f"Time Survived: {minutes:02d}:{seconds:02d}", True, config.colors["light_blue"]) time_rect = time_text.get_rect(center=(self.game.width // 2, self.game.height // 2 + 60)) screen.blit(time_text, time_rect) # Draw the "Going to leaderboard" text with a blinking effect if int(pygame.time.get_ticks() / 500) % 2 == 0: # Blink every 500ms leaderboard_text = self.font_medium.render("Going to leaderboard...", True, config.colors["white"]) leaderboard_rect = leaderboard_text.get_rect(center=(self.game.width // 2, self.game.height // 2 + 110)) screen.blit(leaderboard_text, leaderboard_rect)