Files
Project1/src/scenes/main_menu_scene.py
T
2026-07-31 20:22:56 +08:00

258 lines
8.9 KiB
Python

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):
"""
初始化函数,初始化按钮按下状态、雨滴列表
"""
super().__init__(screen)
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, dt=0):
# 按键按下则切换场景
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(dt)
# 更新鼠标位置
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 __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 = [
"开始游戏",
"设置",
"退出游戏",
]
# 转换为文本框
for text_str in text_list:
self.text_rect_list.append(self.font.render(text_str, True, Color.white))
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("ConfigScene")
elif self.select_id == len(self.text_rect_list) - 1:
self.exit()
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):
"""
展示雨滴画面
"""
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