maze_python/main.py

366 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pygame
from pygame import Surface
from pygame.constants import QUIT
from maze import Maze
from draw import Button, Toast
from save_ui import SaveLoadUI
from config import *
import sys
import os
screen: Surface = None # 窗口实例
clock = None # 时钟实例
textFont = None # 字体
def pygameInit(title: str = "pygame"):
"""初始化 pygame"""
pygame.init()
pygame.mixer.init() # 声音初始化
pygame.display.set_caption(title)
global screen, clock, textFont # 修改全局变量
screen = pygame.display.set_mode((UI_WIDTH, UI_HEIGHT))
clock = pygame.time.Clock()
# Initialize font with UTF-8 support
pygame.font.init()
textFont = pygame.font.Font(FONT_FILE, FONT_SIZE)
def create_buttons(button_positions):
"""根据按钮位置创建所有按钮"""
# 创建按钮纹理(只需要一次)
button_start_texture = pygame.image.load(START_BUTTON_IMAGE).convert_alpha()
button_start_texture = pygame.transform.scale(button_start_texture, BUTTON_START_SIZE)
button_save_texture = pygame.image.load(SAVE_BUTTON_IMAGE).convert_alpha()
button_save_texture = pygame.transform.scale(button_save_texture, BUTTON_SAVE_SIZE)
button_load_texture = pygame.image.load(LOAD_BUTTON_IMAGE).convert_alpha()
button_load_texture = pygame.transform.scale(button_load_texture, BUTTON_SAVE_SIZE)
# 创建按钮对象
button_start = Button(pygame.rect.Rect(*button_positions['start_button'], *BUTTON_START_SIZE), button_start_texture)
button_save = Button(pygame.rect.Rect(*button_positions['save_button'], *BUTTON_SAVE_SIZE), button_save_texture)
button_load = Button(pygame.rect.Rect(*button_positions['load_button'], *BUTTON_SAVE_SIZE), button_load_texture)
button_next_step = Button(pygame.rect.Rect(*button_positions['next_step_button'], *BUTTON_CONTROL_SIZE), None)
button_reset_path = Button(pygame.rect.Rect(*button_positions['reset_path_button'], *BUTTON_CONTROL_SIZE), None)
button_auto_play = Button(pygame.rect.Rect(*button_positions['auto_play_button'], *BUTTON_CONTROL_SIZE), None)
# 历史迭代控制按钮
button_history_prev = Button(pygame.rect.Rect(*button_positions['history_prev_button'], *BUTTON_CONTROL_SIZE), None)
button_history_next = Button(pygame.rect.Rect(*button_positions['history_next_button'], *BUTTON_CONTROL_SIZE), None)
button_history_auto = Button(pygame.rect.Rect(*button_positions['history_auto_button'], *BUTTON_CONTROL_SIZE), None)
button_history_toggle = Button(pygame.rect.Rect(*button_positions['history_toggle_button'], *BUTTON_CONTROL_SIZE), None)
return {
'start': button_start,
'save': button_save,
'load': button_load,
'next_step': button_next_step,
'reset_path': button_reset_path,
'auto_play': button_auto_play,
'history_prev': button_history_prev,
'history_next': button_history_next,
'history_auto': button_history_auto,
'history_toggle': button_history_toggle
}
def update_save_ui_positions(save_ui, button_positions):
"""更新存档界面的位置"""
save_ui.list_area = pygame.Rect(*button_positions['save_list_area'])
if __name__ == "__main__":
pygameInit("maze")
maze = Maze(wall_size=WALL_SIZE, maze_size=MAZE_SIZE, file_name=DEFAULT_MAZE_FILE)
# 加载图片资源(将根据需要动态缩放)
image_wall = pygame.image.load(WALL_IMAGE).convert_alpha()
image_coin = pygame.image.load(COIN_IMAGE).convert_alpha()
image_trap = pygame.image.load(TRAP_IMAGE).convert_alpha()
# 初始按钮位置和按钮
button_positions = get_button_positions()
buttons = create_buttons(button_positions)
# 提示信息
mes1 = Toast("没有生成迷宫,无法保存", UI_WIDTH, UI_HEIGHT, font=textFont)
mes2 = Toast("迷宫已保存", UI_WIDTH, UI_HEIGHT, font=textFont)
mes3 = Toast("存档已保存", UI_WIDTH, UI_HEIGHT, font=textFont)
mes4 = Toast("存档加载成功", UI_WIDTH, UI_HEIGHT, font=textFont)
mes5 = Toast("加载失败", UI_WIDTH, UI_HEIGHT, font=textFont)
# 创建存档界面
save_ui = SaveLoadUI(textFont)
update_save_ui_positions(save_ui, button_positions)
# 路径控制变量
auto_play = False
auto_play_timer = 0
auto_play_interval = AUTO_PLAY_INTERVAL
# 历史迭代控制变量
history_auto_play = False
history_auto_timer = 0
history_auto_interval = AUTO_PLAY_INTERVAL // 2 # 历史播放速度稍快一些
# 当前显示尺寸跟踪
current_display_size = MAZE_SIZE
running = True
while running:
clock.tick(FPS) # 限制帧数
screen.fill(COLOR_WHITE) # 铺底
# 检查迷宫显示尺寸是否发生变化,如果变化则更新按钮位置
if maze.get_actual_display_size() != current_display_size:
current_display_size = maze.get_actual_display_size()
button_positions = get_button_positions(current_display_size)
buttons = create_buttons(button_positions)
update_save_ui_positions(save_ui, button_positions)
print(f"UI布局已更新迷宫显示尺寸: {current_display_size}")
# 自动播放逻辑
if auto_play and len(maze.full_path) > 0 and not maze.show_history:
auto_play_timer += 1
if auto_play_timer >= auto_play_interval:
if not maze.next_path_step():
auto_play = False # 路径播放完成后停止自动播放
auto_play_timer = 0
# 历史迭代自动播放逻辑
if history_auto_play and len(maze.history_mazes) > 0 and maze.show_history:
history_auto_timer += 1
if history_auto_timer >= history_auto_interval:
if not maze.next_history_step():
history_auto_play = False # 历史播放完成后停止自动播放
history_auto_timer = 0
for event in pygame.event.get():
# 首先让存档界面处理事件
save_result = save_ui.handle_event(event, maze)
if save_result == "save_success":
mes3.show()
elif save_result == "load_success":
mes4.show()
auto_play = False # 加载游戏后停止自动播放
elif save_result == "load_failed":
mes5.show()
# 如果存档界面正在显示,不处理其他按钮事件
if not save_ui.show_save_list:
buttons['start'].handle_event(event=event)
buttons['save'].handle_event(event=event)
buttons['load'].handle_event(event=event)
buttons['next_step'].handle_event(event=event)
buttons['reset_path'].handle_event(event=event)
buttons['auto_play'].handle_event(event=event)
# 历史迭代控制按钮
buttons['history_prev'].handle_event(event=event)
buttons['history_next'].handle_event(event=event)
buttons['history_auto'].handle_event(event=event)
buttons['history_toggle'].handle_event(event=event)
if buttons['start'].pressed == True:
maze.generate()
auto_play = False # 生成新迷宫时停止自动播放
history_auto_play = False # 停止历史播放
auto_play_timer = 0 # 重置计时器
history_auto_timer = 0 # 重置历史计时器
print("已重置所有播放状态")
if buttons['save'].pressed == True:
if len(maze.grid) == 0:
mes1.show()
else:
# 自动保存为JSON格式
result = maze.save_game(format_type="json")
if result:
mes2.text = "迷宫已保存为JSON格式"
mes2.show()
else:
mes5.text = "保存失败"
mes5.show()
if buttons['load'].pressed == True:
# 直接读取样例JSON文件并生成路径
sample_file = "saves/sample.json"
if os.path.exists(sample_file):
if maze.load_game(sample_file):
# 加载成功后检查是否需要重新生成路径
if len(maze.full_path) == 0 and maze.generater.maze:
# 只有当没有路径信息时才重新生成
from SourceCollector import SourceCollector
maze.source_collector = SourceCollector(maze=maze.generater.maze)
maze.source_collector.run()
maze.full_path = maze.source_collector.get_path()
maze.path_step = 0
maze.is_path_complete = False
# 不要重置grid保持加载的包含路径的网格
mes4.text = f"已加载 {sample_file} 并生成路径"
mes4.show()
auto_play = False
else:
mes4.text = f"已加载 {sample_file}"
mes4.show()
auto_play = False
else:
mes5.text = f"无法加载 {sample_file}"
mes5.show()
else:
# 如果sample.json不存在则打开存档选择界面
save_ui.update_save_list(maze)
save_ui.toggle_save_list()
# 路径控制
if buttons['next_step'].pressed == True and len(maze.full_path) > 0 and not maze.show_history:
maze.next_path_step()
if buttons['reset_path'].pressed == True and len(maze.full_path) > 0 and not maze.show_history:
maze.reset_path()
auto_play = False
if buttons['auto_play'].pressed == True and len(maze.full_path) > 0 and not maze.show_history:
auto_play = not auto_play
auto_play_timer = 0
# 历史迭代控制
if buttons['history_prev'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history:
maze.prev_history_step()
if buttons['history_next'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history:
maze.next_history_step()
if buttons['history_auto'].pressed == True and len(maze.history_mazes) > 0 and maze.show_history:
history_auto_play = not history_auto_play
history_auto_timer = 0
if buttons['history_toggle'].pressed == True and len(maze.history_mazes) > 0:
maze.toggle_history_mode()
auto_play = False # 切换模式时停止路径播放
history_auto_play = False # 停止历史播放
# 键盘控制
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and len(maze.full_path) > 0 and not maze.show_history:
maze.next_path_step()
elif event.key == pygame.K_r and len(maze.full_path) > 0 and not maze.show_history:
maze.reset_path()
auto_play = False
elif event.key == pygame.K_a and len(maze.full_path) > 0 and not maze.show_history:
auto_play = not auto_play
auto_play_timer = 0
# 历史迭代快捷键
elif event.key == pygame.K_LEFT and len(maze.history_mazes) > 0 and maze.show_history:
maze.prev_history_step()
elif event.key == pygame.K_RIGHT and len(maze.history_mazes) > 0 and maze.show_history:
maze.next_history_step()
elif event.key == pygame.K_h and len(maze.history_mazes) > 0:
maze.toggle_history_mode()
auto_play = False
history_auto_play = False
elif event.key == pygame.K_p and len(maze.history_mazes) > 0 and maze.show_history:
history_auto_play = not history_auto_play
history_auto_timer = 0
# 保存快捷键
elif event.key == pygame.K_s and pygame.key.get_pressed()[pygame.K_LCTRL]:
# Ctrl+S 保存包含路径的JSON
if len(maze.grid) > 0:
result = maze.save_game(format_type="json")
if result:
mes3.text = "JSON格式存档已保存"
mes3.show()
elif event.key == pygame.K_s and pygame.key.get_pressed()[pygame.K_LSHIFT]:
# Shift+S 保存为CSV格式兼容旧版本
if len(maze.grid) > 0:
result = maze.save_game(format_type="csv")
if result:
mes3.text = "CSV格式存档已保存"
mes3.show()
elif event.key == pygame.K_l and pygame.key.get_pressed()[pygame.K_LCTRL]:
# Ctrl+L 打开读档界面
save_ui.update_save_list(maze)
save_ui.toggle_save_list()
if event.type == QUIT:
running = False
maze.draw(screen=screen, wall_texture=image_wall, coin_texture=image_coin, trap_texture=image_trap)
buttons['start'].draw(screen=screen)
buttons['save'].draw(screen=screen)
buttons['load'].draw(screen=screen)
# 绘制路径控制按钮(仅在有路径且非历史模式时显示)
if len(maze.full_path) > 0 and not maze.show_history:
# 绘制按钮背景
pygame.draw.rect(screen, COLOR_GRAY, buttons['next_step'].rect)
pygame.draw.rect(screen, COLOR_GRAY, buttons['reset_path'].rect)
pygame.draw.rect(screen, COLOR_GREEN if auto_play else COLOR_GRAY, buttons['auto_play'].rect)
# 绘制按钮文字
next_text = textFont.render("下一步", True, COLOR_BLACK)
reset_text = textFont.render("重置", True, COLOR_BLACK)
auto_text = textFont.render("自动播放" if not auto_play else "停止", True, COLOR_BLACK)
screen.blit(next_text, (buttons['next_step'].rect.x + 10, buttons['next_step'].rect.y + 15))
screen.blit(reset_text, (buttons['reset_path'].rect.x + 25, buttons['reset_path'].rect.y + 15))
screen.blit(auto_text, (buttons['auto_play'].rect.x + 5, buttons['auto_play'].rect.y + 15))
# 显示当前步数信息
progress_text = textFont.render(f"路径进度: {maze.path_step}/{len(maze.full_path)}", True, COLOR_BLACK)
screen.blit(progress_text, button_positions['progress_text'])
# 绘制历史迭代控制按钮(仅在有历史且为历史模式时显示)
if len(maze.history_mazes) > 0 and maze.show_history:
# 绘制按钮背景
pygame.draw.rect(screen, COLOR_GRAY, buttons['history_prev'].rect)
pygame.draw.rect(screen, COLOR_GRAY, buttons['history_next'].rect)
pygame.draw.rect(screen, COLOR_GREEN if history_auto_play else COLOR_GRAY, buttons['history_auto'].rect)
# 绘制按钮文字
prev_text = textFont.render("上一步", True, COLOR_BLACK)
next_text = textFont.render("下一步", True, COLOR_BLACK)
auto_text = textFont.render("自动播放" if not history_auto_play else "停止", True, COLOR_BLACK)
screen.blit(prev_text, (buttons['history_prev'].rect.x + 10, buttons['history_prev'].rect.y + 15))
screen.blit(next_text, (buttons['history_next'].rect.x + 10, buttons['history_next'].rect.y + 15))
screen.blit(auto_text, (buttons['history_auto'].rect.x + 5, buttons['history_auto'].rect.y + 15))
# 显示历史进度信息
history_progress_text = textFont.render(f"生成进度: {maze.history_step}/{len(maze.history_mazes)}", True, COLOR_BLACK)
screen.blit(history_progress_text, button_positions['history_progress_text'])
# 绘制历史模式切换按钮(仅在有历史时显示)
if len(maze.history_mazes) > 0:
pygame.draw.rect(screen, COLOR_GOLD if maze.show_history else COLOR_GRAY, buttons['history_toggle'].rect)
toggle_text = textFont.render("历史模式" if not maze.show_history else "路径模式", True, COLOR_BLACK)
screen.blit(toggle_text, (buttons['history_toggle'].rect.x + 5, buttons['history_toggle'].rect.y + 15))
# 显示操作提示
if len(maze.full_path) > 0 and not maze.show_history:
hint_text = textFont.render("空格键: 下一步 | R键: 重置 | A键: 自动播放", True, COLOR_LIGHT_GRAY)
screen.blit(hint_text, button_positions['hint_text'])
elif len(maze.history_mazes) > 0 and maze.show_history:
hint_text = textFont.render("←→键: 历史步骤 | P键: 自动播放 | H键: 切换模式", True, COLOR_LIGHT_GRAY)
screen.blit(hint_text, button_positions['hint_text'])
# 显示快捷键提示
shortcut_text = textFont.render("Ctrl+S: 保存JSON | Shift+S: 保存CSV | Ctrl+L: 读档", True, COLOR_LIGHT_GRAY)
screen.blit(shortcut_text, button_positions['shortcut_text'])
mes1.draw(screen=screen)
mes2.draw(screen=screen)
mes3.draw(screen=screen)
mes4.draw(screen=screen)
mes5.draw(screen=screen)
# 绘制存档界面(必须在最后绘制以显示在最上层)
save_ui.draw(screen)
pygame.display.flip()
pygame.quit()