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()