Major refactoring for game menu

This commit is contained in:
Vladislav Khorev 2026-02-22 19:50:13 +03:00
parent 5b57696acf
commit b59a10b7e6
8 changed files with 142 additions and 103 deletions

View File

@ -88,7 +88,7 @@ target_compile_definitions(space-game001 PRIVATE
WIN32_LEAN_AND_MEAN WIN32_LEAN_AND_MEAN
PNG_ENABLED PNG_ENABLED
SDL_MAIN_HANDLED SDL_MAIN_HANDLED
#NETWORK NETWORK
# SIMPLIFIED # SIMPLIFIED
) )

View File

@ -96,11 +96,24 @@
}, },
{ {
"type": "Button", "type": "Button",
"name": "exitButton", "name": "multiplayerButton2",
"x": 409, "x": 409,
"y": 218, "y": 218,
"width": 382, "width": 382,
"height": 56, "height": 56,
"textures": {
"normal": "resources/main_menu/multi.png",
"hover": "resources/main_menu/multi.png",
"pressed": "resources/main_menu/multi.png"
}
},
{
"type": "Button",
"name": "exitButton",
"x": 409,
"y": 147,
"width": 382,
"height": 56,
"textures": { "textures": {
"normal": "resources/main_menu/exit.png", "normal": "resources/main_menu/exit.png",
"hover": "resources/main_menu/exit.png", "hover": "resources/main_menu/exit.png",
@ -111,7 +124,7 @@
"type": "Button", "type": "Button",
"name": "versionLabel", "name": "versionLabel",
"x": 559.5, "x": 559.5,
"y": 170, "y": 99,
"width": 81, "width": 81,
"height": 9, "height": 9,
"textures": { "textures": {

View File

@ -21,10 +21,11 @@
#else #else
#include "network/WebSocketClient.h" #include "network/WebSocketClient.h"
#endif #endif
#else
#include "network/LocalClient.h"
#endif #endif
#include "network/LocalClient.h"
namespace ZL namespace ZL
{ {
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
@ -34,12 +35,6 @@ namespace ZL
const char* CONST_ZIP_FILE = ""; const char* CONST_ZIP_FILE = "";
#endif #endif
bool g_exitBgAnimating = false;
bool firePressed = false;
float x = 0;
Game::Game() Game::Game()
: window(nullptr) : window(nullptr)
, glContext(nullptr) , glContext(nullptr)
@ -88,9 +83,15 @@ namespace ZL
#endif #endif
menuManager.setupMenu(); menuManager.setupMenu();
renderer.InitOpenGL();
space.setup();
menuManager.onSingleplayerPressed = [this]() {
networkClient = std::make_unique<LocalClient>();
networkClient->Connect("", 0);
spaceGameStarted = 1;
};
menuManager.onMultiplayerPressed = [this]() {
#ifdef NETWORK #ifdef NETWORK
#ifdef EMSCRIPTEN #ifdef EMSCRIPTEN
networkClient = std::make_unique<WebSocketClientEmscripten>(); networkClient = std::make_unique<WebSocketClientEmscripten>();
@ -99,10 +100,13 @@ namespace ZL
networkClient = std::make_unique<WebSocketClient>(taskManager.getIOContext()); networkClient = std::make_unique<WebSocketClient>(taskManager.getIOContext());
networkClient->Connect("127.0.0.1", 8081); networkClient->Connect("127.0.0.1", 8081);
#endif #endif
#else
networkClient = std::make_unique<LocalClient>();
networkClient->Connect("", 0);
#endif #endif
lastTickCount = 0;
spaceGameStarted = 1;
};
renderer.InitOpenGL();
space.setup();
} }
@ -131,9 +135,21 @@ namespace ZL
CheckGlError(); CheckGlError();
} }
void Game::drawScene() { void Game::drawUnderMainMenu()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
}
void Game::drawScene() {
if (spaceGameStarted) {
space.drawScene(); space.drawScene();
}
else
{
drawUnderMainMenu();
}
drawUI(); drawUI();
CheckGlError(); CheckGlError();
} }
@ -142,7 +158,14 @@ namespace ZL
int64_t localNow = std::chrono::duration_cast<std::chrono::milliseconds>( int64_t localNow = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count(); std::chrono::system_clock::now().time_since_epoch()).count();
// Добавляем смещение, полученное от сервера // Добавляем смещение, полученное от сервера
return localNow + networkClient->getTimeOffset(); // Нужно добавить геттер в интерфейс if (networkClient)
{
return localNow + networkClient->getTimeOffset();
}
else
{
return localNow;
}
} }
void Game::processTickCount() { void Game::processTickCount() {
@ -167,8 +190,9 @@ namespace ZL
//throw std::runtime_error("Synchronization is lost"); //throw std::runtime_error("Synchronization is lost");
} }
if (spaceGameStarted) {
space.processTickCount(newTickCount, delta); space.processTickCount(newTickCount, delta);
}
menuManager.uiManager.update(static_cast<float>(delta)); menuManager.uiManager.update(static_cast<float>(delta));
lastTickCount = newTickCount; lastTickCount = newTickCount;
} }
@ -268,14 +292,8 @@ namespace ZL
} }
if (event.type == SDL_KEYUP) { if (event.type == SDL_KEYUP) {
if (event.key.keysym.sym == SDLK_i) {
x = x + 1;
}
if (event.key.keysym.sym == SDLK_k) {
x = x - 1;
}
if (event.key.keysym.sym == SDLK_a) { if (event.key.keysym.sym == SDLK_a) {
Environment::shipState.position = { 9466.15820, 1046.00159, 18531.2090 }; //Environment::shipState.position = { 9466.15820, 1046.00159, 18531.2090 };
} }
} }
#endif #endif
@ -293,23 +311,68 @@ namespace ZL
} }
mainThreadHandler.processMainThreadTasks(); mainThreadHandler.processMainThreadTasks();
if (spaceGameStarted) {
space.update(); space.update();
} }
}
void Game::handleDown(int mx, int my) void Game::handleDown(int mx, int my)
{ {
int uiX = mx;
int uiY = Environment::height - my;
menuManager.uiManager.onMouseDown(uiX, uiY);
bool uiHandled = false;
for (const auto& button : menuManager.uiManager.findButton("") ? std::vector<std::shared_ptr<UiButton>>{} : std::vector<std::shared_ptr<UiButton>>{}) {
(void)button;
}
auto pressedSlider = [&]() -> std::shared_ptr<UiSlider> {
for (const auto& slider : menuManager.uiManager.findSlider("") ? std::vector<std::shared_ptr<UiSlider>>{} : std::vector<std::shared_ptr<UiSlider>>{}) {
(void)slider;
}
return nullptr;
}();
if (!menuManager.uiManager.isUiInteraction()) {
if (spaceGameStarted) {
space.handleDown(mx, my); space.handleDown(mx, my);
} }
}
}
void Game::handleUp(int mx, int my) void Game::handleUp(int mx, int my)
{ {
int uiX = mx;
int uiY = Environment::height - my;
menuManager.uiManager.onMouseUp(uiX, uiY);
if (!menuManager.uiManager.isUiInteraction()) {
if (spaceGameStarted) {
space.handleUp(mx, my); space.handleUp(mx, my);
} }
}
}
void Game::handleMotion(int mx, int my) void Game::handleMotion(int mx, int my)
{ {
int uiX = mx;
int uiY = Environment::height - my;
menuManager.uiManager.onMouseMove(uiX, uiY);
if (!menuManager.uiManager.isUiInteraction()) {
if (spaceGameStarted) {
space.handleMotion(mx, my); space.handleMotion(mx, my);
} }
}
}
} // namespace ZL } // namespace ZL

View File

@ -41,6 +41,7 @@ namespace ZL {
void processTickCount(); void processTickCount();
void drawScene(); void drawScene();
void drawUI(); void drawUI();
void drawUnderMainMenu();
void handleDown(int mx, int my); void handleDown(int mx, int my);
void handleUp(int mx, int my); void handleUp(int mx, int my);
void handleMotion(int mx, int my); void handleMotion(int mx, int my);
@ -56,6 +57,8 @@ namespace ZL {
MenuManager menuManager; MenuManager menuManager;
Space space; Space space;
int spaceGameStarted = 0;
}; };

View File

@ -55,7 +55,7 @@ namespace ZL {
} }
}); });
uiManager.setAnimationCallback("exitButton", "bgScroll", []() { uiManager.setAnimationCallback("exitButton", "bgScroll", [this]() {
std::cerr << "Exit button bgScroll animation finished" << std::endl; std::cerr << "Exit button bgScroll animation finished" << std::endl;
g_exitBgAnimating = false; g_exitBgAnimating = false;
}); });
@ -96,10 +96,10 @@ namespace ZL {
}); });
uiManager.setButtonCallback("shootButton", [this](const std::string& name) { uiManager.setButtonCallback("shootButton", [this](const std::string& name) {
firePressed = true; onFirePressed();
}); });
uiManager.setButtonCallback("shootButton2", [this](const std::string& name) { uiManager.setButtonCallback("shootButton2", [this](const std::string& name) {
firePressed = true; onFirePressed();
}); });
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) { uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
int newVel = roundf(value * 10); int newVel = roundf(value * 10);
@ -114,16 +114,18 @@ namespace ZL {
}); });
}; };
uiManager.setButtonCallback("singleButton", [loadGameplayUI](const std::string& name) { uiManager.setButtonCallback("singleButton", [loadGameplayUI, this](const std::string& name) {
std::cerr << "Single button pressed: " << name << " -> load gameplay UI\n"; std::cerr << "Single button pressed: " << name << " -> load gameplay UI\n";
loadGameplayUI(); loadGameplayUI();
onSingleplayerPressed();
}); });
/*uiManager.setButtonCallback("multiplayerButton", [loadGameplayUI](const std::string& name) { uiManager.setButtonCallback("multiplayerButton", [loadGameplayUI, this](const std::string& name) {
std::cerr << "Multiplayer button pressed: " << name << " -> load gameplay UI\n"; std::cerr << "Multiplayer button pressed: " << name << " -> load gameplay UI\n";
loadGameplayUI(); loadGameplayUI();
});*/ onMultiplayerPressed();
});
uiManager.setButtonCallback("multiplayerButton", [this](const std::string& name) { uiManager.setButtonCallback("multiplayerButton2", [this](const std::string& name) {
std::cerr << "Multiplayer button pressed → opening multiplayer menu\n"; std::cerr << "Multiplayer button pressed → opening multiplayer menu\n";
uiManager.startAnimationOnNode("playButton", "buttonsExit"); uiManager.startAnimationOnNode("playButton", "buttonsExit");

View File

@ -7,21 +7,21 @@
namespace ZL { namespace ZL {
extern const char* CONST_ZIP_FILE; extern const char* CONST_ZIP_FILE;
extern bool g_exitBgAnimating; //extern bool g_exitBgAnimating;
extern bool firePressed;
class MenuManager class MenuManager
{ {
protected: protected:
Renderer& renderer; Renderer& renderer;
bool uiGameOverShown = false;
std::shared_ptr<UiNode> uiSavedRoot; std::shared_ptr<UiNode> uiSavedRoot;
std::shared_ptr<UiNode> gameOverSavedRoot; std::shared_ptr<UiNode> gameOverSavedRoot;
std::shared_ptr<UiNode> settingsSavedRoot; std::shared_ptr<UiNode> settingsSavedRoot;
std::shared_ptr<UiNode> multiplayerSavedRoot; std::shared_ptr<UiNode> multiplayerSavedRoot;
public: public:
bool uiGameOverShown = false;
bool g_exitBgAnimating = false;
UiManager uiManager; UiManager uiManager;
MenuManager(Renderer& iRenderer); MenuManager(Renderer& iRenderer);
@ -32,6 +32,10 @@ namespace ZL {
std::function<void()> onRestartPressed; std::function<void()> onRestartPressed;
std::function<void(float)> onVelocityChanged; std::function<void(float)> onVelocityChanged;
std::function<void()> onFirePressed;
std::function<void()> onSingleplayerPressed;
std::function<void()> onMultiplayerPressed;
}; };
}; };

View File

@ -30,10 +30,6 @@ namespace ZL
extern const char* CONST_ZIP_FILE; extern const char* CONST_ZIP_FILE;
extern bool g_exitBgAnimating;
extern bool firePressed;
extern float x; extern float x;
Eigen::Quaternionf generateRandomQuaternion(std::mt19937& gen) Eigen::Quaternionf generateRandomQuaternion(std::mt19937& gen)
@ -145,7 +141,7 @@ namespace ZL
return P; return P;
} }
bool Space::worldToScreen(const Vector3f& world, float& outX, float& outY, float& outDepth) const bool worldToScreen(const Vector3f& world, float& outX, float& outY, float& outDepth)
{ {
// Матрицы должны совпасть с drawBoxes/drawShip по смыслу // Матрицы должны совпасть с drawBoxes/drawShip по смыслу
float aspect = static_cast<float>(Environment::width) / static_cast<float>(Environment::height); float aspect = static_cast<float>(Environment::width) / static_cast<float>(Environment::height);
@ -176,7 +172,7 @@ namespace ZL
return true; return true;
} }
bool Space::projectToNDC(const Vector3f& world, float& ndcX, float& ndcY, float& ndcZ, float& clipW) const bool projectToNDC(const Vector3f& world, float& ndcX, float& ndcY, float& ndcZ, float& clipW)
{ {
float aspect = static_cast<float>(Environment::width) / static_cast<float>(Environment::height); float aspect = static_cast<float>(Environment::width) / static_cast<float>(Environment::height);
Eigen::Matrix4f V = makeViewMatrix_FromYourCamera(); Eigen::Matrix4f V = makeViewMatrix_FromYourCamera();
@ -278,6 +274,10 @@ namespace ZL
newShipVelocity = newVelocity; newShipVelocity = newVelocity;
}; };
menuManager.onFirePressed = [this]() {
firePressed = true;
};
bool cfgLoaded = sparkEmitter.loadFromJsonFile("resources/config/spark_config.json", renderer, CONST_ZIP_FILE); bool cfgLoaded = sparkEmitter.loadFromJsonFile("resources/config/spark_config.json", renderer, CONST_ZIP_FILE);
bool projCfgLoaded = projectileEmitter.loadFromJsonFile("resources/config/spark_projectile_config.json", renderer, CONST_ZIP_FILE); bool projCfgLoaded = projectileEmitter.loadFromJsonFile("resources/config/spark_projectile_config.json", renderer, CONST_ZIP_FILE);
bool explosionCfgLoaded = explosionEmitter.loadFromJsonFile("resources/config/explosion_config.json", renderer, CONST_ZIP_FILE); bool explosionCfgLoaded = explosionEmitter.loadFromJsonFile("resources/config/explosion_config.json", renderer, CONST_ZIP_FILE);
@ -566,7 +566,6 @@ namespace ZL
drawBoxesLabels(); drawBoxesLabels();
drawShip(); drawShip();
//drawUI();
drawTargetHud(); drawTargetHud();
CheckGlError(); CheckGlError();
} }
@ -1394,25 +1393,6 @@ namespace ZL
void Space::handleDown(int mx, int my) void Space::handleDown(int mx, int my)
{ {
int uiX = mx;
int uiY = Environment::height - my;
menuManager.uiManager.onMouseDown(uiX, uiY);
bool uiHandled = false;
for (const auto& button : menuManager.uiManager.findButton("") ? std::vector<std::shared_ptr<UiButton>>{} : std::vector<std::shared_ptr<UiButton>>{}) {
(void)button;
}
auto pressedSlider = [&]() -> std::shared_ptr<UiSlider> {
for (const auto& slider : menuManager.uiManager.findSlider("") ? std::vector<std::shared_ptr<UiSlider>>{} : std::vector<std::shared_ptr<UiSlider>>{}) {
(void)slider;
}
return nullptr;
}();
if (!menuManager.uiManager.isUiInteraction()) {
Environment::tapDownHold = true; Environment::tapDownHold = true;
Environment::tapDownStartPos(0) = mx; Environment::tapDownStartPos(0) = mx;
@ -1421,28 +1401,16 @@ namespace ZL
Environment::tapDownCurrentPos(0) = mx; Environment::tapDownCurrentPos(0) = mx;
Environment::tapDownCurrentPos(1) = my; Environment::tapDownCurrentPos(1) = my;
} }
}
void Space::handleUp(int mx, int my) void Space::handleUp(int mx, int my)
{ {
int uiX = mx;
int uiY = Environment::height - my;
menuManager.uiManager.onMouseUp(uiX, uiY);
if (!menuManager.uiManager.isUiInteraction()) {
Environment::tapDownHold = false; Environment::tapDownHold = false;
}
} }
void Space::handleMotion(int mx, int my) void Space::handleMotion(int mx, int my)
{ {
int uiX = mx; if (Environment::tapDownHold) {
int uiY = Environment::height - my;
menuManager.uiManager.onMouseMove(uiX, uiY);
if (Environment::tapDownHold && !menuManager.uiManager.isUiInteraction()) {
Environment::tapDownCurrentPos(0) = mx; Environment::tapDownCurrentPos(0) = mx;
Environment::tapDownCurrentPos(1) = my; Environment::tapDownCurrentPos(1) = my;
} }

View File

@ -36,8 +36,6 @@ namespace ZL {
void setup(); void setup();
void update(); void update();
bool shouldExit() const { return Environment::exitGameLoop; }
Renderer& renderer; Renderer& renderer;
TaskManager& taskManager; TaskManager& taskManager;
MainThreadHandler& mainThreadHandler; MainThreadHandler& mainThreadHandler;
@ -52,24 +50,14 @@ namespace ZL {
void drawShip(); void drawShip();
void drawBoxes(); void drawBoxes();
void drawBoxesLabels(); void drawBoxesLabels();
//void drawUI();
void drawRemoteShips(); void drawRemoteShips();
void drawRemoteShipsLabels(); void drawRemoteShipsLabels();
void fireProjectiles(); void fireProjectiles();
bool worldToScreen(const Vector3f& world, float& outX, float& outY, float& outDepth) const;
void handleDown(int mx, int my); void handleDown(int mx, int my);
void handleUp(int mx, int my); void handleUp(int mx, int my);
void handleMotion(int mx, int my); void handleMotion(int mx, int my);
//SDL_Window* window;
//SDL_GLContext glContext;
//int64_t newTickCount;
//int64_t lastTickCount;
std::vector<BoxCoords> boxCoordsArr; std::vector<BoxCoords> boxCoordsArr;
std::vector<VertexRenderStruct> boxRenderArr; std::vector<VertexRenderStruct> boxRenderArr;
@ -101,8 +89,6 @@ namespace ZL {
SparkEmitter explosionEmitter; SparkEmitter explosionEmitter;
PlanetObject planetObject; PlanetObject planetObject;
//MenuManager menuManager;
std::vector<std::unique_ptr<Projectile>> projectiles; std::vector<std::unique_ptr<Projectile>> projectiles;
std::shared_ptr<Texture> projectileTexture; std::shared_ptr<Texture> projectileTexture;
float projectileCooldownMs = 500.0f; float projectileCooldownMs = 500.0f;
@ -113,6 +99,7 @@ namespace ZL {
bool shipAlive = true; bool shipAlive = true;
bool gameOver = false; bool gameOver = false;
bool firePressed = false;
std::vector<bool> boxAlive; std::vector<bool> boxAlive;
float shipCollisionRadius = 15.0f; float shipCollisionRadius = 15.0f;
float boxCollisionRadius = 2.0f; float boxCollisionRadius = 2.0f;
@ -142,7 +129,6 @@ namespace ZL {
VertexRenderStruct hudTempMesh; VertexRenderStruct hudTempMesh;
// helpers // helpers
bool projectToNDC(const Vector3f& world, float& ndcX, float& ndcY, float& ndcZ, float& clipW) const;
void drawTargetHud(); // рисует рамку или стрелку void drawTargetHud(); // рисует рамку или стрелку
int pickTargetId() const; // выбирает цель (пока: ближайший живой удаленный игрок) int pickTargetId() const; // выбирает цель (пока: ближайший живой удаленный игрок)