Updated UI

This commit is contained in:
Vladislav Khorev 2026-02-22 18:23:27 +03:00
parent 46d7a2a25d
commit 1265d87bc5
10 changed files with 526 additions and 332 deletions

View File

@ -63,6 +63,10 @@ add_executable(space-game001
../src/network/WebSocketClientEmscripten.cpp ../src/network/WebSocketClientEmscripten.cpp
../src/render/TextRenderer.h ../src/render/TextRenderer.h
../src/render/TextRenderer.cpp ../src/render/TextRenderer.cpp
../src/MenuManager.h
../src/MenuManager.cpp
../src/Space.h
../src/Space.cpp
) )
# Установка проекта по умолчанию для Visual Studio # Установка проекта по умолчанию для Visual Studio
@ -84,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

@ -34,7 +34,9 @@ namespace ZL
const char* CONST_ZIP_FILE = ""; const char* CONST_ZIP_FILE = "";
#endif #endif
static bool g_exitBgAnimating = false; bool g_exitBgAnimating = false;
bool firePressed = false;
float x = 0; float x = 0;
@ -246,6 +248,7 @@ namespace ZL
, newTickCount(0) , newTickCount(0)
, lastTickCount(0) , lastTickCount(0)
, planetObject(renderer, taskManager, mainThreadHandler) , planetObject(renderer, taskManager, mainThreadHandler)
, menuManager(renderer)
{ {
projectiles.reserve(maxProjectiles); projectiles.reserve(maxProjectiles);
for (int i = 0; i < maxProjectiles; ++i) { for (int i = 0; i < maxProjectiles; ++i) {
@ -307,156 +310,29 @@ namespace ZL
explosionEmitter.setEmissionPoints(std::vector<Vector3f>()); explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
projectileEmitter.setEmissionPoints(std::vector<Vector3f>()); projectileEmitter.setEmissionPoints(std::vector<Vector3f>());
uiManager.loadFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE); menuManager.onRestartPressed = [this]() {
std::function<void()> loadGameplayUI; this->shipAlive = true;
loadGameplayUI = [this]() { this->gameOver = false;
uiManager.loadFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE); this->showExplosion = false;
this->explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
Environment::shipState.position = Vector3f{ 0, 0, 45000.f };
Environment::shipState.velocity = 0.0f;
Environment::shipState.rotation = Eigen::Matrix3f::Identity();
Environment::inverseShipMatrix = Eigen::Matrix3f::Identity();
Environment::zoom = DEFAULT_ZOOM;
Environment::tapDownHold = false;
auto velocityTv = uiManager.findTextView("velocityText"); std::cerr << "Game restarted\n";
if (velocityTv) {
velocityTv->rect.x = 10.0f;
velocityTv->rect.y = static_cast<float>(Environment::height) - velocityTv->rect.h - 10.0f;
}
else {
std::cerr << "Failed to find velocityText in UI" << std::endl;
}
uiManager.startAnimationOnNode("backgroundNode", "bgScroll");
static bool isExitButtonAnimating = false;
uiManager.setAnimationCallback("settingsButton", "buttonsExit", [this]() {
std::cerr << "Settings button animation finished -> переход в настройки" << std::endl;
if (uiManager.pushMenuFromFile("resources/config/settings.json", this->renderer, CONST_ZIP_FILE)) {
uiManager.setButtonCallback("Opt1", [this](const std::string& n) {
std::cerr << "Opt1 pressed: " << n << std::endl;
});
uiManager.setButtonCallback("Opt2", [this](const std::string& n) {
std::cerr << "Opt2 pressed: " << n << std::endl;
});
uiManager.setButtonCallback("backButton", [this](const std::string& n) {
uiManager.stopAllAnimations();
uiManager.popMenu();
});
}
else {
std::cerr << "Failed to open settings menu after animations" << std::endl;
}
});
uiManager.setAnimationCallback("exitButton", "bgScroll", []() {
std::cerr << "Exit button bgScroll animation finished" << std::endl;
g_exitBgAnimating = false;
});
uiManager.setButtonCallback("playButton", [this](const std::string& name) {
std::cerr << "Play button pressed: " << name << std::endl;
});
uiManager.setButtonCallback("settingsButton", [this](const std::string& name) {
std::cerr << "Settings button pressed: " << name << std::endl;
uiManager.startAnimationOnNode("playButton", "buttonsExit");
uiManager.startAnimationOnNode("settingsButton", "buttonsExit");
uiManager.startAnimationOnNode("exitButton", "buttonsExit");
});
uiManager.setButtonCallback("exitButton", [this](const std::string& name) {
std::cerr << "Exit button pressed: " << name << std::endl;
if (!g_exitBgAnimating) {
std::cerr << "start repeat anim bgScroll on exitButton" << std::endl;
g_exitBgAnimating = true;
uiManager.startAnimationOnNode("exitButton", "bgScroll");
}
else {
std::cerr << "stop repeat anim bgScroll on exitButton" << std::endl;
g_exitBgAnimating = false;
uiManager.stopAnimationOnNode("exitButton", "bgScroll");
auto exitButton = uiManager.findButton("exitButton");
if (exitButton) {
exitButton->animOffsetX = 0.0f;
exitButton->animOffsetY = 0.0f;
exitButton->animScaleX = 1.0f;
exitButton->animScaleY = 1.0f;
exitButton->buildMesh();
}
}
});
uiManager.setButtonCallback("shootButton", [this](const std::string& name) {
firePressed = true;
});
uiManager.setButtonCallback("shootButton2", [this](const std::string& name) {
firePressed = true;
});
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
int newVel = roundf(value * 10);
if (newVel > 2)
{
newVel = 2;
}
if (newVel != Environment::shipState.selectedVelocity) {
newShipVelocity = newVel;
}
});
}; };
uiManager.setButtonCallback("singleButton", [loadGameplayUI](const std::string& name) { menuManager.onVelocityChanged = [this](float newVelocity) {
std::cerr << "Single button pressed: " << name << " -> load gameplay UI\n"; newShipVelocity = newVelocity;
loadGameplayUI(); //Environment::shipState.velocity = newVelocity;
}); //std::cerr << "Ship velocity changed: " << newVelocity << "\n";
/*uiManager.setButtonCallback("multiplayerButton", [loadGameplayUI](const std::string& name) { };
std::cerr << "Multiplayer button pressed: " << name << " -> load gameplay UI\n"; menuManager.setupMenu();
loadGameplayUI();
});*/
uiManager.setButtonCallback("multiplayerButton", [this](const std::string& name) {
std::cerr << "Multiplayer button pressed → opening multiplayer menu\n";
uiManager.startAnimationOnNode("playButton", "buttonsExit");
uiManager.startAnimationOnNode("settingsButton", "buttonsExit");
uiManager.startAnimationOnNode("multiplayerButton", "buttonsExit");
uiManager.startAnimationOnNode("exitButton", "buttonsExit");
if (uiManager.pushMenuFromFile("resources/config/multiplayer_menu.json", this->renderer, CONST_ZIP_FILE)) {
// Callback для кнопки подключения
uiManager.setButtonCallback("connectButton", [this](const std::string& buttonName) {
std::string serverAddress = uiManager.getTextFieldValue("serverInputField");
if (serverAddress.empty()) {
uiManager.setText("statusText", "Please enter server address");
return;
}
uiManager.setText("statusText", "Connecting to " + serverAddress + "...");
std::cerr << "Connecting to server: " << serverAddress << std::endl;
// Здесь добавить вашу логику подключения к серверу
// connectToServer(serverAddress);
});
// Callback для кнопки назад
uiManager.setButtonCallback("backButton", [this](const std::string& buttonName) {
uiManager.popMenu();
});
// Callback для отслеживания ввода текста
uiManager.setTextFieldCallback("serverInputField",
[this](const std::string& fieldName, const std::string& newText) {
std::cout << "Server input field changed to: " << newText << std::endl;
});
std::cerr << "Multiplayer menu loaded successfully\n";
}
else {
std::cerr << "Failed to load multiplayer menu\n";
}
});
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n";
Environment::exitGameLoop = true;
});
cubemapTexture = std::make_shared<Texture>( cubemapTexture = std::make_shared<Texture>(
std::array<TextureDataStruct, 6>{ std::array<TextureDataStruct, 6>{
@ -756,7 +632,7 @@ namespace ZL
renderer.DisableVertexAttribArray(vPositionName); renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName); renderer.DisableVertexAttribArray(vTexCoordName);
glEnable(GL_BLEND); glEnable(GL_BLEND);
uiManager.draw(renderer); menuManager.uiManager.draw(renderer);
glDisable(GL_BLEND); glDisable(GL_BLEND);
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();
CheckGlError(); CheckGlError();
@ -1445,41 +1321,7 @@ namespace ZL
std::cerr << "GAME OVER: collision with planet (moved back and exploded)\n"; std::cerr << "GAME OVER: collision with planet (moved back and exploded)\n";
if (!uiGameOverShown) { menuManager.showGameOver();
if (uiManager.pushMenuFromFile("resources/config/game_over.json", this->renderer, CONST_ZIP_FILE)) {
uiManager.setButtonCallback("restartButton", [this](const std::string& name) {
std::string respawnMsg = "RESPAWN:" + std::to_string(networkClient->GetClientId());
networkClient->Send(respawnMsg);
this->shipAlive = true;
this->gameOver = false;
this->uiGameOverShown = false;
this->showExplosion = false;
this->explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
this->deadRemotePlayers.clear();
Environment::shipState.position = Vector3f{ 0, 0, 45000.f };
Environment::shipState.velocity = 0.0f;
Environment::shipState.rotation = Eigen::Matrix3f::Identity();
Environment::inverseShipMatrix = Eigen::Matrix3f::Identity();
Environment::zoom = DEFAULT_ZOOM;
Environment::tapDownHold = false;
uiManager.popMenu();
std::cerr << "Game restarted\n";
});
uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) {
Environment::exitGameLoop = true;
});
uiGameOverShown = true;
}
else {
std::cerr << "Failed to load game_over.json\n";
}
}
} }
else { else {
bool stoneCollided = false; bool stoneCollided = false;
@ -1554,34 +1396,7 @@ namespace ZL
planetObject.planetStones.statuses[collidedTriIdx] = ChunkStatus::Empty; planetObject.planetStones.statuses[collidedTriIdx] = ChunkStatus::Empty;
} }
if (!uiGameOverShown) { menuManager.showGameOver();
if (uiManager.pushMenuFromFile("resources/config/game_over.json", this->renderer, CONST_ZIP_FILE)) {
uiManager.setButtonCallback("restartButton", [this](const std::string& name) {
this->shipAlive = true;
this->gameOver = false;
this->uiGameOverShown = false;
this->showExplosion = false;
this->explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
Environment::shipState.position = Vector3f{ 0, 0, 45000.f };
Environment::shipState.velocity = 0.0f;
Environment::shipState.rotation = Eigen::Matrix3f::Identity();
Environment::inverseShipMatrix = Eigen::Matrix3f::Identity();
Environment::zoom = DEFAULT_ZOOM;
Environment::tapDownHold = false;
uiManager.popMenu();
std::cerr << "Game restarted\n";
});
uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) {
Environment::exitGameLoop = true;
});
uiGameOverShown = true;
}
else {
std::cerr << "Failed to load game_over.json\n";
}
}
} }
} }
} }
@ -1643,14 +1458,14 @@ namespace ZL
// update velocity text // update velocity text
if (shipAlive && !gameOver) { if (shipAlive && !gameOver) {
auto velocityTv = uiManager.findTextView("velocityText"); auto velocityTv = menuManager.uiManager.findTextView("velocityText");
if (velocityTv) { if (velocityTv) {
std::string velocityStr = "Velocity: " + std::to_string(static_cast<int>(Environment::shipState.velocity)); std::string velocityStr = "Velocity: " + std::to_string(static_cast<int>(Environment::shipState.velocity));
uiManager.setText("velocityText", velocityStr); menuManager.uiManager.setText("velocityText", velocityStr);
} }
} }
uiManager.update(static_cast<float>(delta)); menuManager.uiManager.update(static_cast<float>(delta));
lastTickCount = newTickCount; lastTickCount = newTickCount;
} }
} }
@ -1763,14 +1578,14 @@ namespace ZL
// Обработка ввода текста // Обработка ввода текста
if (event.type == SDL_KEYDOWN) { if (event.type == SDL_KEYDOWN) {
if (event.key.keysym.sym == SDLK_BACKSPACE) { if (event.key.keysym.sym == SDLK_BACKSPACE) {
uiManager.onKeyBackspace(); menuManager.uiManager.onKeyBackspace();
} }
} }
if (event.type == SDL_TEXTINPUT) { if (event.type == SDL_TEXTINPUT) {
// Пропускаем ctrl+c и другие команды // Пропускаем ctrl+c и другие команды
if ((event.text.text[0] & 0x80) == 0) { // ASCII символы if ((event.text.text[0] & 0x80) == 0) { // ASCII символы
uiManager.onKeyPress((unsigned char)event.text.text[0]); menuManager.uiManager.onKeyPress((unsigned char)event.text.text[0]);
} }
} }
@ -1903,41 +1718,7 @@ namespace ZL
shipAlive = false; shipAlive = false;
gameOver = true; gameOver = true;
Environment::shipState.velocity = 0.0f; Environment::shipState.velocity = 0.0f;
menuManager.showGameOver();
if (!uiGameOverShown) {
if (uiManager.pushMenuFromFile("resources/config/game_over.json",
this->renderer, CONST_ZIP_FILE)) {
uiManager.setButtonCallback("restartButton", [this](const std::string& name) {
std::cout << "Client: Restart button clicked, sending RESPAWN" << std::endl;
std::string respawnMsg = "RESPAWN:" + std::to_string(networkClient->GetClientId());
networkClient->Send(respawnMsg);
this->shipAlive = true;
this->gameOver = false;
this->uiGameOverShown = false;
this->showExplosion = false;
this->explosionEmitter.setEmissionPoints(std::vector<Vector3f>());
this->deadRemotePlayers.clear();
Environment::shipState.position = Vector3f{ 0, 0, 45000.f };
Environment::shipState.velocity = 0.0f;
Environment::shipState.rotation = Eigen::Matrix3f::Identity();
Environment::inverseShipMatrix = Eigen::Matrix3f::Identity();
Environment::zoom = DEFAULT_ZOOM;
Environment::tapDownHold = false;
uiManager.popMenu();
std::cerr << "Game restarted\n";
});
uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) {
Environment::exitGameLoop = true;
});
uiGameOverShown = true;
}
}
} }
else { else {
deadRemotePlayers.insert(d.targetId); deadRemotePlayers.insert(d.targetId);
@ -1998,22 +1779,22 @@ namespace ZL
int uiX = mx; int uiX = mx;
int uiY = Environment::height - my; int uiY = Environment::height - my;
uiManager.onMouseDown(uiX, uiY); menuManager.uiManager.onMouseDown(uiX, uiY);
bool uiHandled = false; bool uiHandled = false;
for (const auto& button : uiManager.findButton("") ? std::vector<std::shared_ptr<UiButton>>{} : std::vector<std::shared_ptr<UiButton>>{}) { for (const auto& button : menuManager.uiManager.findButton("") ? std::vector<std::shared_ptr<UiButton>>{} : std::vector<std::shared_ptr<UiButton>>{}) {
(void)button; (void)button;
} }
auto pressedSlider = [&]() -> std::shared_ptr<UiSlider> { auto pressedSlider = [&]() -> std::shared_ptr<UiSlider> {
for (const auto& slider : uiManager.findSlider("") ? std::vector<std::shared_ptr<UiSlider>>{} : std::vector<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; (void)slider;
} }
return nullptr; return nullptr;
}(); }();
if (!uiManager.isUiInteraction()) { if (!menuManager.uiManager.isUiInteraction()) {
Environment::tapDownHold = true; Environment::tapDownHold = true;
Environment::tapDownStartPos(0) = mx; Environment::tapDownStartPos(0) = mx;
@ -2029,9 +1810,9 @@ namespace ZL
int uiX = mx; int uiX = mx;
int uiY = Environment::height - my; int uiY = Environment::height - my;
uiManager.onMouseUp(uiX, uiY); menuManager.uiManager.onMouseUp(uiX, uiY);
if (!uiManager.isUiInteraction()) { if (!menuManager.uiManager.isUiInteraction()) {
Environment::tapDownHold = false; Environment::tapDownHold = false;
} }
} }
@ -2041,9 +1822,9 @@ namespace ZL
int uiX = mx; int uiX = mx;
int uiY = Environment::height - my; int uiY = Environment::height - my;
uiManager.onMouseMove(uiX, uiY); menuManager.uiManager.onMouseMove(uiX, uiY);
if (Environment::tapDownHold && !uiManager.isUiInteraction()) { if (Environment::tapDownHold && !menuManager.uiManager.isUiInteraction()) {
Environment::tapDownCurrentPos(0) = mx; Environment::tapDownCurrentPos(0) = mx;
Environment::tapDownCurrentPos(1) = my; Environment::tapDownCurrentPos(1) = my;
} }

View File

@ -14,19 +14,13 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <render/TextRenderer.h> #include <render/TextRenderer.h>
#include "MenuManager.h"
#include "Space.h"
#include <unordered_set> #include <unordered_set>
namespace ZL { namespace ZL {
struct BoxCoords
{
Vector3f pos;
Matrix3f m;
};
class Game { class Game {
public: public:
Game(); Game();
@ -43,6 +37,8 @@ namespace ZL {
MainThreadHandler mainThreadHandler; MainThreadHandler mainThreadHandler;
std::unique_ptr<INetworkClient> networkClient; std::unique_ptr<INetworkClient> networkClient;
private: private:
int64_t getSyncTimeMs(); int64_t getSyncTimeMs();
void processTickCount(); void processTickCount();
@ -100,7 +96,8 @@ namespace ZL {
SparkEmitter projectileEmitter; SparkEmitter projectileEmitter;
SparkEmitter explosionEmitter; SparkEmitter explosionEmitter;
PlanetObject planetObject; PlanetObject planetObject;
UiManager uiManager;
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;
@ -115,12 +112,10 @@ namespace ZL {
std::vector<bool> boxAlive; std::vector<bool> boxAlive;
float shipCollisionRadius = 15.0f; float shipCollisionRadius = 15.0f;
float boxCollisionRadius = 2.0f; float boxCollisionRadius = 2.0f;
bool uiGameOverShown = false; //bool uiGameOverShown = false;
bool showExplosion = false; bool showExplosion = false;
uint64_t lastExplosionTime = 0; uint64_t lastExplosionTime = 0;
const uint64_t explosionDurationMs = 500; const uint64_t explosionDurationMs = 500;
bool firePressed = false;
bool serverBoxesApplied = false; bool serverBoxesApplied = false;

196
src/MenuManager.cpp Normal file
View File

@ -0,0 +1,196 @@
#include "MenuManager.h"
namespace ZL {
MenuManager::MenuManager(Renderer& iRenderer) :
renderer(iRenderer)
{
}
void MenuManager::setupMenu()
{
uiManager.loadFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE);
uiSavedRoot = loadUiFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE);
settingsSavedRoot = loadUiFromFile("resources/config/settings.json", renderer, CONST_ZIP_FILE);
multiplayerSavedRoot = loadUiFromFile("resources/config/multiplayer_menu.json", renderer, CONST_ZIP_FILE);
gameOverSavedRoot = loadUiFromFile("resources/config/game_over.json", renderer, CONST_ZIP_FILE);
std::function<void()> loadGameplayUI;
loadGameplayUI = [this]() {
uiManager.replaceRoot(uiSavedRoot);
auto velocityTv = uiManager.findTextView("velocityText");
if (velocityTv) {
velocityTv->rect.x = 10.0f;
velocityTv->rect.y = static_cast<float>(Environment::height) - velocityTv->rect.h - 10.0f;
}
else {
std::cerr << "Failed to find velocityText in UI" << std::endl;
}
uiManager.startAnimationOnNode("backgroundNode", "bgScroll");
static bool isExitButtonAnimating = false;
uiManager.setAnimationCallback("settingsButton", "buttonsExit", [this]() {
std::cerr << "Settings button animation finished -> переход в настройки" << std::endl;
if (uiManager.pushMenuFromSavedRoot(settingsSavedRoot)) {
uiManager.setButtonCallback("Opt1", [this](const std::string& n) {
std::cerr << "Opt1 pressed: " << n << std::endl;
});
uiManager.setButtonCallback("Opt2", [this](const std::string& n) {
std::cerr << "Opt2 pressed: " << n << std::endl;
});
uiManager.setButtonCallback("backButton", [this](const std::string& n) {
uiManager.stopAllAnimations();
uiManager.popMenu();
});
}
else {
std::cerr << "Failed to open settings menu after animations" << std::endl;
}
});
uiManager.setAnimationCallback("exitButton", "bgScroll", []() {
std::cerr << "Exit button bgScroll animation finished" << std::endl;
g_exitBgAnimating = false;
});
uiManager.setButtonCallback("playButton", [this](const std::string& name) {
std::cerr << "Play button pressed: " << name << std::endl;
});
uiManager.setButtonCallback("settingsButton", [this](const std::string& name) {
std::cerr << "Settings button pressed: " << name << std::endl;
uiManager.startAnimationOnNode("playButton", "buttonsExit");
uiManager.startAnimationOnNode("settingsButton", "buttonsExit");
uiManager.startAnimationOnNode("exitButton", "buttonsExit");
});
uiManager.setButtonCallback("exitButton", [this](const std::string& name) {
std::cerr << "Exit button pressed: " << name << std::endl;
if (!g_exitBgAnimating) {
std::cerr << "start repeat anim bgScroll on exitButton" << std::endl;
g_exitBgAnimating = true;
uiManager.startAnimationOnNode("exitButton", "bgScroll");
}
else {
std::cerr << "stop repeat anim bgScroll on exitButton" << std::endl;
g_exitBgAnimating = false;
uiManager.stopAnimationOnNode("exitButton", "bgScroll");
auto exitButton = uiManager.findButton("exitButton");
if (exitButton) {
exitButton->animOffsetX = 0.0f;
exitButton->animOffsetY = 0.0f;
exitButton->animScaleX = 1.0f;
exitButton->animScaleY = 1.0f;
exitButton->buildMesh();
}
}
});
uiManager.setButtonCallback("shootButton", [this](const std::string& name) {
firePressed = true;
});
uiManager.setButtonCallback("shootButton2", [this](const std::string& name) {
firePressed = true;
});
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
int newVel = roundf(value * 10);
/*if (newVel > 2)
{
newVel = 2;
}*/
if (newVel != Environment::shipState.selectedVelocity) {
onVelocityChanged(newVel);
}
});
};
uiManager.setButtonCallback("singleButton", [loadGameplayUI](const std::string& name) {
std::cerr << "Single button pressed: " << name << " -> load gameplay UI\n";
loadGameplayUI();
});
/*uiManager.setButtonCallback("multiplayerButton", [loadGameplayUI](const std::string& name) {
std::cerr << "Multiplayer button pressed: " << name << " -> load gameplay UI\n";
loadGameplayUI();
});*/
uiManager.setButtonCallback("multiplayerButton", [this](const std::string& name) {
std::cerr << "Multiplayer button pressed → opening multiplayer menu\n";
uiManager.startAnimationOnNode("playButton", "buttonsExit");
uiManager.startAnimationOnNode("settingsButton", "buttonsExit");
uiManager.startAnimationOnNode("multiplayerButton", "buttonsExit");
uiManager.startAnimationOnNode("exitButton", "buttonsExit");
if (uiManager.pushMenuFromSavedRoot(multiplayerSavedRoot)) {
// Callback для кнопки подключения
uiManager.setButtonCallback("connectButton", [this](const std::string& buttonName) {
std::string serverAddress = uiManager.getTextFieldValue("serverInputField");
if (serverAddress.empty()) {
uiManager.setText("statusText", "Please enter server address");
return;
}
uiManager.setText("statusText", "Connecting to " + serverAddress + "...");
std::cerr << "Connecting to server: " << serverAddress << std::endl;
// Здесь добавить вашу логику подключения к серверу
// connectToServer(serverAddress);
});
// Callback для кнопки назад
uiManager.setButtonCallback("backButton", [this](const std::string& buttonName) {
uiManager.popMenu();
});
// Callback для отслеживания ввода текста
uiManager.setTextFieldCallback("serverInputField",
[this](const std::string& fieldName, const std::string& newText) {
std::cout << "Server input field changed to: " << newText << std::endl;
});
std::cerr << "Multiplayer menu loaded successfully\n";
}
else {
std::cerr << "Failed to load multiplayer menu\n";
}
});
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n";
Environment::exitGameLoop = true;
});
}
void MenuManager::showGameOver()
{
if (!uiGameOverShown) {
if (uiManager.pushMenuFromSavedRoot(gameOverSavedRoot)) {
uiManager.setButtonCallback("restartButton", [this](const std::string& name) {
uiGameOverShown = false;
uiManager.popMenu();
onRestartPressed();
});
uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) {
Environment::exitGameLoop = true;
});
uiGameOverShown = true;
}
else {
std::cerr << "Failed to load game_over.json\n";
}
}
}
}

38
src/MenuManager.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include "render/Renderer.h"
#include "Environment.h"
#include "render/TextureManager.h"
#include "UiManager.h"
namespace ZL {
extern const char* CONST_ZIP_FILE;
extern bool g_exitBgAnimating;
extern bool firePressed;
class MenuManager
{
protected:
Renderer& renderer;
bool uiGameOverShown = false;
std::shared_ptr<UiNode> uiSavedRoot;
std::shared_ptr<UiNode> gameOverSavedRoot;
std::shared_ptr<UiNode> settingsSavedRoot;
std::shared_ptr<UiNode> multiplayerSavedRoot;
public:
UiManager uiManager;
MenuManager(Renderer& iRenderer);
void setupMenu();
void showGameOver();
std::function<void()> onRestartPressed;
std::function<void(float)> onVelocityChanged;
};
};

0
src/Space.cpp Normal file
View File

152
src/Space.h Normal file
View File

@ -0,0 +1,152 @@
#pragma once
#include "render/Renderer.h"
#include "Environment.h"
#include "render/TextureManager.h"
#include "SparkEmitter.h"
#include "planet/PlanetObject.h"
#include "UiManager.h"
#include "Projectile.h"
#include "utils/TaskManager.h"
#include "network/NetworkInterface.h"
#include <queue>
#include <vector>
#include <string>
#include <memory>
#include <render/TextRenderer.h>
#include "MenuManager.h"
#include <unordered_set>
namespace ZL {
struct BoxCoords
{
Vector3f pos;
Matrix3f m;
};
/*
class Space {
public:
Space(Renderer& iRenderer, TaskManager& iTaskManager, MainThreadHandler& iMainThreadHandler);
~Space();
void setup();
void update();
void render();
bool shouldExit() const { return Environment::exitGameLoop; }
Renderer& renderer;
TaskManager& taskManager;
MainThreadHandler& mainThreadHandler;
private:
int64_t getSyncTimeMs();
void processTickCount();
void drawScene();
void drawCubemap(float skyPercent);
void drawShip();
void drawBoxes();
void drawBoxesLabels();
void drawUI();
void drawRemoteShips();
void drawRemoteShipsLabels();
void fireProjectiles();
bool worldToScreen(const Vector3f& world, float& outX, float& outY, float& outDepth) const;
void handleDown(int mx, int my);
void handleUp(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<VertexRenderStruct> boxRenderArr;
std::vector<std::string> boxLabels;
std::unique_ptr<TextRenderer> textRenderer;
//std::unordered_map<int, ClientStateInterval> latestRemotePlayers;
std::unordered_map<int, ClientState> remotePlayerStates;
float newShipVelocity = 0;
static const size_t CONST_TIMER_INTERVAL = 10;
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
std::shared_ptr<Texture> sparkTexture;
std::shared_ptr<Texture> spaceshipTexture;
std::shared_ptr<Texture> cubemapTexture;
VertexDataStruct spaceshipBase;
VertexRenderStruct spaceship;
VertexRenderStruct cubemap;
std::shared_ptr<Texture> boxTexture;
VertexDataStruct boxBase;
SparkEmitter sparkEmitter;
SparkEmitter projectileEmitter;
SparkEmitter explosionEmitter;
PlanetObject planetObject;
MenuManager menuManager;
std::vector<std::unique_ptr<Projectile>> projectiles;
std::shared_ptr<Texture> projectileTexture;
float projectileCooldownMs = 500.0f;
int64_t lastProjectileFireTime = 0;
int maxProjectiles = 32;
std::vector<Vector3f> shipLocalEmissionPoints;
bool shipAlive = true;
bool gameOver = false;
std::vector<bool> boxAlive;
float shipCollisionRadius = 15.0f;
float boxCollisionRadius = 2.0f;
//bool uiGameOverShown = false;
bool showExplosion = false;
uint64_t lastExplosionTime = 0;
const uint64_t explosionDurationMs = 500;
bool serverBoxesApplied = false;
static constexpr float MAX_DIST_SQ = 10000.f * 10000.f;
static constexpr float FADE_START = 6000.f;
static constexpr float FADE_RANGE = 4000.f;
static constexpr float BASE_SCALE = 140.f;
static constexpr float PERSPECTIVE_K = 0.05f; // Tune
static constexpr float MIN_SCALE = 0.4f;
static constexpr float MAX_SCALE = 0.8f;
static constexpr float CLOSE_DIST = 600.0f;
std::unordered_set<int> deadRemotePlayers;
// --- Target HUD (brackets + offscreen arrow) ---
int trackedTargetId = -1;
bool targetWasVisible = false;
float targetAcquireAnim = 0.0f; // 0..1 схлопывание (0 = далеко, 1 = на месте)
// временный меш для HUD (будем перезаливать VBO маленькими порциями)
VertexRenderStruct hudTempMesh;
// helpers
bool projectToNDC(const Vector3f& world, float& ndcX, float& ndcY, float& ndcZ, float& clipW) const;
void drawTargetHud(); // рисует рамку или стрелку
int pickTargetId() const; // выбирает цель (пока: ближайший живой удаленный игрок)
};*/
} // namespace ZL

View File

@ -182,60 +182,7 @@ namespace ZL {
} }
} }
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) { std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
std::string content;
try {
if (zipFile.empty()) {
content = readTextFile(path);
}
else {
auto buf = readFileFromZIP(path, zipFile);
if (buf.empty()) {
std::cerr << "UiManager: failed to read " << path << " from zip " << zipFile << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
content.assign(buf.begin(), buf.end());
}
}
catch (const std::exception& e) {
std::cerr << "UiManager: failed to open " << path << " : " << e.what() << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
json j;
try {
j = json::parse(content);
}
catch (const std::exception& e) {
std::cerr << "UiManager: json parse error: " << e.what() << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
if (!j.contains("root") || !j["root"].is_object()) {
std::cerr << "UiManager: root node missing or invalid" << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
root = parseNode(j["root"], renderer, zipFile);
layoutNode(root);
buttons.clear();
sliders.clear();
textViews.clear();
textFields.clear();
collectButtonsAndSliders(root);
nodeActiveAnims.clear();
for (auto& b : buttons) {
b->buildMesh();
}
for (auto& s : sliders) {
s->buildTrackMesh();
s->buildKnobMesh();
}
}
std::shared_ptr<UiNode> UiManager::parseNode(const json& j, Renderer& renderer, const std::string& zipFile) {
auto node = std::make_shared<UiNode>(); auto node = std::make_shared<UiNode>();
if (j.contains("type") && j["type"].is_string()) node->type = j["type"].get<std::string>(); if (j.contains("type") && j["type"].is_string()) node->type = j["type"].get<std::string>();
if (j.contains("name") && j["name"].is_string()) node->name = j["name"].get<std::string>(); if (j.contains("name") && j["name"].is_string()) node->name = j["name"].get<std::string>();
@ -416,6 +363,76 @@ namespace ZL {
return node; return node;
} }
std::shared_ptr<UiNode> loadUiFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile)
{
std::shared_ptr<UiNode> root;
std::string content;
try {
if (zipFile.empty()) {
content = readTextFile(path);
}
else {
auto buf = readFileFromZIP(path, zipFile);
if (buf.empty()) {
std::cerr << "UiManager: failed to read " << path << " from zip " << zipFile << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
content.assign(buf.begin(), buf.end());
}
}
catch (const std::exception& e) {
std::cerr << "UiManager: failed to open " << path << " : " << e.what() << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
json j;
try {
j = json::parse(content);
}
catch (const std::exception& e) {
std::cerr << "UiManager: json parse error: " << e.what() << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
if (!j.contains("root") || !j["root"].is_object()) {
std::cerr << "UiManager: root node missing or invalid" << std::endl;
throw std::runtime_error("Failed to load UI file: " + path);
}
root = parseNode(j["root"], renderer, zipFile);
return root;
}
void UiManager::replaceRoot(std::shared_ptr<UiNode> newRoot) {
root = newRoot;
layoutNode(root);
buttons.clear();
sliders.clear();
textViews.clear();
textFields.clear();
collectButtonsAndSliders(root);
nodeActiveAnims.clear();
for (auto& b : buttons) {
b->buildMesh();
}
for (auto& s : sliders) {
s->buildTrackMesh();
s->buildKnobMesh();
}
}
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
std::shared_ptr<UiNode> newRoot = loadUiFromFile(path, renderer, zipFile);
replaceRoot(newRoot);
}
void UiManager::layoutNode(const std::shared_ptr<UiNode>& node) { void UiManager::layoutNode(const std::shared_ptr<UiNode>& node) {
for (auto& child : node->children) { for (auto& child : node->children) {
child->rect.x += node->rect.x; child->rect.x += node->rect.x;
@ -553,7 +570,8 @@ namespace ZL {
return tf->text; return tf->text;
} }
bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) { bool UiManager::pushMenuFromSavedRoot(std::shared_ptr<UiNode> newRoot)
{
MenuState prev; MenuState prev;
prev.root = root; prev.root = root;
prev.buttons = buttons; prev.buttons = buttons;
@ -579,17 +597,22 @@ namespace ZL {
} }
} }
loadFromFile(path, renderer, zipFile); replaceRoot(newRoot);
menuStack.push_back(std::move(prev)); menuStack.push_back(std::move(prev));
return true; return true;
} }
catch (const std::exception& e) { catch (const std::exception& e) {
std::cerr << "UiManager: pushMenuFromFile failed to load " << path << " : " << e.what() << std::endl; std::cerr << "UiManager: pushMenuFromFile failed to load from root : " << e.what() << std::endl;
animCallbacks = prev.animCallbacks; animCallbacks = prev.animCallbacks;
return false; return false;
} }
} }
bool UiManager::pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
auto newRoot = loadUiFromFile(path, renderer, zipFile);
return pushMenuFromSavedRoot(newRoot);
}
bool UiManager::popMenu() { bool UiManager::popMenu() {
if (menuStack.empty()) { if (menuStack.empty()) {
std::cerr << "UiManager: popMenu called but menu stack is empty" << std::endl; std::cerr << "UiManager: popMenu called but menu stack is empty" << std::endl;

View File

@ -136,10 +136,15 @@ namespace ZL {
std::map<std::string, AnimSequence> animations; std::map<std::string, AnimSequence> animations;
}; };
std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile);
std::shared_ptr<UiNode> loadUiFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
class UiManager { class UiManager {
public: public:
UiManager() = default; UiManager() = default;
void replaceRoot(std::shared_ptr<UiNode> newRoot);
void loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = ""); void loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
void draw(Renderer& renderer); void draw(Renderer& renderer);
@ -186,6 +191,7 @@ namespace ZL {
std::string getTextFieldValue(const std::string& name); std::string getTextFieldValue(const std::string& name);
bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = ""); bool pushMenuFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile = "");
bool pushMenuFromSavedRoot(std::shared_ptr<UiNode> newRoot);
bool popMenu(); bool popMenu();
void clearMenuStack(); void clearMenuStack();
@ -196,7 +202,6 @@ namespace ZL {
bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb); bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb);
private: private:
std::shared_ptr<UiNode> parseNode(const json& j, Renderer& renderer, const std::string& zipFile);
void layoutNode(const std::shared_ptr<UiNode>& node); void layoutNode(const std::shared_ptr<UiNode>& node);
void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node); void collectButtonsAndSliders(const std::shared_ptr<UiNode>& node);

View File

@ -239,7 +239,7 @@ namespace ZL {
for (size_t bi = 0; bi < serverBoxes.size(); ++bi) { for (size_t bi = 0; bi < serverBoxes.size(); ++bi) {
if (serverBoxes[bi].destroyed) continue; if (serverBoxes[bi].destroyed) continue;
Eigen::Vector3f boxWorld = serverBoxes[bi].position + Eigen::Vector3f(0.0f, 6.0f, 45000.0f); Eigen::Vector3f boxWorld = serverBoxes[bi].position + Eigen::Vector3f(0.0f, 0.0f, 45000.0f);
for (size_t pi = 0; pi < projectiles.size(); ++pi) { for (size_t pi = 0; pi < projectiles.size(); ++pi) {
const auto& pr = projectiles[pi]; const auto& pr = projectiles[pi];
@ -387,8 +387,8 @@ namespace ZL {
} }
const std::vector<Eigen::Vector3f> localOffsets = { const std::vector<Eigen::Vector3f> localOffsets = {
Eigen::Vector3f(-1.5f, 0.9f, 5.0f), Eigen::Vector3f(-1.5f, 0.9f - 6.f, 5.0f),
Eigen::Vector3f(1.5f, 0.9f, 5.0f) Eigen::Vector3f(1.5f, 0.9f - 6.f, 5.0f)
}; };
uint64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>( uint64_t now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(