diff --git a/data/Dymon-ShouXieTi.otf b/data/Dymon-ShouXieTi.otf new file mode 100644 index 0000000..e912992 Binary files /dev/null and b/data/Dymon-ShouXieTi.otf differ diff --git a/src/config/color.py b/src/config/color.py new file mode 100644 index 0000000..2257750 --- /dev/null +++ b/src/config/color.py @@ -0,0 +1,6 @@ +class Color: + black = (0, 0, 0) + white = (255, 255, 255) + a = (255, 0, 0) + b = (0, 255, 0) + c = (0, 0, 255) \ No newline at end of file diff --git a/src/config/game_config.py b/src/config/game_config.py new file mode 100644 index 0000000..26daaee --- /dev/null +++ b/src/config/game_config.py @@ -0,0 +1,16 @@ +import os + + +class GameConfig: + game_name = "Test Game" + window_x = 1920/2 + window_y = 1080/2 + bg_color = (0, 0, 0) + +def data_path(): + file_path = os.path.abspath(__file__) + project_path = os.path.dirname(os.path.dirname(os.path.dirname(file_path))) + data_path = os.path.join(project_path, "data") + return data_path + +print(data_path()) \ No newline at end of file diff --git a/src/scenes/base_scene.py b/src/scenes/base_scene.py new file mode 100644 index 0000000..1df1859 --- /dev/null +++ b/src/scenes/base_scene.py @@ -0,0 +1,16 @@ +import pygame + +class BaseScene: + def __init__(self, screen: pygame.Surface, clock: pygame.time.Clock): + self.screen = screen + self.clock = clock + self.next_scene = None + + def update(self): + pass + + def handle_event(self, events): + pass + + def switch_to_scene(self, scene_class): + self.next_scene = scene_class(self.screen, self.clock) \ No newline at end of file diff --git a/src/scenes/game_scene.py b/src/scenes/game_scene.py new file mode 100644 index 0000000..e3c2822 --- /dev/null +++ b/src/scenes/game_scene.py @@ -0,0 +1,52 @@ +import pygame + +from src.config.game_config import GameConfig +from src.scenes.base_scene import BaseScene + + +class GameScene(BaseScene): + def __init__(self, screen, clock): + super().__init__(screen, clock) + self.dirty_x = 400 + self.dirty_y = 300 + + self.Left_status = False + self.right_status = False + self.up_status = False + self.down_status = False + + # 颜色 + self.WHITE = (255, 255, 255) + self.RED = (255, 0, 0) + self.BLUE = (0, 0, 255) + + def update(self): + if self.Left_status and self.dirty_x >= 0: + self.dirty_x -= 1 + if self.right_status and self.dirty_x <= GameConfig.window_x - 20: + self.dirty_x += 1 + if self.up_status and self.dirty_y >= 0: + self.dirty_y -= 1 + if self.down_status and self.dirty_y <= GameConfig.window_y - 30: + self.dirty_y += 1 + dirty = pygame.draw.rect(self.screen, self.BLUE, [self.dirty_x, self.dirty_y, 20, 30]) + + def handle_event(self, event): + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_LEFT or event.key == pygame.K_a: + self.Left_status = True + elif event.key == pygame.K_RIGHT or event.key == pygame.K_d: + self.right_status = True + elif event.key == pygame.K_UP or event.key == pygame.K_w: + self.up_status = True + elif event.key == pygame.K_DOWN or event.key == pygame.K_s: + self.down_status = True + elif event.type == pygame.KEYUP: + if event.key == pygame.K_LEFT or event.key == pygame.K_a: + self.Left_status = False + elif event.key == pygame.K_RIGHT or event.key == pygame.K_d: + self.right_status = False + elif event.key == pygame.K_UP or event.key == pygame.K_w: + self.up_status = False + elif event.key == pygame.K_DOWN or event.key == pygame.K_s: + self.down_status = False \ No newline at end of file diff --git a/src/scenes/main_menu_scene.py b/src/scenes/main_menu_scene.py new file mode 100644 index 0000000..f579f5e --- /dev/null +++ b/src/scenes/main_menu_scene.py @@ -0,0 +1,123 @@ +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 +from src.scenes.game_scene import GameScene + + +class MainMenuScene(BaseScene): + def __init__(self, screen, clock): + super().__init__(screen, clock) + self.button_status = False + self.debug_status = False + + # 雨滴列表 + self.rain_max = 50 + self.rain_list = [] + # 雨滴生命周期 + self.rain_live_max = 6 + + # 设置字体 + font_file = os.path.join(data_path(),"Dymon-ShouXieTi.otf") + self.font = pygame.font.Font(font_file,16) + + # 文本 + self.game_info_text = self.font.render('按任意键开始游戏,鼠标点击退出', True, Color.white) + + # 初始化雨滴 + self.init_rain(self.rain_max) + + def update(self): + # 按键按下则切换场景 + if self.button_status: + self.switch_to_scene(GameScene) + + # 显示提示文本 + + screen_rect = self.screen.get_rect() # 获取屏幕矩形 + text_rect = self.game_info_text.get_rect() # 获取文本矩形 + self.__show(text_rect, (0, 15)) + text_rect.center = screen_rect.center # 将文本居中到屏幕 + self.screen.blit(self.game_info_text, text_rect.topleft) + + self.__show(screen_rect) + self.__show(text_rect,(0,30)) + self.__show(text_rect.center,(0,45)) + + # 雨滴坠落 + self.rain_fall() + + def handle_event(self, event): + if event.type == pygame.KEYDOWN: + self.button_status = True + elif event.type == pygame.MOUSEBUTTONDOWN: + return "exit" # 主循环接收返回,为exit时设置running为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 __rain_list_append(self, status=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_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: + # 线条长度 + line_len = 10*(self.rain_live_max - rain["status"]) + # 起点位置 + x = rain["dest"][0] + y = rain["dest"][1] - line_len + line_start_point = (x, y) + pygame.draw.line(self.screen, Color.white, line_start_point, rain["dest"], 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 = 200 + 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 \ No newline at end of file diff --git a/start.py b/start.py new file mode 100644 index 0000000..4b28203 --- /dev/null +++ b/start.py @@ -0,0 +1,50 @@ +import pygame + +from src.config.game_config import GameConfig +from src.scenes.main_menu_scene import MainMenuScene + + +class Game: + def __init__(self): + # 初始化 + pygame.init() + + # 设置窗口大小 + size = (GameConfig.window_x, GameConfig.window_y) + self.screen = pygame.display.set_mode(size, pygame.NOFRAME) + + # 设置计时器 + self.clock = pygame.time.Clock() + + # 当前场景 + self.scene = MainMenuScene(self.screen, self.clock) + + # 运行循环 + self.running = True + self.run() + + def run(self): + while self.running: + # 事件处理 + for event in pygame.event.get(): # 这里已经获取了事件,事件队列被清空,在场景的update中再次获取将没有事件 + if event.type == pygame.QUIT: + self.running = False + result = self.scene.handle_event(event) # 当前场景接收事件 + if result == "exit": + self.running = False + + # 检查是否切换场景 + if self.scene.next_scene is not None: + self.scene = self.scene.next_scene + + # 清屏 + self.screen.fill(GameConfig.bg_color) + + # 场景每帧更新逻辑运行 + self.scene.update() + + # 更新显示 + pygame.display.update() + +if __name__ == "__main__": + Game() \ No newline at end of file