forked from zhiyuan/Project1
258 lines
8.9 KiB
Python
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 __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 |