mirror of
https://github.com/zhenyan121/SporeBG-Conid.git
synced 2026-04-10 06:14:08 +08:00
Added GameOver Scene
This commit is contained in:
@@ -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<std::pair<int, int>> 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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<float>(m_Width),
|
||||
static_cast<float>(m_Height)
|
||||
};
|
||||
|
||||
SDL_RenderFillRect(m_renderer, &rect);
|
||||
|
||||
// 恢复原来的混合模式
|
||||
SDL_SetRenderDrawBlendMode(m_renderer, SDL_BLENDMODE_NONE);
|
||||
}
|
||||
@@ -31,6 +31,9 @@ private:
|
||||
float m_pieceRadiusRatio = 0.8f; // 棋子半径相对于格子大小的比例
|
||||
PlayerColors m_colors;
|
||||
std::unordered_set<int> 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();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ void GameScene::onEnter(SDL_Renderer* renderer, int WIDTH, int HEIGHT, UIRendere
|
||||
m_uiRenderer = uiRenderer;
|
||||
m_gameUIManager = std::make_unique<GameUIManager>(renderer, uiRenderer->getTextRenderer());
|
||||
m_gameUIManager->init();
|
||||
m_gameUIManager->setCallback([this]() {
|
||||
this->restartGame();
|
||||
});
|
||||
m_boardRenderer = std::make_unique<BoardRenderer>(WIDTH, HEIGHT, renderer);
|
||||
m_gameSession = std::make_unique<GameSession>();
|
||||
m_CoordinateConverter = std::make_unique<CoordinateConverter>(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<GameSession>();
|
||||
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);
|
||||
}
|
||||
@@ -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<BoardRenderer> m_boardRenderer;
|
||||
|
||||
@@ -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<float>(x);
|
||||
m_rect.y = static_cast<float>(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<float>(w);
|
||||
m_rect.h = static_cast<float>(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<void()> 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -11,21 +11,12 @@ GameUIManager::~GameUIManager() {
|
||||
}
|
||||
|
||||
void GameUIManager::init() {
|
||||
auto button = std::make_unique<Button>(m_textRenderer);
|
||||
button->setBackgroundColor({255, 100, 0, 255});
|
||||
button->setBorder(2, {0, 0, 0, 255});
|
||||
button->setPosition(20, 20);
|
||||
button->setEnabled(true);
|
||||
button->setVisible(true);
|
||||
button->setText("Please Choose", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
||||
button->setName("ActionButton");
|
||||
m_buttons.emplace(button->getNameHash(), std::move(button));
|
||||
//m_restartCallback = restartCallback;
|
||||
setupUIComponents();
|
||||
}
|
||||
|
||||
auto label = std::make_unique<Label>();
|
||||
label->setPosition(1200, 20);
|
||||
label->setText("0 0", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
||||
label->setName("MousePositionLabel");
|
||||
m_labels.emplace(label->getNameHash(), std::move(label));
|
||||
void GameUIManager::setCallback(std::function<void()> restartCallback) {
|
||||
m_restartCallback = restartCallback;
|
||||
}
|
||||
|
||||
|
||||
@@ -92,4 +83,100 @@ void GameUIManager::updateActionType(ActionType type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameUIManager::updateGameState(GameState state) {
|
||||
if (m_currentGameState == state) {
|
||||
SDL_Log("State unchanged, returning early\n");
|
||||
return;
|
||||
}
|
||||
// 根据传入的 GameState 更新 UI 组件状态
|
||||
{
|
||||
auto buttonIt = m_buttons.find(makeHash("RestartButton"));
|
||||
if (buttonIt != m_buttons.end()) {
|
||||
auto& button = buttonIt->second;
|
||||
if (state != GameState::GAME_RUNING) {
|
||||
//button->setBackgroundColor({255, 215, 0, 255}); // 金色
|
||||
//button->setText("GAME OVER");
|
||||
button->setVisible(true);
|
||||
button->setEnabled(true);
|
||||
} else {
|
||||
button->setVisible(false);
|
||||
button->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
auto actionButtonIt = m_buttons.find(makeHash("ActionButton"));
|
||||
if (actionButtonIt != m_buttons.end()) {
|
||||
auto& actionButton = actionButtonIt->second;
|
||||
if (state != GameState::GAME_RUNING) {
|
||||
actionButton->setEnabled(false);
|
||||
actionButton->setVisible(false);
|
||||
} else {
|
||||
actionButton->setEnabled(true);
|
||||
actionButton->setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 保存当前游戏状态,避免后续相同状态更新被误判为无变化
|
||||
m_currentGameState = state;
|
||||
}
|
||||
|
||||
void GameUIManager::setupUIComponents() {
|
||||
// 这里可以添加更多的UI组件初始化逻辑
|
||||
|
||||
auto button = std::make_unique<Button>(m_textRenderer);
|
||||
button->setBackgroundColor({255, 100, 0, 255});
|
||||
button->setBorder(2, {0, 0, 0, 255});
|
||||
button->setPosition(20, 20);
|
||||
button->setEnabled(true);
|
||||
button->setVisible(true);
|
||||
button->setText("Please Choose", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
||||
button->setName("ActionButton");
|
||||
m_buttons.emplace(button->getNameHash(), std::move(button));
|
||||
|
||||
auto label = std::make_unique<Label>();
|
||||
label->setPosition(1200, 20);
|
||||
label->setText("0 0", {"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}});
|
||||
label->setName("MousePositionLabel");
|
||||
m_labels.emplace(label->getNameHash(), std::move(label));
|
||||
|
||||
auto restartButton = std::make_unique<Button>(
|
||||
"Restart",
|
||||
(TextStyle){"SourceHanSansSC-Regular.otf", 48, {0, 0, 0, 255}},
|
||||
700,
|
||||
500,
|
||||
m_textRenderer,
|
||||
(SDL_Color){100, 255, 100, 255}
|
||||
);
|
||||
|
||||
restartButton->setCallback([this](){
|
||||
if (m_restartCallback) {
|
||||
m_restartCallback();
|
||||
}
|
||||
});
|
||||
restartButton->setName("RestartButton");
|
||||
restartButton->setVisible(false); // 初始时隐藏
|
||||
restartButton->setEnabled(false);
|
||||
m_buttons.emplace(restartButton->getNameHash(), std::move(restartButton));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool GameUIManager::handleClick(float x, float y) {
|
||||
auto logicalPos = physicalToLogical(static_cast<float>(x), static_cast<float>(y), m_renderer);
|
||||
int lx = logicalPos.first;
|
||||
int ly = logicalPos.second;
|
||||
|
||||
// 遍历所有按钮,检查点击位置是否在按钮范围内
|
||||
for (auto& [id, button] : m_buttons) {
|
||||
if (button->isEnabled() && button->isVisible()) {
|
||||
if(button->handleCilck(lx, ly)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "ui/base/IUIManager.h"
|
||||
#include "game/GameTypes.h"
|
||||
#include <functional>
|
||||
class GameUIManager : public IUIManager {
|
||||
public:
|
||||
GameUIManager(SDL_Renderer* renderer, TextRenderer* textRenderer);
|
||||
@@ -9,6 +10,8 @@ public:
|
||||
|
||||
void init();
|
||||
|
||||
void setCallback(std::function<void()> restartCallback);
|
||||
|
||||
const UIRenderData& getUIRenderData();
|
||||
// 收集渲染数据
|
||||
void CollectRenderData();
|
||||
@@ -16,7 +19,14 @@ public:
|
||||
void UpdateMousePositon(float x, float y);
|
||||
|
||||
void updateActionType(ActionType type);
|
||||
private:
|
||||
|
||||
|
||||
};
|
||||
void updateGameState(GameState state);
|
||||
|
||||
bool handleClick(float x, float y);
|
||||
private:
|
||||
std::function<void()> m_restartCallback;
|
||||
void setupUIComponents();
|
||||
|
||||
GameState m_currentGameState = GameState::GAME_RUNING;
|
||||
};
|
||||
|
||||
|
||||
@@ -72,6 +72,8 @@ void MainMenuUIManager::handleClick(float x, float y) {
|
||||
auto logicPos = physicalToLogical(x, y, m_renderer);
|
||||
for (auto& buttonPair : m_buttons) {
|
||||
SDL_Log("Handling click at logical position (%d, %d)\n", logicPos.first, logicPos.second);
|
||||
buttonPair.second->handleCilck(logicPos.first, logicPos.second);
|
||||
if (buttonPair.second->handleCilck(logicPos.first, logicPos.second)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user