From ad3b8d7732fd86bf79d168e982c358f02deac6ad Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Sun, 14 Dec 2025 10:33:06 +0800 Subject: [PATCH] Added GameOver Scene --- src/game/GameSession.cpp | 34 +++++++- src/game/GameSession.h | 3 + src/game/GameTypes.h | 11 +-- src/graphics/game/BoardRenderer.cpp | 26 ++++++ src/graphics/game/BoardRenderer.h | 6 ++ src/scenes/gameplay/GameScene.cpp | 28 ++++++ src/scenes/gameplay/GameScene.h | 2 + src/ui/components/Button.cpp | 33 +++++++- src/ui/components/Button.h | 14 ++- src/ui/managers/GameUIManager.cpp | 117 ++++++++++++++++++++++---- src/ui/managers/GameUIManager.h | 16 +++- src/ui/managers/MainMenuUIManager.cpp | 4 +- 12 files changed, 264 insertions(+), 30 deletions(-) diff --git a/src/game/GameSession.cpp b/src/game/GameSession.cpp index 6a507bf..2c22548 100644 --- a/src/game/GameSession.cpp +++ b/src/game/GameSession.cpp @@ -19,6 +19,15 @@ void GameSession::cleanup() { bool GameSession::initialize() { + + // 重置游戏状态为运行中 + m_gameState = GameState::GAME_RUNING; + + // 重置其它必要状态 + m_currentPlayer = PlayerID::P1; + m_currentActionType = ActionType::GROW; + m_seletedPiece = std::nullopt; + // 初始化游戏特定资源(棋盘、棋子等) if (!m_board->initialize()) { return false; @@ -109,6 +118,14 @@ void GameSession::nextTurn() { } bool GameSession::handleCoordinateInput(int row, int col) { + if (!m_board) { + std::cout << "board is null\n"; + return false; + } + if (m_gameState != GameState::GAME_RUNING) { + std::cout << "game is not running\n"; + return false; + } // 如果当前没有选择棋子就选择棋子 if (m_seletedPiece == std::nullopt) { if (!Rule::canbeSelect(m_board->getPieceAt(row, col), m_currentPlayer)) { @@ -162,7 +179,15 @@ bool GameSession::handleCoordinateInput(int row, int col) { return false; } - + auto opponent = (m_currentPlayer == PlayerID::P1) ? PlayerID::P2 : PlayerID::P1;; + //检查胜利条件 + if (m_board->getAllPlayerComponent(opponent).empty()) { + m_gameState = GameState::GAME_WIN; + m_actionableComponents.clear(); + m_seletedPiece = std::nullopt; + std::cout << "Player " << ((m_currentPlayer == PlayerID::P1) ? "P1" : "P2") << " wins!\n"; + return true; + } // 执行完之后检查是否m_actionableComponents为空, // m_actionableComponents只保存了ID,但是在执行棋子之后会处理组件的连通性,rule会获取到新的连通性,不过这样更符合逻辑 @@ -193,6 +218,9 @@ int GameSession::getOldComponentID(int row, int col) { } std::optional> GameSession::getSelectedPiece() const { + if (m_gameState != GameState::GAME_RUNING) { + return std::nullopt; + } return m_seletedPiece; } @@ -202,4 +230,8 @@ int GameSession::getOldComponentID(int row, int col) { ActionType GameSession::getCurrentActionType() const { return m_currentActionType; + } + + GameState GameSession::getGameState() const { + return m_gameState; } \ No newline at end of file diff --git a/src/game/GameSession.h b/src/game/GameSession.h index f4da8cf..58d38fa 100644 --- a/src/game/GameSession.h +++ b/src/game/GameSession.h @@ -21,6 +21,7 @@ private: // 如果操作执行成功就从 void markComponentAsUsed(int componentID); + GameState m_gameState = GameState::GAME_RUNING; public: GameSession(); @@ -55,4 +56,6 @@ public: ActionType getCurrentActionType() const; + GameState getGameState() const; + }; \ No newline at end of file diff --git a/src/game/GameTypes.h b/src/game/GameTypes.h index 3c2adad..6c39510 100644 --- a/src/game/GameTypes.h +++ b/src/game/GameTypes.h @@ -15,14 +15,9 @@ enum class ActionType { }; enum class GameState { - MAIN_MENU, // 主菜单(开始界面) - SETTINGS, // 设置界面(音量、画质等) - GAME_SETUP, // 对局前的设置(选择模式、玩家类型等) - IN_GAME, // 正在对局中(核心逻辑运行) - PAUSED, // 游戏暂停(可返回菜单或继续) - GAME_OVER, // 对局结束(显示胜负/平局) - TUTORIAL, // 教程模式(可选) - EXITING // 退出确认或正在退出 + GAME_RUNING, // 游戏进行中 + GAME_LOSE, // 游戏失败 + GAME_WIN, // 游戏胜利 }; enum class GameMode { diff --git a/src/graphics/game/BoardRenderer.cpp b/src/graphics/game/BoardRenderer.cpp index 0983f29..02cbd20 100644 --- a/src/graphics/game/BoardRenderer.cpp +++ b/src/graphics/game/BoardRenderer.cpp @@ -198,3 +198,29 @@ BoardArea BoardRenderer::getBoardArea() const { }; } +void BoardRenderer::setGameState(GameState state) { + m_gameState = state; +} + +void BoardRenderer::renderBlackOverlay() { + if (!m_renderer) return; + if (m_gameState == GameState::GAME_RUNING) return; + // 开启混合模式(重要!) + SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_BLEND); + + // 设置黑色半透明颜色 + SDL_SetRenderDrawColor(m_renderer, 0, 0, 0, 150); // 半透明黑色 + + // 绘制覆盖整个屏幕的矩形 + SDL_FRect rect{ + 0.0f, + 0.0f, + static_cast(m_Width), + static_cast(m_Height) + }; + + SDL_RenderFillRect(m_renderer, &rect); + + // 恢复原来的混合模式 + SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_NONE); +} \ No newline at end of file diff --git a/src/graphics/game/BoardRenderer.h b/src/graphics/game/BoardRenderer.h index 23422de..648dec8 100644 --- a/src/graphics/game/BoardRenderer.h +++ b/src/graphics/game/BoardRenderer.h @@ -31,6 +31,9 @@ private: float m_pieceRadiusRatio = 0.8f; // 棋子半径相对于格子大小的比例 PlayerColors m_colors; std::unordered_set m_currentDrawRange; + + GameState m_gameState = GameState::GAME_RUNING; + public: BoardRenderer(int WIDTH, int HEIGHT, SDL_Renderer* renderer); @@ -55,6 +58,9 @@ public: void drawMovementRange(); BoardArea getBoardArea() const; + void setGameState(GameState state); + + void renderBlackOverlay(); }; diff --git a/src/scenes/gameplay/GameScene.cpp b/src/scenes/gameplay/GameScene.cpp index 1c01e44..06f4700 100644 --- a/src/scenes/gameplay/GameScene.cpp +++ b/src/scenes/gameplay/GameScene.cpp @@ -13,6 +13,9 @@ void GameScene::onEnter(SDL_Renderer* renderer, int WIDTH, int HEIGHT, UIRendere m_uiRenderer = uiRenderer; m_gameUIManager = std::make_unique(renderer, uiRenderer->getTextRenderer()); m_gameUIManager->init(); + m_gameUIManager->setCallback([this]() { + this->restartGame(); + }); m_boardRenderer = std::make_unique(WIDTH, HEIGHT, renderer); m_gameSession = std::make_unique(); m_CoordinateConverter = std::make_unique(renderer); @@ -36,18 +39,33 @@ void GameScene::render() { m_boardRenderer->drawPiece(m_gameSession->getSelectedPiece()); m_boardRenderer->drawMovementRange(); + m_boardRenderer->renderBlackOverlay(); m_uiRenderer->renderUI(m_gameUIManager->getUIRenderData()); endFrame(); } void GameScene::handleClick(float screenX, float screenY) { + if (m_gameUIManager->handleClick(screenX, screenY)) { + return; + } + + if (m_gameSession->getGameState() != GameState::GAME_RUNING) { + SDL_Log("Game is not running, click ignored."); + return; + } auto click = m_CoordinateConverter->ScreenToBoard(screenX, screenY, m_boardRenderer->getBoardArea()); if (click) { auto [row, col] = click.value(); m_gameSession->handleCoordinateInput(row, col); m_gameSession->printBoard(); + SDL_Log("try to updateActionType\n"); m_gameUIManager->updateActionType( m_gameSession->getCurrentActionType()); + SDL_Log("tyr to updateMovementRange\n"); m_boardRenderer->updateMovementRange(m_gameSession->getSelectedPiece(), m_gameSession->getCurrentActionType()); + SDL_Log("tyr to updateGameState\n"); + m_gameUIManager->updateGameState(m_gameSession->getGameState()); + m_boardRenderer->setGameState(m_gameSession->getGameState()); + } else { SDL_Log("invail cilck aera!"); } @@ -55,4 +73,14 @@ void GameScene::handleClick(float screenX, float screenY) { void GameScene::renderMousePosition(float x, float y) { m_gameUIManager->UpdateMousePositon(x, y); +} + +void GameScene::restartGame() { + m_gameSession = std::make_unique(); + m_gameSession->initialize(); + m_boardRenderer->setBoard(m_gameSession->getBoard()); + m_boardRenderer->updateMovementRange(std::nullopt, ActionType::GROW); + m_gameUIManager->updateActionType(ActionType::GROW); + m_gameUIManager->updateGameState(GameState::GAME_RUNING); + m_boardRenderer->setGameState(GameState::GAME_RUNING); } \ No newline at end of file diff --git a/src/scenes/gameplay/GameScene.h b/src/scenes/gameplay/GameScene.h index 6e0da91..e8895b7 100644 --- a/src/scenes/gameplay/GameScene.h +++ b/src/scenes/gameplay/GameScene.h @@ -18,6 +18,8 @@ public: void handleClick(float screenX, float screenY) override; void renderMousePosition(float x, float y) override; + + void restartGame(); private: std::unique_ptr m_boardRenderer; diff --git a/src/ui/components/Button.cpp b/src/ui/components/Button.cpp index d9dab92..afe0db3 100644 --- a/src/ui/components/Button.cpp +++ b/src/ui/components/Button.cpp @@ -9,6 +9,35 @@ Button::Button() Button::Button(TextRenderer* textRenderer) : m_textRenderer(textRenderer) { } +Button::Button( + const std::string& text, + TextStyle style, + int x, + int y, + TextRenderer* textRenderer, + SDL_Color backgroundColor, + int borderThickness, + SDL_Color borderColor +) : m_textRenderer(textRenderer) +{ + m_rect.x = static_cast(x); + m_rect.y = static_cast(y); + m_buttonData.text = text; + m_buttonData.textstytle = style; + m_buttonData.backgroundColor = backgroundColor; + m_buttonData.borderThickness = borderThickness; + m_buttonData.borderColor = borderColor; + + // 如果提供了 TextRenderer,则立即测量文本并更新控件尺寸 + if (m_textRenderer) { + auto [w, h] = m_textRenderer->getTextSize(text, style); + m_rect.w = static_cast(w); + m_rect.h = static_cast(h); + m_buttonData.rect = m_rect; + } +} + + void Button::setText(const std::string& text, TextStyle style) { m_buttonData.text = text; m_buttonData.textstytle = style; @@ -51,15 +80,17 @@ void Button::setCallback(std::function callback) { void Button::update(float deltaTime) { } -void Button::handleCilck(int x, int y) { +bool Button::handleCilck(int x, int y) { if (m_callback && m_isEnabled && m_isVisible) { //SDL_Log("rect x: %f, y: %f, w: %f, h: %f", m_rect.x, m_rect.y, m_rect.w, m_rect.h); if (x >= m_rect.x && x <= m_rect.x + m_rect.w && y >= m_rect.y && y <= m_rect.y + m_rect.h) { m_callback(); + return true; } } + return false; } diff --git a/src/ui/components/Button.h b/src/ui/components/Button.h index 47b95c7..f96a2e5 100644 --- a/src/ui/components/Button.h +++ b/src/ui/components/Button.h @@ -12,6 +12,18 @@ public: Button(); // 可以传入 TextRenderer 指针以便在 setText 时立即计算文字尺寸并更新 rect explicit Button(TextRenderer* textRenderer); + + explicit Button( + const std::string& text, + TextStyle style = {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}}, + int x = 0, + int y = 0, + TextRenderer* textRenderer = nullptr, + SDL_Color backgroundColor = {200, 200, 200, 255}, + int borderThickness = 0, + SDL_Color borderColor = {0, 0, 0, 255} + + ); ~Button() override = default; // 实现UIComponent接口 @@ -57,7 +69,7 @@ public: * @param x 点击位置的X坐标 * @param y 点击位置的Y坐标 */ - void handleCilck(int x, int y); + bool handleCilck(int x, int y); private: diff --git a/src/ui/managers/GameUIManager.cpp b/src/ui/managers/GameUIManager.cpp index 8d50a99..0c6035f 100644 --- a/src/ui/managers/GameUIManager.cpp +++ b/src/ui/managers/GameUIManager.cpp @@ -11,21 +11,12 @@ GameUIManager::~GameUIManager() { } void GameUIManager::init() { - auto button = std::make_unique