Программа не возвращается в меню по условию
При смерти или победе игрока вместо выхода в меню закрывается окно программы
import pygame
import sys
import random
import json
from PIL import Image, ImageSequence
# Инициализация Pygame
pygame.init()
# Настройки экрана
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Mini Mario")
# Цвета
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)
# Загрузка пользовательского шрифта
custom_font = pygame.font.Font('assets/fonts/EnterCommand.ttf', 36)
# Часы для контроля FPS
clock = pygame.time.Clock()
FPS = 60
# Загрузка звуков
jump_sound = pygame.mixer.Sound('assets/sounds/jump.wav')
hit_sound = pygame.mixer.Sound('assets/sounds/hit.wav')
# Группы спрайтов
all_sprites = pygame.sprite.Group()
platforms = pygame.sprite.Group()
enemies = pygame.sprite.Group()
coins = pygame.sprite.Group()
flag_group = pygame.sprite.Group()
# Функция загрузки GIF
def load_gif(filename, size=None):
"""Загружает все кадры из GIF и преобразует их в формат Pygame."""
try:
pil_image = Image.open(filename)
except FileNotFoundError:
print(f"Файл не найден: {filename}")
return []
frames = []
for frame in ImageSequence.Iterator(pil_image):
frame = frame.convert("RGBA")
if size:
frame = frame.resize(size, Image.Resampling.LANCZOS)
pygame_image = pygame.image.fromstring(frame.tobytes(), frame.size, frame.mode)
frames.append(pygame_image)
return frames
# Класс камеры
class Camera:
def __init__(self, width, height):
self.width = width
self.height = height
self.offset_x = 0
self.offset_y = 0
def apply(self, entity):
"""Применяет смещение камеры к объекту."""
return entity.rect.move(-self.offset_x, -self.offset_y)
def update(self, target):
"""Обновляет положение камеры относительно цели (игрока)."""
self.offset_x = max(0, target.rect.x - WIDTH // 2)
self.offset_x = min(self.offset_x, self.width - WIDTH)
# Класс игрока
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.running_frames = load_gif('assets/images/player/running_mario.gif', size=(60, 60))
self.current_running_frame = 0
self.standing_image = pygame.transform.scale(
pygame.image.load('assets/images/player/standing_mario.png'), (60, 60))
self.jumping_image = pygame.transform.scale(
pygame.image.load('assets/images/player/jumping_mario.png'), (60, 60))
self.image = self.standing_image
self.rect = self.image.get_rect(center=(100, HEIGHT - 100))
self.velocity_y = 0
self.on_ground = False
self.is_jumping = False
self.is_moving = False
self.lives = 3 # Добавляем жизни
def update(self):
if self.is_moving and not self.is_jumping:
self.current_running_frame += 0.2
if self.current_running_frame >= len(self.running_frames):
self.current_running_frame = 0
self.image = self.running_frames[int(self.current_running_frame)]
elif self.is_jumping:
self.image = self.jumping_image
else:
self.image = self.standing_image
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= 5
self.is_moving = True
elif keys[pygame.K_RIGHT]:
self.rect.x += 5
self.is_moving = True
else:
self.is_moving = False
self.velocity_y += 1
if self.velocity_y > 10:
self.velocity_y = 10
self.rect.y += self.velocity_y
self.on_ground = False
for platform in platforms:
if self.rect.colliderect(platform.rect) and self.velocity_y > 0:
self.rect.bottom = platform.rect.top
self.velocity_y = 0
self.on_ground = True
self.is_jumping = False
def jump(self):
if self.on_ground:
self.velocity_y = -15
self.is_jumping = True
jump_sound.play()
# Класс платформы
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, width, height):
super().__init__()
self.image = pygame.Surface((width, height))
self.image.fill(GREEN)
self.rect = self.image.get_rect(topleft=(x, y))
# Класс монеты
class Coin(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.frames = load_gif('assets/images/coins/coin_animation.gif', size=(20, 20))
self.current_frame = 0
self.image = self.frames[self.current_frame]
self.rect = self.image.get_rect(topleft=(x, y))
def update(self):
self.current_frame += 0.2
if self.current_frame >= len(self.frames):
self.current_frame = 0
self.image = self.frames[int(self.current_frame)]
# Класс обычного врага
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.frames = load_gif('assets/images/enemies/enemy_animation.gif', size=(40, 40))
self.current_frame = 0
self.image = self.frames[self.current_frame]
self.rect = self.image.get_rect(topleft=(x, y))
self.speed = random.choice([2, -2])
self.direction = 1
def update(self):
self.current_frame += 0.2
if self.current_frame >= len(self.frames):
self.current_frame = 0
self.image = self.frames[int(self.current_frame)]
self.rect.x += self.speed * self.direction
if self.rect.left <= 0 or self.rect.right >= WIDTH * 2:
self.direction *= -1
# Класс флага
class Flag(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
try:
self.image = pygame.image.load('assets/images/flag.png')
self.image = pygame.transform.scale(self.image, (50, 100))
except FileNotFoundError:
print("Файл flag.png не найден! Проверьте путь.")
self.image = pygame.Surface((50, 100))
self.image.fill(RED)
self.rect = self.image.get_rect(topleft=(x, y))
# Класс кнопки
class Button(pygame.sprite.Sprite):
def __init__(self, x, y, image, action=None):
super().__init__()
self.image = image
self.rect = self.image.get_rect()
self.rect.topleft = (x, y)
self.action = action
def check_click(self, mouse_pos):
if self.rect.collidepoint(mouse_pos):
return self.action
return None
# Функция отображения меню
def show_menu():
global game_state
menu_background = pygame.image.load('assets/images/menu/menu_background.png')
start_button_img = pygame.image.load('assets/images/menu/start_button.png')
quit_button_img = pygame.image.load('assets/images/menu/quit_button.png')
buttons = pygame.sprite.Group()
start_button = Button(WIDTH // 2 - 100, HEIGHT // 2 - 50, start_button_img, "start")
quit_button = Button(WIDTH // 2 - 100, HEIGHT // 2 + 20, quit_button_img, "quit")
buttons.add(start_button, quit_button)
game_state = "menu"
while game_state == "menu":
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
for button in buttons:
action = button.check_click(mouse_pos)
if action == "start":
game_state = "game"
return
elif action == "quit":
pygame.quit()
sys.exit()
screen.blit(menu_background, (0, 0))
buttons.draw(screen)
pygame.display.flip()
clock.tick(FPS)
# Функция загрузки уровня
def load_level(level_number):
platforms.empty()
enemies.empty()
coins.empty()
flag_group.empty()
platforms.add(Platform(0, HEIGHT - 40, WIDTH * 2, 40))
if level_number == 1:
generate_platforms(8, level_length_multiplier=2)
generate_coins(6)
enemies.add(Enemy(300, HEIGHT - 80), Enemy(600, HEIGHT - 80))
flag_group.add(Flag(WIDTH * 2 - 100, HEIGHT - 140))
else:
generate_platforms(3 + level_number * 2)
generate_coins(3 + level_number)
global spawn_enemies_continuously
spawn_enemies_continuously = True
flag_group.add(Flag(WIDTH * 2 - 100, HEIGHT - 140))
# Генерация платформ
def generate_platforms(count, level_length_multiplier=1):
last_x = 0
last_y = HEIGHT - 150
for _ in range(count):
x = random.randint(last_x + 150, last_x + 350)
y = random.randint(last_y - 50, last_y + 50)
if y > HEIGHT - 100:
y = HEIGHT - 150
platforms.add(Platform(x, y, 100, 20))
last_x, last_y = x, y
if level_length_multiplier > 1:
for _ in range(count):
x = random.randint(last_x + 150, last_x + 350)
y = random.randint(last_y - 50, last_y + 50)
if y > HEIGHT - 100:
y = HEIGHT - 150
platforms.add(Platform(x, y, 100, 20))
last_x, last_y = x, y
# Генерация монет
def generate_coins(count):
for _ in range(count):
x = random.randint(50, WIDTH * 2 - 50)
y = random.randint(HEIGHT // 2, HEIGHT - 150)
coins.add(Coin(x, y))
# Сохранение рекорда
def save_high_score(score):
try:
with open("high_score.json", "r") as file:
data = json.load(file)
except FileNotFoundError:
data = {"high_score": 0}
if score > data["high_score"]:
data["high_score"] = score
with open("high_score.json", "w") as file:
json.dump(data, file)
# Загрузка рекорда
def load_high_score():
try:
with open("high_score.json", "r") as file:
return json.load(file)["high_score"]
except FileNotFoundError:
return 0
# Основной игровой цикл
def main():
global game_state, current_level, score, high_score, spawn_enemies_continuously, start_time
player = Player()
all_sprites.add(player)
camera = Camera(WIDTH * 2, HEIGHT) # Создаем камеру
current_level = 1
score = 0
high_score = load_high_score()
spawn_enemies_continuously = False
time_limit = 60
start_time = pygame.time.get_ticks()
load_level(current_level)
game_state = "menu"
running = True
while running:
if game_state == "menu":
show_menu()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player.jump()
# Обновление
all_sprites.update()
enemies.update()
coins.update()
# Проверка столкновений с врагами
for enemy in enemies:
if player.rect.colliderect(enemy.rect):
# Если Марио находится выше врага (прыгает на голову)
if player.rect.bottom <= enemy.rect.top + 10:
hit_sound.play()
score += 20
player.velocity_y = -15 # Подпрыгивание после удара по врагу
player.is_jumping = True
enemy.kill() # Уничтожаем врага
else:
# Если Марио столкнулся с врагом иначе (не на голову)
hit_sound.play()
player.lives -= 1 # Уменьшаем жизни
if player.lives <= 0: # Если жизни закончились
show_explosion(player.rect.x, player.rect.y)
save_high_score(score)
reset_game()
else:
# Возвращаем игрока в начало уровня
player.rect.x = 50
player.rect.y = HEIGHT - 100
player.velocity_y = 0
# Проверка сбора монет
coin_hits = pygame.sprite.spritecollide(player, coins, True)
score += len(coin_hits) * 10
# Проверка взаимодействия с флагом
if pygame.sprite.spritecollide(player, flag_group, False):
current_level += 1
if current_level > 2:
win_text = custom_font.render("You Win!", True, BLUE)
screen.fill(WHITE)
screen.blit(win_text, (WIDTH // 2 - win_text.get_width() // 2, HEIGHT // 2))
pygame.display.flip()
pygame.time.delay(3000)
save_high_score(score)
reset_game()
else:
load_level(current_level)
player.rect.x = 50
score += 100
# Таймер
current_time = pygame.time.get_ticks()
elapsed_time = (current_time - start_time) // 1000
remaining_time = max(0, time_limit - elapsed_time)
if remaining_time == 0:
game_over_text = custom_font.render("Game Over", True, RED)
screen.fill(WHITE)
screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2))
pygame.display.flip()
pygame.time.delay(3000)
save_high_score(score)
reset_game()
# Спавн врагов на втором уровне
if current_level == 2 and spawn_enemies_continuously:
if len(enemies) < 5: # Ограничение на количество врагов
if random.randint(1, 100) == 1:
enemies.add(Enemy(random.randint(50, WIDTH - 50), HEIGHT - 80))
# Обновление камеры
camera.update(player)
# Отрисовка
screen.fill(WHITE)
# Отрисовка платформ
for platform in platforms:
screen.blit(platform.image, camera.apply(platform))
# Отрисовка врагов
for enemy in enemies:
screen.blit(enemy.image, camera.apply(enemy))
# Отрисовка монет
for coin in coins:
screen.blit(coin.image, camera.apply(coin))
# Отрисовка флага
for flag in flag_group:
screen.blit(flag.image, camera.apply(flag))
# Отрисовка игрока
screen.blit(player.image, camera.apply(player))
# Отображение счета, времени и жизней
score_text = custom_font.render(f"Score: {score}", True, BLACK)
time_text = custom_font.render(f"Time: {remaining_time}", True, BLACK)
high_score_text = custom_font.render(f"High Score: {high_score}", True, BLACK)
lives_text = custom_font.render(f"Lives: {player.lives}", True, RED)
screen.blit(score_text, (10, 10))
screen.blit(time_text, (WIDTH - 170, 10))
screen.blit(high_score_text, (10, HEIGHT - 40))
screen.blit(lives_text, (WIDTH - 200, HEIGHT - 40)) # Отображаем жизни
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
# Сброс игры
def reset_game():
global current_level, score, start_time, game_state
score = 0
current_level = 1
load_level(current_level)
player.rect.x = 50
player.rect.y = HEIGHT - 100
player.lives = 3 # Сбрасываем жизни
start_time = pygame.time.get_ticks()
game_state = "menu"
# Функция отображения взрыва
def show_explosion(x, y):
explosion = pygame.image.load('assets/images/explosion.png')
screen.blit(explosion, (x, y))
pygame.display.flip()
pygame.time.delay(500)
if __name__ == "__main__":
main()