From 6f5c378cb0d8be19462750aca38efa4952646155 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Sat, 13 Dec 2025 16:07:22 +0800 Subject: [PATCH] Added SceneEvent callback --- src/scenes/base/Scene.h | 64 +++++++++-- src/scenes/base/SceneEvent.h | 26 +++++ src/scenes/base/SceneManager.cpp | 177 +++++++++++++++++++++++++++++-- src/scenes/base/SceneManager.h | 127 ++++++++++++++++++++-- src/ui/base/IUIManager.h | 35 ++++++ 5 files changed, 398 insertions(+), 31 deletions(-) create mode 100644 src/scenes/base/SceneEvent.h create mode 100644 src/ui/base/IUIManager.h diff --git a/src/scenes/base/Scene.h b/src/scenes/base/Scene.h index e0c2e39..36d359c 100644 --- a/src/scenes/base/Scene.h +++ b/src/scenes/base/Scene.h @@ -1,27 +1,71 @@ -// ui/Scene.h - 场景基类 +/** + * @file Scene.h + * @brief 定义场景基类 + * @author zhenyan121 + * @date 2025-12-13 + */ #pragma once #include #include "graphics/ui/UIRenderer.h" - - - +#include "scenes/base/SceneEvent.h" +#include +/** + * @class Scene + * @brief 场景基类 + * @details 定义场景的基本接口和生命周期方法,供具体场景类继承和实现。 + */ class Scene { public: + using EventCallback = std::function; ///< 场景事件回调函数类型 + /** + * @brief 析构函数 + */ virtual ~Scene() = default; - // 空实现,可选择性重写 + /** + * @brief 场景进入回调 + * @param renderer SDL 渲染器指针 + * @param width 窗口宽度 + * @param height 窗口高度 + * @param uiRenderer UI 渲染器指针 + */ virtual void onEnter(SDL_Renderer* renderer, int width, int height, UIRenderer* uiRenderer){}; + /** + * @brief 场景退出 + */ virtual void onExit(){}; - + /** + * @brief 每帧更新 + */ virtual void update() = 0; - // 暂时先直接传入SDLrender,以后在改,理论上来说scene是应该不知道SDlrenderer的 + /** + * @brief 每帧渲染 + */ virtual void render() = 0; - // 处理点击 + /** + * @brief 处理点击事件 + * @param x 点击位置的 X 坐标 + * @param y 点击位置的 Y 坐标 + */ virtual void handleClick(float x, float y) = 0; + /** + * @brief 渲染鼠标位置相关内容 + * @param x 鼠标位置的 X 坐标 + * @param y 鼠标位置的 Y 坐标 + */ + virtual void renderMousePosition(float x, float y){}; + /** + * @brief 设置场景事件回调函数 + * @param callback 场景事件回调函数 + */ + void setEventCallback(EventCallback callback) { + m_eventCallback = std::move(callback); + } protected: // UIRenderer应该由上层管理 - UIRenderer* m_uiRenderer; - SDL_Renderer* m_renderer; + UIRenderer* m_uiRenderer; ///< UI 渲染器指针 + SDL_Renderer* m_renderer; ///< SDL 渲染器指针 + EventCallback m_eventCallback; ///< 场景事件回调函数 }; diff --git a/src/scenes/base/SceneEvent.h b/src/scenes/base/SceneEvent.h new file mode 100644 index 0000000..43df7f2 --- /dev/null +++ b/src/scenes/base/SceneEvent.h @@ -0,0 +1,26 @@ +/** + * @file SceneEvent.h + * @brief 定义场景事件类型和结构体 + * @author zhenyan121 + * @date 2025-12-13 + */ +#pragma once +#include +/** + * @enum SceneEventType + * @brief 定义场景事件的类型 + */ +enum class SceneEventType { + ChangeScene, ///< 切换场景事件 + PushScene, ///< 推入新场景事件 + PopScene, ///< 弹出当前场景事件 + QuitGame ///< 退出游戏事件 +}; +/** + * @struct SceneEvent + * @brief 场景事件结构体,包含事件类型和相关数据 + */ +struct SceneEvent { + SceneEventType type; ///< 事件类型 + std::string sceneName; ///< 只在 ChangeScene/PushScene 时使用 +}; \ No newline at end of file diff --git a/src/scenes/base/SceneManager.cpp b/src/scenes/base/SceneManager.cpp index ddef384..121b396 100644 --- a/src/scenes/base/SceneManager.cpp +++ b/src/scenes/base/SceneManager.cpp @@ -1,35 +1,192 @@ #include "SceneManager.h" -SceneManager::SceneManager(SDL_Renderer* renderer, UIRenderer* uiRenderer) : +SceneManager::SceneManager(SDL_Renderer* renderer, UIRenderer* uiRenderer, SDL_Window* window) : m_renderer(renderer), - m_uiRenderer(uiRenderer) + m_uiRenderer(uiRenderer), + m_window(window) { - m_scene = std::make_unique(); - m_scene->onEnter(renderer, 1600, 900, m_uiRenderer); + + + } + SceneManager::~SceneManager() { + // 清理栈和缓存;shared_ptr 会自动管理生命周期 while (!m_scenes.empty()) { m_scenes.pop(); } + m_sceneCache.clear(); + m_sceneFactories.clear(); } -void SceneManager::PushScene(const std::string& sceneName, std::unique_ptr scene) { +bool SceneManager::initialize() { + // 注册所有场景工厂 + registerAllScene(); + changeScene("MainMenuScene"); + return true; +} + +void SceneManager::registerAllScene() { + // 注册所有默认场景工厂(可以由外部覆盖/新增) + + registerSceneFactory("MainMenuScene", []() -> std::shared_ptr { + return std::make_shared(); + }); + registerSceneFactory("GameScene", []() -> std::shared_ptr { + return std::make_shared(); + }); +} + +void SceneManager::createScene(const std::string& sceneName) { + // 使用注册的工厂创建场景实例 + auto it = m_sceneFactories.find(sceneName); + if (it != m_sceneFactories.end()) { + auto scene = it->second(); + if (scene) { + m_sceneCache[sceneName] = scene; + } else { + SDL_Log("SceneManager::createScene: factory for '%s' returned nullptr\n", sceneName.c_str()); + } + } else { + SDL_Log("SceneManager::createScene: no factory registered for '%s'\n", sceneName.c_str()); + } +} + +void SceneManager::registerSceneFactory(const std::string& sceneName, std::function()> factory) { + if (sceneName.empty()) return; + m_sceneFactories[sceneName] = std::move(factory); +} + +void SceneManager::unregisterSceneFactory(const std::string& sceneName) { + m_sceneFactories.erase(sceneName); +} + +void SceneManager::pushScene(const std::string& sceneName) { + if (sceneName.empty()) { + SDL_Log("SceneManager::pushScene: sceneName is empty!\n"); + return; + } + + // 检查场景是否已在缓存中 + auto it = m_sceneCache.find(sceneName); + if (it == m_sceneCache.end()) { + // 场景未缓存,尝试创建 + createScene(sceneName); + it = m_sceneCache.find(sceneName); + if (it == m_sceneCache.end()) { + SDL_Log("SceneManager::pushScene: Scene '%s' not found in cache after creation!\n", sceneName.c_str()); + return; + } + } + + // 保存当前场景并调用退出回调 + if (m_scene) { + m_scene->onExit(); + m_scenes.push(m_scene); + } + + // 设置新场景为当前场景 + m_scene = it->second; + + + auto [w, h] = getWindowDimensions(); + m_scene->onEnter(m_renderer, w, h, m_uiRenderer); +} + + +void SceneManager::popScene() { + if (!m_scene) return; + + // 调用当前场景的退出回调并释放引用 + m_scene->onExit(); + m_scene.reset(); + + // 如果栈不为空,恢复前一个场景 + if (!m_scenes.empty()) { + m_scene = m_scenes.top(); + m_scenes.pop(); + auto [w, h] = getWindowDimensions(); + if (m_scene) { + m_scene->onEnter(m_renderer, w, h, m_uiRenderer); + } + } +} + + +void SceneManager::changeScene(const std::string& sceneName) { + if (sceneName.empty()) return; + + // 检查场景是否已在缓存中 + if (m_sceneCache.find(sceneName) == m_sceneCache.end()) { + // 场景未缓存,尝试创建 + createScene(sceneName); + } + + // 从缓存中获取目标场景 + auto target = m_sceneCache[sceneName]; + if (!target) { + SDL_Log("SceneManager::changeScene: Scene '%s' not found in cache!\n", sceneName.c_str()); + return; + } + target->setEventCallback([this](const SceneEvent& event) { + handleSceneEvent(event); + }); + // 退出当前场景 + if (m_scene) { + m_scene->onExit(); + } + // 切换到目标场景 + m_scene = target; + + auto [w, h] = getWindowDimensions(); + m_scene->onEnter(m_renderer, w, h, m_uiRenderer); } -void SceneManager::handleClickCurrent(std::pair clickon) { - auto [screenX, screenY] = clickon; - m_scene->handleClick(screenX, screenY); +void SceneManager::handleClickCurrent(std::pair clickOn) { + auto [screenX, screenY] = clickOn; + if (m_scene) m_scene->handleClick(screenX, screenY); +} + +void SceneManager::handleMousePosition(std::pair mousePosition) { + auto [screenX, screenY] = mousePosition; + if (m_scene) m_scene->renderMousePosition(screenX, screenY); } void SceneManager::updateCurrent() { - m_scene->update(); + if (m_scene) m_scene->update(); } + void SceneManager::renderCurrent() { - m_scene->render(); + if (m_scene) m_scene->render(); +} + +std::pair SceneManager::getWindowDimensions() const { + // 获取窗口尺寸的辅助方法 + int w = 0, h = 0; + if (m_window) { + SDL_GetWindowSize(m_window, &w, &h); + } + return {w > 0 ? w : 1600, h > 0 ? h : 900}; +} + +void SceneManager::handleSceneEvent(const SceneEvent& event) { + // 根据事件类型处理场景事件 + switch (event.type) + { + case SceneEventType::ChangeScene: + changeScene(event.sceneName); + break; + case SceneEventType::PushScene: + pushScene(event.sceneName); + break; + default: + SDL_Log("SceneManager::handleSceneEvent: Unhandled event type!\n"); + break; + } } \ No newline at end of file diff --git a/src/scenes/base/SceneManager.h b/src/scenes/base/SceneManager.h index 36bd6f8..226175e 100644 --- a/src/scenes/base/SceneManager.h +++ b/src/scenes/base/SceneManager.h @@ -1,29 +1,134 @@ +/** + * @file SceneManager.h + * @brief 场景管理器类,负责场景的推入/弹出与切换 + * @details 场景可以被缓存(按名字)以便重用。 + * @author zhenyan121 + * @date 2025-12-13 + */ #pragma once #include "Scene.h" #include "scenes/gameplay/GameScene.h" +#include "scenes/menu/MainMenuScene.h" #include #include #include #include #include +#include + +/** + * @class SceneManager + * @brief 场景管理器 + * @details 负责管理游戏中的不同场景(如菜单、游戏玩法等),支持场景的推入、弹出和切换。 + * 场景可以按名称缓存以便重用,使用工厂函数动态创建场景实例。 + */ class SceneManager { public: - SceneManager(SDL_Renderer* renderer, UIRenderer* uiRenderer); + /** + * @brief 构造函数 + * @param renderer SDL 渲染器指针,用于传递给场景进行渲染 + * @param uiRenderer UI 渲染器指针,用于传递给场景进行 UI 渲染 + * @param window SDL 窗口指针,用于获取窗口尺寸 + */ + SceneManager(SDL_Renderer* renderer, UIRenderer* uiRenderer, SDL_Window* window); + /** + * @brief 析构函数 + * @details 清理场景栈和缓存,释放资源 + */ ~SceneManager(); + + /** + * @brief 初始化场景管理器 + * @return 初始化成功返回 true,否则返回 false + */ + bool initialize(); + /** + * @brief 注册所有默认场景工厂 + */ + void registerAllScene(); + /** + * @brief 创建场景实例并缓存 + * @param sceneName 场景名称 + */ + void createScene(const std::string& sceneName); - void PushScene(const std::string& sceneName, std::unique_ptr scene); - // void PopScene(); - //void ChangeScene(const std::string& sceneName, std::unique_ptr scene); - void handleClickCurrent(std::pair clickon); + /** + * @brief 注册场景工厂函数,用于按名字动态创建场景实例 + * @param sceneName 场景名称 + * @param factory 返回新场景的函数,返回类型为 std::shared_ptr + */ + void registerSceneFactory(const std::string& sceneName, std::function()> factory); + + /** + * @brief 注销已注册的场景工厂 + */ + void unregisterSceneFactory(const std::string& sceneName); + + /** + * @brief 将新场景推入场景栈顶端并作为当前场景 + * @param sceneName 场景名称,用于缓存查找。若缓存中不存在,则通过工厂函数动态创建 + * @details 前一个当前场景将接收 onExit() 回调并保留在栈中。新场景成为当前场景并接收 onEnter() 回调 + */ + void pushScene(const std::string& sceneName); + + /** + * @brief 弹出当前场景并恢复前一个场景 + * @details 当前场景将接收 onExit() 回调,然后从栈中移除。如果栈不为空,则弹出的上一个场景成为新的当前场景并接收 onEnter() 回调 + */ + void popScene(); + + /** + * @brief 替换当前场景 + * @param sceneName 场景名称,用于缓存查找或工厂创建 + * @details 当前场景将接收 onExit() 回调并被替换。若缓存中存在该名称的场景则复用, + * 否则通过工厂函数创建新场景。新场景成为当前场景并接收 onEnter() 回调 + */ + void changeScene(const std::string& sceneName); + + /** + * @brief 处理当前场景的点击事件 + * @param clickOn 点击位置坐标对 {x, y} + * @details 将点击事件转发给当前场景的 handleClick() 方法进行处理 + */ + void handleClickCurrent(std::pair clickOn); + + /** + * @brief 处理当前场景的鼠标位置事件 + * @param mousePosition 鼠标位置坐标对 {x, y} + * @details 将鼠标位置事件转发给当前场景的 renderMousePosition() 方法进行处理 + */ + void handleMousePosition(std::pair mousePosition); + + /** + * @brief 更新当前场景 + * @details 调用当前场景的 update() 方法进行每帧逻辑更新 + */ void updateCurrent(); + + /** + * @brief 渲染当前场景 + * @details 调用当前场景的 render() 方法进行每帧渲染 + */ void renderCurrent(); + /** + * @brief 处理场景事件的回调函数 + * @param event 场景事件对象 + */ + void handleSceneEvent(const SceneEvent& event); private: - SDL_Renderer* m_renderer; - UIRenderer* m_uiRenderer; - std::unique_ptr m_scene; - // 用栈的形式来存储场景 - std::stack> m_scenes; - std::unordered_map> m_sceneCache; + SDL_Renderer* m_renderer; ///< SDL 渲染器指针 + SDL_Window* m_window; ///< SDL 窗口指针 + UIRenderer* m_uiRenderer; ///< UI 渲染器指针 + std::shared_ptr m_scene; ///< 当前场景(使用 shared_ptr 便于在缓存和栈中共享) + std::stack> m_scenes; ///< 场景栈,存储场景层级关系(使用 shared_ptr) + std::unordered_map> m_sceneCache; ///< 场景缓存,按名字缓存场景以便切换时复用 + std::unordered_map()>> m_sceneFactories; ///< 场景工厂映射,按名字动态创建场景实例 + + /** + * @brief 获取窗口尺寸的辅助方法 + * @return 返回 {宽度, 高度},如果获取失败则返回默认值 {1600, 900} + */ + std::pair getWindowDimensions() const; }; \ No newline at end of file diff --git a/src/ui/base/IUIManager.h b/src/ui/base/IUIManager.h new file mode 100644 index 0000000..fb06c5e --- /dev/null +++ b/src/ui/base/IUIManager.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include "ui/components/Button.h" +#include "ui/components/Label.h" +#include "ui/base/UIRenderData.h" + +#include +#include +#include + +class IUIManager { +public: + virtual ~IUIManager() = default; + + virtual void init() = 0; + + virtual const UIRenderData& getUIRenderData() = 0; + // 收集渲染数据 + virtual void CollectRenderData() = 0; + + virtual void UpdateMousePositon(float x, float y) = 0; + +protected: + std::unordered_map> m_buttons; + std::unordered_map> m_labels; + UIRenderData m_uiRenderData; + SDL_Renderer* m_renderer = nullptr; + TextRenderer* m_textRenderer = nullptr; + + size_t makeHash(const std::string& name) { + return std::hash{}(name); + } +}; \ No newline at end of file