Files
ReProject1/src/scenes/main_menu_scene.py
T

258 lines
8.9 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 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 __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):
"""
展示雨滴画面
"""
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