mirror of
https://github.com/zhenyan121/SporeBG-Conid.git
synced 2026-04-10 06:14:08 +08:00
Added SceneEvent callback
This commit is contained in:
@@ -1,27 +1,71 @@
|
||||
// ui/Scene.h - 场景基类
|
||||
/**
|
||||
* @file Scene.h
|
||||
* @brief 定义场景基类
|
||||
* @author zhenyan121
|
||||
* @date 2025-12-13
|
||||
*/
|
||||
#pragma once
|
||||
#include <SDL3/SDL.h>
|
||||
#include "graphics/ui/UIRenderer.h"
|
||||
|
||||
|
||||
|
||||
#include "scenes/base/SceneEvent.h"
|
||||
#include <functional>
|
||||
/**
|
||||
* @class Scene
|
||||
* @brief 场景基类
|
||||
* @details 定义场景的基本接口和生命周期方法,供具体场景类继承和实现。
|
||||
*/
|
||||
class Scene {
|
||||
public:
|
||||
|
||||
using EventCallback = std::function<void(const SceneEvent&)>; ///< 场景事件回调函数类型
|
||||
/**
|
||||
* @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; ///< 场景事件回调函数
|
||||
};
|
||||
|
||||
|
||||
26
src/scenes/base/SceneEvent.h
Normal file
26
src/scenes/base/SceneEvent.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file SceneEvent.h
|
||||
* @brief 定义场景事件类型和结构体
|
||||
* @author zhenyan121
|
||||
* @date 2025-12-13
|
||||
*/
|
||||
#pragma once
|
||||
#include <string>
|
||||
/**
|
||||
* @enum SceneEventType
|
||||
* @brief 定义场景事件的类型
|
||||
*/
|
||||
enum class SceneEventType {
|
||||
ChangeScene, ///< 切换场景事件
|
||||
PushScene, ///< 推入新场景事件
|
||||
PopScene, ///< 弹出当前场景事件
|
||||
QuitGame ///< 退出游戏事件
|
||||
};
|
||||
/**
|
||||
* @struct SceneEvent
|
||||
* @brief 场景事件结构体,包含事件类型和相关数据
|
||||
*/
|
||||
struct SceneEvent {
|
||||
SceneEventType type; ///< 事件类型
|
||||
std::string sceneName; ///< 只在 ChangeScene/PushScene 时使用
|
||||
};
|
||||
@@ -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<GameScene>();
|
||||
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> scene) {
|
||||
|
||||
bool SceneManager::initialize() {
|
||||
// 注册所有场景工厂
|
||||
registerAllScene();
|
||||
changeScene("MainMenuScene");
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneManager::registerAllScene() {
|
||||
// 注册所有默认场景工厂(可以由外部覆盖/新增)
|
||||
|
||||
registerSceneFactory("MainMenuScene", []() -> std::shared_ptr<Scene> {
|
||||
return std::make_shared<MainMenuScene>();
|
||||
});
|
||||
registerSceneFactory("GameScene", []() -> std::shared_ptr<Scene> {
|
||||
return std::make_shared<GameScene>();
|
||||
});
|
||||
}
|
||||
|
||||
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<std::shared_ptr<Scene>()> 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<float, float> clickon) {
|
||||
auto [screenX, screenY] = clickon;
|
||||
m_scene->handleClick(screenX, screenY);
|
||||
void SceneManager::handleClickCurrent(std::pair<float, float> clickOn) {
|
||||
auto [screenX, screenY] = clickOn;
|
||||
if (m_scene) m_scene->handleClick(screenX, screenY);
|
||||
}
|
||||
|
||||
void SceneManager::handleMousePosition(std::pair<float, float> 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<int, int> 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;
|
||||
}
|
||||
}
|
||||
@@ -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 <SDL3/SDL.h>
|
||||
#include <string>
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* @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> scene);
|
||||
// void PopScene();
|
||||
//void ChangeScene(const std::string& sceneName, std::unique_ptr<Scene> scene);
|
||||
void handleClickCurrent(std::pair<float, float> clickon);
|
||||
/**
|
||||
* @brief 注册场景工厂函数,用于按名字动态创建场景实例
|
||||
* @param sceneName 场景名称
|
||||
* @param factory 返回新场景的函数,返回类型为 std::shared_ptr<Scene>
|
||||
*/
|
||||
void registerSceneFactory(const std::string& sceneName, std::function<std::shared_ptr<Scene>()> 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<float, float> clickOn);
|
||||
|
||||
/**
|
||||
* @brief 处理当前场景的鼠标位置事件
|
||||
* @param mousePosition 鼠标位置坐标对 {x, y}
|
||||
* @details 将鼠标位置事件转发给当前场景的 renderMousePosition() 方法进行处理
|
||||
*/
|
||||
void handleMousePosition(std::pair<float, float> 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<Scene> m_scene;
|
||||
// 用栈的形式来存储场景
|
||||
std::stack<std::unique_ptr<Scene>> m_scenes;
|
||||
std::unordered_map<std::string, std::unique_ptr<Scene>> m_sceneCache;
|
||||
SDL_Renderer* m_renderer; ///< SDL 渲染器指针
|
||||
SDL_Window* m_window; ///< SDL 窗口指针
|
||||
UIRenderer* m_uiRenderer; ///< UI 渲染器指针
|
||||
std::shared_ptr<Scene> m_scene; ///< 当前场景(使用 shared_ptr 便于在缓存和栈中共享)
|
||||
std::stack<std::shared_ptr<Scene>> m_scenes; ///< 场景栈,存储场景层级关系(使用 shared_ptr)
|
||||
std::unordered_map<std::string, std::shared_ptr<Scene>> m_sceneCache; ///< 场景缓存,按名字缓存场景以便切换时复用
|
||||
std::unordered_map<std::string, std::function<std::shared_ptr<Scene>()>> m_sceneFactories; ///< 场景工厂映射,按名字动态创建场景实例
|
||||
|
||||
/**
|
||||
* @brief 获取窗口尺寸的辅助方法
|
||||
* @return 返回 {宽度, 高度},如果获取失败则返回默认值 {1600, 900}
|
||||
*/
|
||||
std::pair<int, int> getWindowDimensions() const;
|
||||
};
|
||||
35
src/ui/base/IUIManager.h
Normal file
35
src/ui/base/IUIManager.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3_ttf/SDL_ttf.h>
|
||||
#include "ui/components/Button.h"
|
||||
#include "ui/components/Label.h"
|
||||
#include "ui/base/UIRenderData.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
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<int, std::unique_ptr<Button>> m_buttons;
|
||||
std::unordered_map<int, std::unique_ptr<Label>> m_labels;
|
||||
UIRenderData m_uiRenderData;
|
||||
SDL_Renderer* m_renderer = nullptr;
|
||||
TextRenderer* m_textRenderer = nullptr;
|
||||
|
||||
size_t makeHash(const std::string& name) {
|
||||
return std::hash<std::string>{}(name);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user