import os import random import pygame from src.config.color import Color from src.config.game_config import data_path, GameConfig from src.scenes.base_scene import BaseScene class MainMenuScene(BaseScene): name = "MainMenuScene" """ 主菜单场景,显示提示信息,展示雨天特效 """ def __init__(self, screen, clock): """ 初始化函数,初始化按钮按下状态、雨滴列表 """ super().__init__(screen, clock) self.button_status = False self.mouse_status = None self.mouse_last_status = None self.debug_status = True self.secret_keys = ["w","w","s","s","a","d","a","d"] # 上上下下左右左右 self.secret_key_compare_num = 0 self.select_id = 0 self.mouse_on_option = False self.keyboard_ensure_status = False # 设置字体 font_file = os.path.join(data_path(),"Dymon-ShouXieTi.otf") self.font = pygame.font.Font(font_file,16) self.text_rect_list = [] self.__init_text_list() # 雨滴列表 self.rain_max = 50 self.rain_list = [] # 雨滴生命周期 self.rain_live_max = 10 # 初始化雨滴 self.init_rain(self.rain_max) # 鼠标位置 self.mouse_pos = (0, 0) def update(self): # 按键按下则切换场景 if self.button_status: self.switch_to_scene("GameScene") if self.secret_key_compare_num == len(self.secret_keys): print("我去,是通关秘籍!") self.switch_to_scene("EmptyScene") # 显示选项文本 self.__show_ui() # 处理鼠标点击 self.handle_mouse_click() # 处理空格或enter self.handle_keyboard() # 雨滴坠落 self.rain_fall() # 更新鼠标位置 self.mouse_pos = pygame.mouse.get_pos() # 更新当前帧鼠标状态 self.mouse_last_status = self.mouse_status # self.__show(self.select_id) def handle_event(self, event): """ 接收主循环传来的事件并处理 """ if event.type == pygame.KEYDOWN: keys = pygame.key.get_pressed() # 获取所有按键状态,用于组合按键判断 if event.key == pygame.K_ESCAPE: self.exit() # 退出游戏 elif keys[pygame.K_z] and keys[pygame.K_x]: # 同时按下Z和X self.button_status = True elif event.key == pygame.K_SPACE or event.key == pygame.K_RETURN: self.keyboard_ensure_status = True else: self.keyboard_ensure_status = False # 判断是否按顺序按下秘籍 if event.unicode.lower() == self.secret_keys[self.secret_key_compare_num]: self.secret_key_compare_num += 1 else: self.secret_key_compare_num = 0 if event.type == pygame.KEYUP: if event.key == pygame.K_w or event.key == pygame.K_UP: self.select_id -= 1 elif event.key == pygame.K_s or event.key == pygame.K_DOWN: self.select_id += 1 self.select_id = self.select_id % len(self.text_rect_list) # 鼠标事件 if event.type == pygame.MOUSEBUTTONDOWN: self.mouse_status = "down" elif event.type == pygame.MOUSEBUTTONUP: self.mouse_status = "up" elif event.type == pygame.MOUSEMOTION: self.mouse_status = "move" def __click(self): """ 判断是否点击鼠标:鼠标抬起时,上一帧的状态如果是按下或者移动则判断为点击 """ if self.mouse_status == "up": if self.mouse_last_status in ["down", "move"]: return True return False def __show(self, info, dest=(0, 0)): """ 显示调试文本 """ if not self.debug_status: return if not isinstance(info, str): info = str(info) info_text = self.font.render(info, True, Color.white) self.screen.blit(info_text, dest) def handle_mouse_click(self): """ 处理鼠标点击 """ if self.__click() and self.mouse_on_option: # 触发选项处理 self.__handle_option() def handle_keyboard(self): if self.keyboard_ensure_status: # 触发选项处理 self.__handle_option() def __handle_option(self): """ 处理选项,根据id执行 """ if self.select_id is None: return elif self.select_id == 0: self.switch_to_scene("GameScene") elif self.select_id == 1: self.switch_to_scene("OptionScene") elif self.select_id == len(self.text_rect_list) - 1: self.exit() def __rain_list_append(self, status=0): """ 向列表中添加一个雨滴,默认状态为0 """ x = random.randint(0, int(GameConfig.window_x)) y = random.randint(0, int(GameConfig.window_y)) rain = { "dest": (x, y), "timer": 0, "status": status } self.rain_list.append(rain) def __init_text_list(self): # 文本 text_list = [ "开始游戏(Z和X)", "设置", "退出游戏(ESC)", ] # 转换为文本框 for text_str in text_list: self.text_rect_list.append(self.font.render(text_str, True, Color.white)) def __show_ui(self): """ 显示所有选项 """ screen_rect = self.screen.get_rect() # 获取屏幕矩形 self.mouse_on_option = False # 未选中选项时重置状态 count = 0 for text in self.text_rect_list: text_rect = text.get_rect() # 获取文本矩形 text_rect.centerx = screen_rect.centerx # 将文本居中到屏幕 text_rect.y = screen_rect.y + count * 2 * text_rect.height + screen_rect.centerx/2 self.screen.blit(text, text_rect.topleft) # 是否高亮显示 if text_rect.y < self.mouse_pos[1] < text_rect.y + text_rect.height \ and text_rect.x < self.mouse_pos[0] < text_rect.x + text_rect.width: pygame.draw.rect(self.screen, Color.white, text_rect, 1) # 更新选中id self.select_id = count # 更新鼠标状态 self.mouse_on_option = True count += 1 # 显示键盘选中 if self.select_id is not None: text_rect = self.text_rect_list[self.select_id].get_rect() text_rect.centerx = screen_rect.centerx # 将文本居中到屏幕 text_rect.y = screen_rect.y + self.select_id * 2 * text_rect.height + screen_rect.centerx/2 pygame.draw.rect(self.screen, Color.white, text_rect, 1) def init_rain(self, max_num): """ 初始化雨滴列表,保存雨滴落点的坐标 """ for i in range(max_num): status = random.randint(1, self.rain_live_max) self.__rain_list_append(status) def rain_fall(self): """ 展示雨滴画面 """ dt = self.clock.tick() for rain in self.rain_list: # 当状态在区间1时绘制雨滴线条 if rain["status"] <= self.rain_live_max - 3: # 设计高度 rain_high = 10 * self.rain_live_max # 起点位置 # x坐标与最后落点x一致 x = rain["dest"][0] # y坐标为落点位置向上移动设计高度后,加上生命周期去掉尾帧后落下的位置 y = (rain["dest"][1] - rain_high) + (rain["status"] * (self.rain_live_max + 2)) y2 = y + 10 line_start_point = (x, y) line_end_point = (x, y2) pygame.draw.line(self.screen, Color.white, line_start_point, line_end_point, 1) # 状态在区间2时绘制涟漪 elif self.rain_live_max - 3 < rain["status"]: point_width = rain["status"] - (self.rain_live_max - 3) pygame.draw.circle(self.screen, Color.white, rain["dest"], point_width, 1) # # 椭圆 # rain_point_rect = [rain["dest"][0], rain["dest"][1], rain["status"], rain["status"]] # pygame.draw.ellipse(self.screen, Color.white, rain_point_rect, 1) # 雨滴状态每d_status毫秒变化一次 d_status = 30 if rain["timer"] > d_status: rain["status"] += 1 rain["timer"] -= d_status # 不设为0,避免累计时间误差 # 生命周期走完后销毁并添加新雨滴 if rain["status"] > self.rain_live_max: self.rain_list.remove(rain) self.__rain_list_append() # timer累计每帧时间 rain["timer"] += dt