diff --git a/src/core/GameApplication.cpp b/src/core/GameApplication.cpp index bd11a97..1ea0d23 100644 --- a/src/core/GameApplication.cpp +++ b/src/core/GameApplication.cpp @@ -18,12 +18,25 @@ bool GameApplication::initialize() { SDL_Log("无法初始化 SDL_ttf: %s", SDL_GetError()); return false; } + // 输入管理 m_inputManager = std::make_unique(); + // 窗口管理 m_windowManager = std::make_unique(); m_windowManager->Initialize(m_config); - m_sceneManager = std::make_unique(m_windowManager->GetRenderer()); + + + // 字体管理 + m_fontManager = std::make_unique(); + // 文字渲染 + m_textRenderer = std::make_unique(m_windowManager->GetRenderer(), m_fontManager.get()); + + m_uiRenderer = std:: make_unique(m_windowManager->GetRenderer(), m_textRenderer.get()); + + // 场景管理 + m_sceneManager = std::make_unique(m_windowManager->GetRenderer(), m_uiRenderer.get()); + return true; } diff --git a/src/core/GameApplication.h b/src/core/GameApplication.h index 9aa2be0..430216e 100644 --- a/src/core/GameApplication.h +++ b/src/core/GameApplication.h @@ -5,7 +5,10 @@ #include "scenes/base/SceneManager.h" #include "input/InputManager.h" #include "utils/Config.h" -#include + +#include "graphics/font/TextRenderer.h" +#include "graphics/font/FontManager.h" + class GameApplication { private: @@ -14,6 +17,10 @@ private: std::unique_ptr m_windowManager; std::unique_ptr m_sceneManager; std::unique_ptr m_inputManager; + std::unique_ptr m_fontManager; + std::unique_ptr m_textRenderer; + std::unique_ptr m_uiRenderer; + GameConfig m_config; public: diff --git a/src/graphics/ui/UIRenderer.cpp b/src/graphics/ui/UIRenderer.cpp new file mode 100644 index 0000000..09e71ea --- /dev/null +++ b/src/graphics/ui/UIRenderer.cpp @@ -0,0 +1,106 @@ +#include "UIRenderer.h" + +UIRenderer::UIRenderer(SDL_Renderer* renderer, TextRenderer* textRenderer) : + m_renderer(renderer), + //m_fontManager(fontManager), + m_textRenderer(textRenderer) +{ + +} +UIRenderer::~UIRenderer() { + +} + +void UIRenderer::renderUI(const UIRenderData& uiRenderData) { + if (!m_renderer) return; + // 控制颜色混合方式 用 NONE 可避免所有混合 + //SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_NONE); + + // 保存当前渲染状态 + SDL_Color previousDrawColor; + SDL_GetRenderDrawColor(m_renderer, + &previousDrawColor.r, + &previousDrawColor.g, + &previousDrawColor.b, + &previousDrawColor.a); + + // 设置UI渲染状态 + // 例如:SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_BLEND); + for (auto& button : uiRenderData.buttons) { + renderButton(button); + } + + + + // 恢复之前的渲染状态 + SDL_SetRenderDrawColor(m_renderer, + previousDrawColor.r, + previousDrawColor.g, + previousDrawColor.b, + previousDrawColor.a); +} + +void UIRenderer::renderButton(const ButtonData& buttonData) { + //SDL_Log("render button\n"); + renderButtonBackground(buttonData); + + renderButtonBorder(buttonData); + renderButtonText(buttonData); +} + +void UIRenderer::renderButtonBackground(const ButtonData& buttonData) { + // 设置绘制颜色 + if (!m_renderer) { + SDL_Log("renderer is null\n"); + } + // SDL_Log("renderButtonBackground called for rect (%d,%d,%d,%d)", + // buttonData.rect.x, buttonData.rect.y, buttonData.rect.w, buttonData.rect.h); + //SDL_Log("start render background\n"); + auto m_backgroundColor = buttonData.backgroundColor; + auto m_rect = buttonData.rect; + + SDL_SetRenderDrawColor(m_renderer, m_backgroundColor.r, m_backgroundColor.g, m_backgroundColor.b, m_backgroundColor.a); + + // 绘制普通矩形 + SDL_FRect rect = { static_cast(m_rect.x), + static_cast(m_rect.y), + static_cast(m_rect.w), + static_cast(m_rect.h) }; + SDL_RenderRect(m_renderer, &rect); +} + +void UIRenderer::renderButtonBorder(const ButtonData& buttonData) { + +} + +void UIRenderer::renderButtonText(const ButtonData& buttonData) { + auto m_text = buttonData.text; + auto m_rect = buttonData.rect; + auto m_textStyle = buttonData.textstytle; + if (m_text.empty()) return; + + // 计算文本位置(居中) + // 这里需要TextRenderer的实际实现 + // 假设TextRenderer有一个renderText方法: + // void renderText(SDL_Renderer* renderer, const std::string& text, + // const TextStyle& style, int x, int y); + + // 计算居中位置 + // 注意:这里需要获取文本的实际大小来计算居中 + // 由于不知道TextRenderer的具体实现,这里提供基本思路 + + // 假设可以获取文本尺寸 + // SDL_Point textSize = m_textRenderer.getTextSize(m_text, m_textStyle); + + // 计算居中位置 + // int textX = static_cast(m_rect.x + (m_rect.w - textSize.x) / 2); + // int textY = static_cast(m_rect.y + (m_rect.h - textSize.y) / 2); + + // 临时实现:直接在中心位置渲染 + int centerX = static_cast(m_rect.x + m_rect.w / 2); + int centerY = static_cast(m_rect.y + m_rect.h / 2); + + // 根据你的TextRenderer实际API调整 + m_textRenderer->renderText(m_text, m_textStyle, + centerX, centerY); // true表示居中 +} diff --git a/src/graphics/ui/UIRenderer.h b/src/graphics/ui/UIRenderer.h new file mode 100644 index 0000000..9dc9159 --- /dev/null +++ b/src/graphics/ui/UIRenderer.h @@ -0,0 +1,40 @@ +#pragma once +#include +#include +#include "graphics/font/TextRenderer.h" +#include "graphics/font/FontManager.h" +#include "ui/base/UIRenderData.h" +class UIRenderer { +private: + TextRenderer* m_textRenderer; + + + SDL_Renderer* m_renderer; + + + /** + * @brief 渲染按钮背景 + * @param renderer SDL渲染器 + */ + void renderButtonBackground(const ButtonData& buttonData); + + /** + * @brief 渲染按钮边框 + * @param renderer SDL渲染器 + */ + void renderButtonBorder(const ButtonData& buttonData); + + /** + * @brief 渲染按钮文本 + * @param renderer SDL渲染器 + */ + void renderButtonText(const ButtonData& buttonData); +public: + + UIRenderer(SDL_Renderer* m_renderer, TextRenderer* textRenderer); + ~UIRenderer(); + void renderUI(const UIRenderData& uiRenderData); + + void renderButton(const ButtonData& buttonData); + +}; \ No newline at end of file diff --git a/src/scenes/base/Scene.h b/src/scenes/base/Scene.h index b01484d..e0c2e39 100644 --- a/src/scenes/base/Scene.h +++ b/src/scenes/base/Scene.h @@ -1,16 +1,17 @@ // ui/Scene.h - 场景基类 #pragma once #include - +#include "graphics/ui/UIRenderer.h" class Scene { public: + virtual ~Scene() = default; // 空实现,可选择性重写 - virtual void onEnter(SDL_Renderer* renderer, int width, int height){}; + virtual void onEnter(SDL_Renderer* renderer, int width, int height, UIRenderer* uiRenderer){}; virtual void onExit(){}; virtual void update() = 0; @@ -18,6 +19,9 @@ public: virtual void render() = 0; // 处理点击 virtual void handleClick(float x, float y) = 0; - +protected: + // UIRenderer应该由上层管理 + UIRenderer* m_uiRenderer; + SDL_Renderer* m_renderer; }; diff --git a/src/scenes/base/SceneManager.cpp b/src/scenes/base/SceneManager.cpp index 2fa4472..ddef384 100644 --- a/src/scenes/base/SceneManager.cpp +++ b/src/scenes/base/SceneManager.cpp @@ -1,8 +1,12 @@ #include "SceneManager.h" -SceneManager::SceneManager(SDL_Renderer* renderer) { +SceneManager::SceneManager(SDL_Renderer* renderer, UIRenderer* uiRenderer) : + m_renderer(renderer), + m_uiRenderer(uiRenderer) + +{ m_scene = std::make_unique(); - m_scene->onEnter(renderer, 1600, 900); + m_scene->onEnter(renderer, 1600, 900, m_uiRenderer); } SceneManager::~SceneManager() { diff --git a/src/scenes/base/SceneManager.h b/src/scenes/base/SceneManager.h index 566c472..36bd6f8 100644 --- a/src/scenes/base/SceneManager.h +++ b/src/scenes/base/SceneManager.h @@ -8,7 +8,7 @@ #include class SceneManager { public: - SceneManager(SDL_Renderer* renderer); + SceneManager(SDL_Renderer* renderer, UIRenderer* uiRenderer); ~SceneManager(); void PushScene(const std::string& sceneName, std::unique_ptr scene); @@ -20,6 +20,8 @@ public: private: + SDL_Renderer* m_renderer; + UIRenderer* m_uiRenderer; std::unique_ptr m_scene; // 用栈的形式来存储场景 std::stack> m_scenes; diff --git a/src/scenes/gameplay/GameScene.cpp b/src/scenes/gameplay/GameScene.cpp index a47f145..707429c 100644 --- a/src/scenes/gameplay/GameScene.cpp +++ b/src/scenes/gameplay/GameScene.cpp @@ -8,13 +8,21 @@ GameScene::~GameScene() { } -void GameScene::onEnter(SDL_Renderer* renderer, int WIDTH, int HEIGHT) { - m_renderer = std::make_unique(WIDTH, HEIGHT, renderer); +void GameScene::onEnter(SDL_Renderer* renderer, int WIDTH, int HEIGHT, UIRenderer* uiRenderer){ + m_renderer = renderer; + m_uiRenderer = uiRenderer; + m_gameUIManager = std::make_unique(); + m_gameUIManager->init(); + m_gameRenderer = std::make_unique(WIDTH, HEIGHT, renderer); m_gameSession = std::make_unique(); m_CoordinateConverter = std::make_unique(renderer); m_gameSession->initialize(); - m_renderer->setBoard(m_gameSession->getBoard()); + m_gameRenderer->setBoard(m_gameSession->getBoard()); + + + + } void GameScene::update() { @@ -22,15 +30,17 @@ void GameScene::update() { } void GameScene::render() { - m_renderer->beginFrame(); - m_renderer->drawBackground(); - m_renderer->drawBoard(); - m_renderer->drawPiece(m_gameSession->getSelectedPiece()); - m_renderer->endFrame(); + m_gameRenderer->beginFrame(); + m_gameRenderer->drawBackground(); + m_gameRenderer->drawBoard(); + + m_gameRenderer->drawPiece(m_gameSession->getSelectedPiece()); + m_uiRenderer->renderUI(m_gameUIManager->getUIRenderData()); + m_gameRenderer->endFrame(); } void GameScene::handleClick(float screenX, float screenY) { - auto click = m_CoordinateConverter->ScreenToBoard(screenX, screenY, m_renderer->getBoardArea()); + auto click = m_CoordinateConverter->ScreenToBoard(screenX, screenY, m_gameRenderer->getBoardArea()); if (click) { auto [row, col] = click.value(); m_gameSession->handleCoordinateInput(row, col); diff --git a/src/scenes/gameplay/GameScene.h b/src/scenes/gameplay/GameScene.h index 565569d..0411bef 100644 --- a/src/scenes/gameplay/GameScene.h +++ b/src/scenes/gameplay/GameScene.h @@ -3,19 +3,27 @@ #include "graphics/GameRenderer.h" #include "graphics/CoordinateConverter.h" #include "game/GameSession.h" +#include "graphics/ui/UIRenderer.h" +#include "ui/managers/GameUIManager.h" #include class GameScene : public Scene { public: GameScene(); ~GameScene(); - void onEnter(SDL_Renderer* renderer, int WIDTH, int HEIGHT) override; + void onEnter(SDL_Renderer* renderer, int WIDTH, int HEIGHT, UIRenderer* uiRenderer) override; void update() override; void render() override; void handleClick(float screenX, float screenY) override; private: - std::unique_ptr m_renderer; + + std::unique_ptr m_gameRenderer; std::unique_ptr m_CoordinateConverter; std::unique_ptr m_gameSession; + + std::unique_ptr m_gameUIManager; + + + }; \ No newline at end of file diff --git a/src/ui/base/UIComponent.h b/src/ui/base/UIComponent.h new file mode 100644 index 0000000..82f96f4 --- /dev/null +++ b/src/ui/base/UIComponent.h @@ -0,0 +1,80 @@ +#pragma once +#include + +/** + * @brief UI组件抽象基类 + * + * 所有UI组件(按钮、标签、面板等)都应继承此类 + * 提供统一的接口用于渲染、更新和事件处理 + */ + + + +class UIComponent { +public: + virtual ~UIComponent() = default; + + + + /** + * @brief 更新组件状态 + * @param deltaTime 距离上一帧的时间(秒),用于动画 + */ + + virtual void update(float deltaTime) = 0; + + /** + * @brief 处理SDL事件 + * @param event SDL事件引用 + */ + + //virtual void handleEvent + + /** + * @brief 设置组件位置 + * @param x X坐标(屏幕像素) + * @param y Y坐标(屏幕像素) + */ + + virtual void setPosition(int x, int y) { + m_rect.x = static_cast(x); + m_rect.y = static_cast(y); + } + + /** + * @brief 获取组件边界矩形 + * @return SDL_FRect 浮点矩形 + */ + SDL_FRect getBounds() const { return m_rect; } + + /** + * @brief 设置是否可见 + * @param visible 可见性标志 + */ + void setVisible(bool visible) { m_isVisible = visible; } + + /** + * @brief 检查是否可见 + * @return true 如果组件可见 + */ + bool isVisible() const { return m_isVisible; } + + /** + * @brief 设置是否启用交互 + * @param enabled 启用标志 + */ + void setEnabled(bool enabled) { m_isEnabled = enabled; } + + /** + * @brief 检查是否启用 + * @return true 如果组件启用 + */ + bool isEnabled() const { return m_isEnabled; } + +protected: + UIComponent() = default; + SDL_FRect m_rect = {0.0f, 0.0f, 0.0f, 0.0f}; ///< 组件位置和尺寸 + bool m_isVisible = true; ///< 可见性标志 + bool m_isEnabled = true; ///< 启用标志 + +}; \ No newline at end of file diff --git a/src/ui/base/UIRenderData.h b/src/ui/base/UIRenderData.h new file mode 100644 index 0000000..74ac4b7 --- /dev/null +++ b/src/ui/base/UIRenderData.h @@ -0,0 +1,20 @@ +#pragma once +#include "graphics/font/Textstyle.h" +#include +#include +struct ButtonData +{ + std::string text; + TextStyle textstytle; + SDL_FRect rect; + SDL_Color backgroundColor; + + int borderThickness; + SDL_Color borderColor; +}; + +// 可以通过索引方式实现,及直接传递Button指针, 在UIRender里面调用getData +struct UIRenderData +{ + std::vector buttons; +}; diff --git a/src/ui/components/Button.cpp b/src/ui/components/Button.cpp new file mode 100644 index 0000000..70d2aa6 --- /dev/null +++ b/src/ui/components/Button.cpp @@ -0,0 +1,33 @@ +#include "Button.h" + +Button::Button() + +{ + +} + + +void Button::setText(const std::string& text, TextStyle style) { + m_buttonData.text = text; + m_buttonData.textstytle = style; +} + +void Button::setBackgroundColor(SDL_Color normal) { + m_buttonData.backgroundColor = normal; +} + +void Button::setBorder(int thickness, SDL_Color color) { + m_buttonData.borderThickness = thickness; + m_buttonData.borderColor = color; +} + + +void Button::update(float deltaTime) { + +} + + + + + + diff --git a/src/ui/components/Button.h b/src/ui/components/Button.h new file mode 100644 index 0000000..b1cf4f6 --- /dev/null +++ b/src/ui/components/Button.h @@ -0,0 +1,48 @@ +#pragma once +#include "ui/base/UIRenderData.h" +#include "ui/base/UIComponent.h" +class Button : public UIComponent{ +public: + Button(); + ~Button() override = default; + + // 实现UIComponent接口 + + void update(float deltaTime) override; + + /** + * @brief 设置按钮文本 + * @param text 按钮文本 + * @param style 文本样式 + */ + void setText(const std::string& text, TextStyle style); + + /** + * @brief 设置背景颜色 + * @param normal 正常状态颜色 + * @param hovered 悬停状态颜色(可选,默认透明) + * @param pressed 按下状态颜色(可选,默认透明) + */ + void setBackgroundColor(SDL_Color normal); + /** + * @brief 设置边框 + * @param thickness 边框厚度(像素) + * @param color 边框颜色 + */ + void setBorder(int thickness, SDL_Color color); + + + ButtonData& getButtonDate() { + m_buttonData.rect = m_rect; + return m_buttonData; + } + +private: + + + + + ButtonData m_buttonData; + + +}; diff --git a/src/ui/managers/GameUIManager.cpp b/src/ui/managers/GameUIManager.cpp new file mode 100644 index 0000000..0b4897f --- /dev/null +++ b/src/ui/managers/GameUIManager.cpp @@ -0,0 +1,43 @@ +#include "GameUIManager.h" + +GameUIManager::GameUIManager() +{ + +} + +GameUIManager::~GameUIManager() { + +} + +void GameUIManager::init() { + auto button = std::make_unique