Make menu to state machine
This commit is contained in:
parent
ac551122d9
commit
4af20c65e6
22
resources/config/connecting.json
Normal file
22
resources/config/connecting.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "connecting",
|
||||||
|
"x" : 0,
|
||||||
|
"y" : 0,
|
||||||
|
"width": 488,
|
||||||
|
"height": 154,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"vertical_gravity": "center",
|
||||||
|
"texture": "resources/connecting.png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
52
resources/config/connection_failed.json
Normal file
52
resources/config/connection_failed.json
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "connectionFailed",
|
||||||
|
"x" : 0,
|
||||||
|
"y" : 0,
|
||||||
|
"width": 488,
|
||||||
|
"height": 308,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"vertical_gravity": "center",
|
||||||
|
"texture": "resources/connection_failed.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "connectionFailedReconnectButton",
|
||||||
|
"width": 382,
|
||||||
|
"height": 56,
|
||||||
|
"x" : 0,
|
||||||
|
"y" : -20,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"vertical_gravity": "center",
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/game_over/Filledbuttons.png",
|
||||||
|
"hover": "resources/game_over/Variant5.png",
|
||||||
|
"pressed": "resources/game_over/Variant6.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "connectionFailedGoBack",
|
||||||
|
"width": 382,
|
||||||
|
"height": 56,
|
||||||
|
"x" : 0,
|
||||||
|
"y" : -86,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"vertical_gravity": "center",
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/game_over/Secondarybutton.png",
|
||||||
|
"hover": "resources/game_over/Secondarybutton.png",
|
||||||
|
"pressed": "resources/game_over/Secondarybutton.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
52
resources/config/connection_lost.json
Normal file
52
resources/config/connection_lost.json
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "FrameLayout",
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
"width": "match_parent",
|
||||||
|
"height": "match_parent",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "StaticImage",
|
||||||
|
"name": "connectionLost",
|
||||||
|
"x" : 0,
|
||||||
|
"y" : 0,
|
||||||
|
"width": 488,
|
||||||
|
"height": 308,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"vertical_gravity": "center",
|
||||||
|
"texture": "resources/connection_lost.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "reconnectButton",
|
||||||
|
"width": 382,
|
||||||
|
"height": 56,
|
||||||
|
"x" : 0,
|
||||||
|
"y" : -20,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"vertical_gravity": "center",
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/game_over/Filledbuttons.png",
|
||||||
|
"hover": "resources/game_over/Variant5.png",
|
||||||
|
"pressed": "resources/game_over/Variant6.png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Button",
|
||||||
|
"name": "exitServerButton",
|
||||||
|
"width": 382,
|
||||||
|
"height": 56,
|
||||||
|
"x" : 0,
|
||||||
|
"y" : -86,
|
||||||
|
"horizontal_gravity": "center",
|
||||||
|
"vertical_gravity": "center",
|
||||||
|
"textures": {
|
||||||
|
"normal": "resources/game_over/Secondarybutton.png",
|
||||||
|
"hover": "resources/game_over/Secondarybutton.png",
|
||||||
|
"pressed": "resources/game_over/Secondarybutton.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,26 +11,18 @@
|
|||||||
"height": "match_parent",
|
"height": "match_parent",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "StaticImage",
|
||||||
"name": "titleBtn",
|
"name": "titleBtn",
|
||||||
"width": 254,
|
"width": 254,
|
||||||
"height": 35,
|
"height": 35,
|
||||||
"textures": {
|
"texture": "resources/main_menu/title.png"
|
||||||
"normal": "resources/main_menu/title.png",
|
|
||||||
"hover": "resources/main_menu/title.png",
|
|
||||||
"pressed": "resources/main_menu/title.png"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "StaticImage",
|
||||||
"name": "underlineBtn",
|
"name": "underlineBtn",
|
||||||
"width": 168,
|
"width": 168,
|
||||||
"height": 44,
|
"height": 44,
|
||||||
"textures": {
|
"texture": "resources/main_menu/line.png"
|
||||||
"normal": "resources/main_menu/line.png",
|
|
||||||
"hover": "resources/main_menu/line.png",
|
|
||||||
"pressed": "resources/main_menu/line.png"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "Button",
|
||||||
@ -66,15 +58,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Button",
|
"type": "StaticImage",
|
||||||
"name": "versionLabel",
|
"name": "versionLabel",
|
||||||
"width": 81,
|
"width": 81,
|
||||||
"height": 9,
|
"height": 9,
|
||||||
"textures": {
|
"texture": "resources/main_menu/version.png"
|
||||||
"normal": "resources/main_menu/version.png",
|
|
||||||
"hover": "resources/main_menu/version.png",
|
|
||||||
"pressed": "resources/main_menu/version.png"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
resources/connecting.png
(Stored with Git LFS)
Normal file
BIN
resources/connecting.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/connection_failed.png
(Stored with Git LFS)
Normal file
BIN
resources/connection_failed.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/connection_lost.png
(Stored with Git LFS)
Normal file
BIN
resources/connection_lost.png
(Stored with Git LFS)
Normal file
Binary file not shown.
17
src/Game.cpp
17
src/Game.cpp
@ -150,7 +150,6 @@ namespace ZL
|
|||||||
Environment::shipState.nickname = nickname;
|
Environment::shipState.nickname = nickname;
|
||||||
Environment::shipState.shipType = shipType;
|
Environment::shipState.shipType = shipType;
|
||||||
|
|
||||||
|
|
||||||
if (Environment::shipState.shipType == 1)
|
if (Environment::shipState.shipType == 1)
|
||||||
{
|
{
|
||||||
menuManager.uiManager.findButton("shootButton")->state = ButtonState::Disabled;
|
menuManager.uiManager.findButton("shootButton")->state = ButtonState::Disabled;
|
||||||
@ -171,14 +170,12 @@ namespace ZL
|
|||||||
networkClient->Connect("", 0);
|
networkClient->Connect("", 0);
|
||||||
|
|
||||||
lastTickCount = 0;
|
lastTickCount = 0;
|
||||||
spaceGameStarted = 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
menuManager.onMultiplayerPressed = [this](const std::string& nickname, int shipType) {
|
menuManager.onMultiplayerPressed = [this](const std::string& nickname, int shipType) {
|
||||||
Environment::shipState.nickname = nickname;
|
Environment::shipState.nickname = nickname;
|
||||||
Environment::shipState.shipType = shipType;
|
Environment::shipState.shipType = shipType;
|
||||||
|
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
#ifdef EMSCRIPTEN
|
||||||
networkClient = std::make_unique<WebSocketClientEmscripten>();
|
networkClient = std::make_unique<WebSocketClientEmscripten>();
|
||||||
networkClient->Connect("localhost", 8081);
|
networkClient->Connect("localhost", 8081);
|
||||||
@ -195,12 +192,10 @@ namespace ZL
|
|||||||
|
|
||||||
space.boxCoordsArr.clear();
|
space.boxCoordsArr.clear();
|
||||||
space.boxRenderArr.clear();
|
space.boxRenderArr.clear();
|
||||||
//space.boxLabels.clear();
|
|
||||||
space.boxAlive.clear();
|
space.boxAlive.clear();
|
||||||
space.serverBoxesApplied = false;
|
space.serverBoxesApplied = false;
|
||||||
|
|
||||||
lastTickCount = 0;
|
lastTickCount = 0;
|
||||||
spaceGameStarted = 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -252,7 +247,7 @@ namespace ZL
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
if (spaceGameStarted) {
|
if (menuManager.shouldRenderSpace()) {
|
||||||
space.drawScene();
|
space.drawScene();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -336,7 +331,7 @@ namespace ZL
|
|||||||
//throw std::runtime_error("Synchronization is lost");
|
//throw std::runtime_error("Synchronization is lost");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spaceGameStarted) {
|
if (menuManager.shouldRenderSpace()) {
|
||||||
space.processTickCount(newTickCount, delta);
|
space.processTickCount(newTickCount, delta);
|
||||||
}
|
}
|
||||||
menuManager.uiManager.update(static_cast<float>(delta));
|
menuManager.uiManager.update(static_cast<float>(delta));
|
||||||
@ -503,7 +498,7 @@ namespace ZL
|
|||||||
}
|
}
|
||||||
mainThreadHandler.processMainThreadTasks();
|
mainThreadHandler.processMainThreadTasks();
|
||||||
|
|
||||||
if (spaceGameStarted) {
|
if (menuManager.shouldRenderSpace()) {
|
||||||
space.update();
|
space.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -529,7 +524,7 @@ namespace ZL
|
|||||||
}();
|
}();
|
||||||
|
|
||||||
if (!menuManager.uiManager.isUiInteraction()) {
|
if (!menuManager.uiManager.isUiInteraction()) {
|
||||||
if (spaceGameStarted) {
|
if (menuManager.shouldRenderSpace()) {
|
||||||
space.handleDown(mx, my);
|
space.handleDown(mx, my);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,7 +539,7 @@ namespace ZL
|
|||||||
menuManager.uiManager.onMouseUp(uiX, uiY);
|
menuManager.uiManager.onMouseUp(uiX, uiY);
|
||||||
|
|
||||||
if (!menuManager.uiManager.isUiInteraction()) {
|
if (!menuManager.uiManager.isUiInteraction()) {
|
||||||
if (spaceGameStarted) {
|
if (menuManager.shouldRenderSpace()) {
|
||||||
space.handleUp(mx, my);
|
space.handleUp(mx, my);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -559,7 +554,7 @@ namespace ZL
|
|||||||
menuManager.uiManager.onMouseMove(uiX, uiY);
|
menuManager.uiManager.onMouseMove(uiX, uiY);
|
||||||
|
|
||||||
if (!menuManager.uiManager.isUiInteraction()) {
|
if (!menuManager.uiManager.isUiInteraction()) {
|
||||||
if (spaceGameStarted) {
|
if (menuManager.shouldRenderSpace()) {
|
||||||
space.handleMotion(mx, my);
|
space.handleMotion(mx, my);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,8 +71,6 @@ namespace ZL {
|
|||||||
|
|
||||||
MenuManager menuManager;
|
MenuManager menuManager;
|
||||||
Space space;
|
Space space;
|
||||||
|
|
||||||
int spaceGameStarted = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#include "MenuManager.h"
|
#include "MenuManager.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
@ -10,294 +10,177 @@ namespace ZL {
|
|||||||
|
|
||||||
void MenuManager::setupMenu()
|
void MenuManager::setupMenu()
|
||||||
{
|
{
|
||||||
|
mainMenuRoot = loadUiFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE);
|
||||||
|
shipSelectionRoot = loadUiFromFile("resources/config/ship_selection_menu.json", renderer, CONST_ZIP_FILE);
|
||||||
|
gameplayRoot = loadUiFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE);
|
||||||
|
gameOverRoot = loadUiFromFile("resources/config/game_over.json", renderer, CONST_ZIP_FILE);
|
||||||
|
connectionLostRoot = loadUiFromFile("resources/config/connection_lost.json", renderer, CONST_ZIP_FILE);
|
||||||
|
|
||||||
uiManager.loadFromFile("resources/config/main_menu.json", renderer, CONST_ZIP_FILE);
|
enterMainMenu();
|
||||||
|
}
|
||||||
|
|
||||||
uiSavedRoot = loadUiFromFile("resources/config/ui.json", renderer, CONST_ZIP_FILE);
|
bool MenuManager::shouldRenderSpace() const
|
||||||
|
{
|
||||||
|
return state == GameState::Gameplay
|
||||||
|
|| state == GameState::GameOver
|
||||||
|
|| state == GameState::ConnectionLost;
|
||||||
|
}
|
||||||
|
|
||||||
settingsSavedRoot = loadUiFromFile("resources/config/settings.json", renderer, CONST_ZIP_FILE);
|
// ── State: MainMenu ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
multiplayerSavedRoot = loadUiFromFile("resources/config/multiplayer_menu.json", renderer, CONST_ZIP_FILE);
|
void MenuManager::enterMainMenu()
|
||||||
|
{
|
||||||
|
state = GameState::MainMenu;
|
||||||
|
uiManager.replaceRoot(mainMenuRoot);
|
||||||
|
|
||||||
gameOverSavedRoot = loadUiFromFile("resources/config/game_over.json", renderer, CONST_ZIP_FILE);
|
uiManager.setButtonCallback("singleButton", [this](const std::string&) {
|
||||||
|
enterShipSelectionSingle();
|
||||||
|
});
|
||||||
|
|
||||||
auto shipSelectionRoot = loadUiFromFile("resources/config/ship_selection_menu.json", renderer, CONST_ZIP_FILE);
|
uiManager.setButtonCallback("multiplayerButton", [this](const std::string&) {
|
||||||
std::function<void()> loadGameplayUI;
|
enterShipSelectionMulti();
|
||||||
loadGameplayUI = [this]() {
|
});
|
||||||
uiManager.replaceRoot(uiSavedRoot);
|
}
|
||||||
|
|
||||||
|
// ── State: ShipSelectionSingle ───────────────────────────────────────────
|
||||||
|
|
||||||
|
void MenuManager::enterShipSelectionSingle()
|
||||||
|
{
|
||||||
|
state = GameState::ShipSelectionSingle;
|
||||||
|
uiManager.replaceRoot(shipSelectionRoot);
|
||||||
|
|
||||||
|
uiManager.setButtonCallback("spaceshipButton", [this](const std::string&) {
|
||||||
|
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||||
|
if (nick.empty()) nick = "Player";
|
||||||
|
enterGameplay();
|
||||||
|
if (onSingleplayerPressed) onSingleplayerPressed(nick, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
uiManager.setButtonCallback("cargoshipButton", [this](const std::string&) {
|
||||||
|
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||||
|
if (nick.empty()) nick = "Player";
|
||||||
|
enterGameplay();
|
||||||
|
if (onSingleplayerPressed) onSingleplayerPressed(nick, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
uiManager.setButtonCallback("backButton", [this](const std::string&) {
|
||||||
|
enterMainMenu();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── State: ShipSelectionMulti ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
void MenuManager::enterShipSelectionMulti()
|
||||||
|
{
|
||||||
|
state = GameState::ShipSelectionMulti;
|
||||||
|
uiManager.replaceRoot(shipSelectionRoot);
|
||||||
|
|
||||||
|
uiManager.setButtonCallback("spaceshipButton", [this](const std::string&) {
|
||||||
|
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||||
|
if (nick.empty()) nick = "Player";
|
||||||
|
enterGameplay();
|
||||||
|
if (onMultiplayerPressed) onMultiplayerPressed(nick, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
uiManager.setButtonCallback("cargoshipButton", [this](const std::string&) {
|
||||||
|
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
||||||
|
if (nick.empty()) nick = "Player";
|
||||||
|
enterGameplay();
|
||||||
|
if (onMultiplayerPressed) onMultiplayerPressed(nick, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
uiManager.setButtonCallback("backButton", [this](const std::string&) {
|
||||||
|
enterMainMenu();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── State: Gameplay ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
void MenuManager::enterGameplay()
|
||||||
|
{
|
||||||
|
state = GameState::Gameplay;
|
||||||
|
uiManager.replaceRoot(gameplayRoot);
|
||||||
|
|
||||||
auto velocityTv = uiManager.findTextView("velocityText");
|
auto velocityTv = uiManager.findTextView("velocityText");
|
||||||
if (velocityTv) {
|
if (velocityTv) {
|
||||||
velocityTv->rect.x = 10.0f;
|
velocityTv->rect.x = 10.0f;
|
||||||
velocityTv->rect.y = static_cast<float>(Environment::height) - velocityTv->rect.h - 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");
|
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", [this]() {
|
uiManager.setButtonCallback("shootButton", [this](const std::string&) {
|
||||||
std::cerr << "Exit button bgScroll animation finished" << std::endl;
|
if (onFirePressed) onFirePressed();
|
||||||
g_exitBgAnimating = false;
|
|
||||||
});
|
});
|
||||||
|
uiManager.setButtonCallback("shootButton2", [this](const std::string&) {
|
||||||
uiManager.setButtonCallback("playButton", [this](const std::string& name) {
|
if (onFirePressed) onFirePressed();
|
||||||
std::cerr << "Play button pressed: " << name << std::endl;
|
|
||||||
});
|
});
|
||||||
|
uiManager.setButtonCallback("plusButton", [this](const std::string&) {
|
||||||
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) {
|
|
||||||
onFirePressed();
|
|
||||||
});
|
|
||||||
uiManager.setButtonCallback("shootButton2", [this](const std::string& name) {
|
|
||||||
onFirePressed();
|
|
||||||
});
|
|
||||||
uiManager.setButtonCallback("plusButton", [this](const std::string& name) {
|
|
||||||
int newVel = Environment::shipState.selectedVelocity + 1;
|
int newVel = Environment::shipState.selectedVelocity + 1;
|
||||||
if (newVel > 4)
|
if (newVel > 4) newVel = 4;
|
||||||
{
|
if (onVelocityChanged) onVelocityChanged(newVel);
|
||||||
newVel = 4;
|
|
||||||
}
|
|
||||||
onVelocityChanged(newVel);
|
|
||||||
});
|
});
|
||||||
uiManager.setButtonCallback("minusButton", [this](const std::string& name) {
|
uiManager.setButtonCallback("minusButton", [this](const std::string&) {
|
||||||
int newVel = Environment::shipState.selectedVelocity - 1;
|
int newVel = Environment::shipState.selectedVelocity - 1;
|
||||||
if (newVel < 0)
|
if (newVel < 0) newVel = 0;
|
||||||
{
|
if (onVelocityChanged) onVelocityChanged(newVel);
|
||||||
newVel = 0;
|
|
||||||
}
|
|
||||||
onVelocityChanged(newVel);
|
|
||||||
});
|
});
|
||||||
uiManager.setSliderCallback("velocitySlider", [this](const std::string& name, float value) {
|
uiManager.setSliderCallback("velocitySlider", [this](const std::string&, float value) {
|
||||||
|
int newVel = static_cast<int>(roundf(value * 10));
|
||||||
int newVel = roundf(value * 10);
|
if (newVel > 2) newVel = 2;
|
||||||
if (newVel > 2)
|
|
||||||
{
|
|
||||||
newVel = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newVel != Environment::shipState.selectedVelocity) {
|
if (newVel != Environment::shipState.selectedVelocity) {
|
||||||
onVelocityChanged(newVel);
|
if (onVelocityChanged) onVelocityChanged(newVel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("singleButton", [this, shipSelectionRoot, loadGameplayUI](const std::string& name) {
|
|
||||||
std::cerr << "Single button pressed: " << name << " -> open ship selection UI\n";
|
|
||||||
if (!shipSelectionRoot) {
|
|
||||||
std::cerr << "Failed to load ship selection UI\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (uiManager.pushMenuFromSavedRoot(shipSelectionRoot)) {
|
|
||||||
uiManager.setButtonCallback("spaceshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
|
||||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
|
||||||
if (nick.empty()) nick = "Player";
|
|
||||||
int shipType = 0;
|
|
||||||
uiManager.popMenu();
|
|
||||||
loadGameplayUI();
|
|
||||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, shipType);
|
|
||||||
});
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("cargoshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
|
||||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
|
||||||
if (nick.empty()) nick = "Player";
|
|
||||||
int shipType = 1;
|
|
||||||
uiManager.popMenu();
|
|
||||||
loadGameplayUI();
|
|
||||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, shipType);
|
|
||||||
});
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("backButton", [this](const std::string& btnName) {
|
|
||||||
uiManager.popMenu();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::cerr << "Failed to push ship selection menu\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("multiplayerButton", [this, shipSelectionRoot, loadGameplayUI](const std::string& name) {
|
|
||||||
std::cerr << "Multiplayer button pressed: " << name << " -> open ship selection UI\n";
|
|
||||||
if (!shipSelectionRoot) {
|
|
||||||
std::cerr << "Failed to load ship selection UI\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (uiManager.pushMenuFromSavedRoot(shipSelectionRoot)) {
|
|
||||||
uiManager.setButtonCallback("spaceshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
|
||||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
|
||||||
if (nick.empty()) nick = "Player";
|
|
||||||
int shipType = 0;
|
|
||||||
uiManager.popMenu();
|
|
||||||
loadGameplayUI();
|
|
||||||
if (onMultiplayerPressed) onMultiplayerPressed(nick, shipType);
|
|
||||||
});
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("cargoshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
|
||||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
|
||||||
if (nick.empty()) nick = "Player";
|
|
||||||
int shipType = 1;
|
|
||||||
uiManager.popMenu();
|
|
||||||
loadGameplayUI();
|
|
||||||
if (onMultiplayerPressed) onMultiplayerPressed(nick, shipType);
|
|
||||||
});
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("backButton", [this](const std::string& btnName) {
|
|
||||||
uiManager.popMenu();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::cerr << "Failed to push ship selection menu\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/*uiManager.setButtonCallback("multiplayerButton2", [this, shipSelectionRoot, loadGameplayUI](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)) {
|
|
||||||
|
|
||||||
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 + "...");
|
// ── State: GameOver ──────────────────────────────────────────────────────
|
||||||
std::cerr << "Connecting to server: " << serverAddress << std::endl;
|
|
||||||
|
|
||||||
});
|
void MenuManager::enterGameOver(int score)
|
||||||
|
{
|
||||||
|
state = GameState::GameOver;
|
||||||
|
uiManager.replaceRoot(gameOverRoot);
|
||||||
|
|
||||||
uiManager.setButtonCallback("backButton", [this](const std::string& buttonName) {
|
uiManager.setText("scoreText", "Score: " + std::to_string(score));
|
||||||
uiManager.popMenu();
|
|
||||||
});
|
|
||||||
|
|
||||||
uiManager.setTextFieldCallback("serverInputField",
|
uiManager.setButtonCallback("restartButton", [this](const std::string&) {
|
||||||
[this](const std::string& fieldName, const std::string& newText) {
|
if (onRestartPressed) onRestartPressed();
|
||||||
std::cout << "Server input field changed to: " << newText << std::endl;
|
enterGameplay();
|
||||||
});
|
});
|
||||||
|
uiManager.setButtonCallback("gameOverExitButton", [this](const std::string&) {
|
||||||
std::cerr << "Multiplayer menu loaded successfully\n";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::cerr << "Failed to load multiplayer menu\n";
|
|
||||||
}
|
|
||||||
std::cerr << "Single button pressed: " << name << " -> open ship selection UI\n";
|
|
||||||
if (!shipSelectionRoot) {
|
|
||||||
std::cerr << "Failed to load ship selection UI\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (uiManager.pushMenuFromSavedRoot(shipSelectionRoot)) {
|
|
||||||
uiManager.setButtonCallback("spaceshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
|
||||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
|
||||||
if (nick.empty()) nick = "Player";
|
|
||||||
int shipType = 0;
|
|
||||||
uiManager.popMenu();
|
|
||||||
loadGameplayUI();
|
|
||||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, shipType);
|
|
||||||
});
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("cargoshipButton", [this, loadGameplayUI](const std::string& btnName) {
|
|
||||||
std::string nick = uiManager.getTextFieldValue("nicknameInput");
|
|
||||||
if (nick.empty()) nick = "Player";
|
|
||||||
int shipType = 1;
|
|
||||||
uiManager.popMenu();
|
|
||||||
loadGameplayUI();
|
|
||||||
if (onSingleplayerPressed) onSingleplayerPressed(nick, shipType);
|
|
||||||
});
|
|
||||||
|
|
||||||
uiManager.setButtonCallback("backButton", [this](const std::string& btnName) {
|
|
||||||
uiManager.popMenu();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::cerr << "Failed to push ship selection menu\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
uiManager.setButtonCallback("exitButton", [](const std::string& name) {
|
|
||||||
std::cerr << "Exit from main menu pressed: " << name << " -> exiting\n";
|
|
||||||
Environment::exitGameLoop = true;
|
Environment::exitGameLoop = true;
|
||||||
});*/
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── State: ConnectionLost ─────────────────────────────────────────────────
|
||||||
|
|
||||||
|
void MenuManager::enterConnectionLost()
|
||||||
|
{
|
||||||
|
state = GameState::ConnectionLost;
|
||||||
|
uiManager.replaceRoot(connectionLostRoot);
|
||||||
|
|
||||||
|
uiManager.setButtonCallback("reconnectButton", [this](const std::string&) {
|
||||||
|
// TODO: reconnect logic
|
||||||
|
});
|
||||||
|
uiManager.setButtonCallback("exitServerButton", [this](const std::string&) {
|
||||||
|
Environment::exitGameLoop = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Public event API ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
void MenuManager::showGameOver(int score)
|
void MenuManager::showGameOver(int score)
|
||||||
{
|
{
|
||||||
if (!uiGameOverShown) {
|
if (state == GameState::Gameplay) {
|
||||||
if (uiManager.pushMenuFromSavedRoot(gameOverSavedRoot)) {
|
enterGameOver(score);
|
||||||
uiManager.setText("scoreText", std::string("Score: ") + std::to_string(score));
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uiManager.setButtonCallback("restartButton", [this](const std::string& name) {
|
void MenuManager::showConnectionLost()
|
||||||
uiManager.setText("scoreText", "");
|
{
|
||||||
uiGameOverShown = false;
|
if (state == GameState::Gameplay) {
|
||||||
uiManager.popMenu();
|
enterConnectionLost();
|
||||||
if (onRestartPressed) onRestartPressed();
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
uiManager.setButtonCallback("gameOverExitButton", [this](const std::string& name) {
|
} // namespace ZL
|
||||||
Environment::exitGameLoop = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
uiGameOverShown = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::cerr << "Failed to load game_over.json\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "render/Renderer.h"
|
#include "render/Renderer.h"
|
||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
#include "render/TextureManager.h"
|
#include "render/TextureManager.h"
|
||||||
@ -7,37 +7,58 @@
|
|||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
extern const char* CONST_ZIP_FILE;
|
extern const char* CONST_ZIP_FILE;
|
||||||
//extern bool g_exitBgAnimating;
|
|
||||||
|
|
||||||
class MenuManager
|
enum class GameState {
|
||||||
{
|
MainMenu,
|
||||||
|
ShipSelectionSingle,
|
||||||
|
ShipSelectionMulti,
|
||||||
|
Gameplay,
|
||||||
|
GameOver,
|
||||||
|
ConnectionLost
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuManager {
|
||||||
protected:
|
protected:
|
||||||
Renderer& renderer;
|
Renderer& renderer;
|
||||||
std::shared_ptr<UiNode> uiSavedRoot;
|
|
||||||
std::shared_ptr<UiNode> gameOverSavedRoot;
|
// Pre-loaded UI roots (loaded once in setupMenu)
|
||||||
std::shared_ptr<UiNode> settingsSavedRoot;
|
std::shared_ptr<UiNode> mainMenuRoot;
|
||||||
std::shared_ptr<UiNode> multiplayerSavedRoot;
|
std::shared_ptr<UiNode> shipSelectionRoot;
|
||||||
|
std::shared_ptr<UiNode> gameplayRoot;
|
||||||
|
std::shared_ptr<UiNode> gameOverRoot;
|
||||||
|
std::shared_ptr<UiNode> connectionLostRoot;
|
||||||
|
|
||||||
|
GameState state = GameState::MainMenu;
|
||||||
|
|
||||||
|
// State transition methods
|
||||||
|
void enterMainMenu();
|
||||||
|
void enterShipSelectionSingle();
|
||||||
|
void enterShipSelectionMulti();
|
||||||
|
void enterGameplay();
|
||||||
|
void enterGameOver(int score);
|
||||||
|
void enterConnectionLost();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool uiGameOverShown = false;
|
|
||||||
bool g_exitBgAnimating = false;
|
|
||||||
|
|
||||||
UiManager uiManager;
|
UiManager uiManager;
|
||||||
|
|
||||||
MenuManager(Renderer& iRenderer);
|
MenuManager(Renderer& iRenderer);
|
||||||
|
|
||||||
void setupMenu();
|
void setupMenu();
|
||||||
|
|
||||||
//void showGameOver();
|
// Returns true for states where Space should render and run (Gameplay, GameOver, ConnectionLost)
|
||||||
void showGameOver(int score);
|
bool shouldRenderSpace() const;
|
||||||
|
GameState getState() const { return state; }
|
||||||
|
|
||||||
|
// Called by game events
|
||||||
|
void showGameOver(int score);
|
||||||
|
void showConnectionLost();
|
||||||
|
|
||||||
|
// Callbacks set by Game/Space
|
||||||
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()> onFirePressed;
|
||||||
|
|
||||||
std::function<void(const std::string&, int)> onSingleplayerPressed;
|
std::function<void(const std::string&, int)> onSingleplayerPressed;
|
||||||
std::function<void(const std::string&, int)> onMultiplayerPressed;
|
std::function<void(const std::string&, int)> onMultiplayerPressed;
|
||||||
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace ZL
|
||||||
@ -1831,7 +1831,7 @@ namespace ZL
|
|||||||
gameOver = true;
|
gameOver = true;
|
||||||
Environment::shipState.velocity = 0.0f;
|
Environment::shipState.velocity = 0.0f;
|
||||||
std::cout << "Client: Lost connection to server\n";
|
std::cout << "Client: Lost connection to server\n";
|
||||||
menuManager.showGameOver(this->playerScore);
|
menuManager.showConnectionLost();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pending = networkClient->getPendingProjectiles();
|
auto pending = networkClient->getPendingProjectiles();
|
||||||
|
|||||||
@ -169,6 +169,54 @@ namespace ZL {
|
|||||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UiStaticImage::buildMesh() {
|
||||||
|
mesh.data.PositionData.clear();
|
||||||
|
mesh.data.TexCoordData.clear();
|
||||||
|
|
||||||
|
float x0 = rect.x;
|
||||||
|
float y0 = rect.y;
|
||||||
|
float x1 = rect.x + rect.w;
|
||||||
|
float y1 = rect.y + rect.h;
|
||||||
|
|
||||||
|
mesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 0, 0 });
|
||||||
|
|
||||||
|
mesh.data.PositionData.push_back({ x0, y1, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 0, 1 });
|
||||||
|
|
||||||
|
mesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 1, 1 });
|
||||||
|
|
||||||
|
mesh.data.PositionData.push_back({ x0, y0, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 0, 0 });
|
||||||
|
|
||||||
|
mesh.data.PositionData.push_back({ x1, y1, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 1, 1 });
|
||||||
|
|
||||||
|
mesh.data.PositionData.push_back({ x1, y0, 0 });
|
||||||
|
mesh.data.TexCoordData.push_back({ 1, 0 });
|
||||||
|
|
||||||
|
mesh.RefreshVBO();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UiStaticImage::draw(Renderer& renderer) const {
|
||||||
|
if (!texture) return;
|
||||||
|
|
||||||
|
static const std::string vPositionName = "vPosition";
|
||||||
|
static const std::string vTexCoordName = "vTexCoord";
|
||||||
|
static const std::string textureUniformName = "Texture";
|
||||||
|
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
renderer.EnableVertexAttribArray(vPositionName);
|
||||||
|
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||||
|
renderer.DrawVertexRenderStruct(mesh);
|
||||||
|
|
||||||
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
|
}
|
||||||
|
|
||||||
void UiTextField::draw(Renderer& renderer) const {
|
void UiTextField::draw(Renderer& renderer) const {
|
||||||
if (textRenderer) {
|
if (textRenderer) {
|
||||||
float textX = rect.x + 10.0f;
|
float textX = rect.x + 10.0f;
|
||||||
@ -249,6 +297,7 @@ namespace ZL {
|
|||||||
if (j.contains("horizontal_gravity")) {
|
if (j.contains("horizontal_gravity")) {
|
||||||
std::string hg = j["horizontal_gravity"].get<std::string>();
|
std::string hg = j["horizontal_gravity"].get<std::string>();
|
||||||
if (hg == "right") node->layoutSettings.hGravity = HorizontalGravity::Right;
|
if (hg == "right") node->layoutSettings.hGravity = HorizontalGravity::Right;
|
||||||
|
else if (hg == "center") node->layoutSettings.hGravity = HorizontalGravity::Center;
|
||||||
else node->layoutSettings.hGravity = HorizontalGravity::Left;
|
else node->layoutSettings.hGravity = HorizontalGravity::Left;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,6 +305,7 @@ namespace ZL {
|
|||||||
if (j.contains("vertical_gravity")) {
|
if (j.contains("vertical_gravity")) {
|
||||||
std::string vg = j["vertical_gravity"].get<std::string>();
|
std::string vg = j["vertical_gravity"].get<std::string>();
|
||||||
if (vg == "bottom") node->layoutSettings.vGravity = VerticalGravity::Bottom;
|
if (vg == "bottom") node->layoutSettings.vGravity = VerticalGravity::Bottom;
|
||||||
|
else if (vg == "center") node->layoutSettings.vGravity = VerticalGravity::Center;
|
||||||
else node->layoutSettings.vGravity = VerticalGravity::Top;
|
else node->layoutSettings.vGravity = VerticalGravity::Top;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,6 +453,32 @@ namespace ZL {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeStr == "StaticImage") {
|
||||||
|
auto img = std::make_shared<UiStaticImage>();
|
||||||
|
img->name = node->name;
|
||||||
|
img->rect = initialRect;
|
||||||
|
|
||||||
|
std::string texPath;
|
||||||
|
if (j.contains("texture") && j["texture"].is_string()) {
|
||||||
|
texPath = j["texture"].get<std::string>();
|
||||||
|
}
|
||||||
|
else if (j.contains("textures") && j["textures"].is_object() && j["textures"].contains("normal")) {
|
||||||
|
texPath = j["textures"]["normal"].get<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!texPath.empty()) {
|
||||||
|
try {
|
||||||
|
auto data = CreateTextureDataFromPng(texPath.c_str(), zipFile.c_str());
|
||||||
|
img->texture = std::make_shared<Texture>(data);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
std::cerr << "UiManager: failed load texture for StaticImage '" << img->name << "' : " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node->staticImage = img;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeStr == "TextView") {
|
if (typeStr == "TextView") {
|
||||||
auto tv = std::make_shared<UiTextView>();
|
auto tv = std::make_shared<UiTextView>();
|
||||||
|
|
||||||
@ -492,6 +568,7 @@ namespace ZL {
|
|||||||
sliders.clear();
|
sliders.clear();
|
||||||
textViews.clear();
|
textViews.clear();
|
||||||
textFields.clear();
|
textFields.clear();
|
||||||
|
staticImages.clear();
|
||||||
collectButtonsAndSliders(root);
|
collectButtonsAndSliders(root);
|
||||||
|
|
||||||
nodeActiveAnims.clear();
|
nodeActiveAnims.clear();
|
||||||
@ -503,6 +580,9 @@ namespace ZL {
|
|||||||
s->buildTrackMesh();
|
s->buildTrackMesh();
|
||||||
s->buildKnobMesh();
|
s->buildKnobMesh();
|
||||||
}
|
}
|
||||||
|
for (auto& img : staticImages) {
|
||||||
|
img->buildMesh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
void UiManager::loadFromFile(const std::string& path, Renderer& renderer, const std::string& zipFile) {
|
||||||
@ -618,9 +698,15 @@ namespace ZL {
|
|||||||
if (child->layoutSettings.hGravity == HorizontalGravity::Right) {
|
if (child->layoutSettings.hGravity == HorizontalGravity::Right) {
|
||||||
fLX = currentW - childW - child->localX;
|
fLX = currentW - childW - child->localX;
|
||||||
}
|
}
|
||||||
|
else if (child->layoutSettings.hGravity == HorizontalGravity::Center) {
|
||||||
|
fLX = (currentW - childW) / 2.0f + child->localX;
|
||||||
|
}
|
||||||
if (child->layoutSettings.vGravity == VerticalGravity::Top) {
|
if (child->layoutSettings.vGravity == VerticalGravity::Top) {
|
||||||
fLY = currentH - childH - child->localY;
|
fLY = currentH - childH - child->localY;
|
||||||
}
|
}
|
||||||
|
else if (child->layoutSettings.vGravity == VerticalGravity::Center) {
|
||||||
|
fLY = (currentH - childH) / 2.0f + child->localY;
|
||||||
|
}
|
||||||
|
|
||||||
// Передаем рассчитанные fLX, fLY в рекурсию
|
// Передаем рассчитанные fLX, fLY в рекурсию
|
||||||
layoutNode(child, node->screenRect.x, node->screenRect.y, currentW, currentH, fLX, fLY);
|
layoutNode(child, node->screenRect.x, node->screenRect.y, currentW, currentH, fLX, fLY);
|
||||||
@ -661,6 +747,12 @@ namespace ZL {
|
|||||||
node->textField->rect = node->screenRect;
|
node->textField->rect = node->screenRect;
|
||||||
// Аналогично для курсора и фонового меша
|
// Аналогично для курсора и фонового меша
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5. Обновляем статическое изображение
|
||||||
|
if (node->staticImage) {
|
||||||
|
node->staticImage->rect = node->screenRect;
|
||||||
|
node->staticImage->buildMesh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UiManager::updateAllLayouts() {
|
void UiManager::updateAllLayouts() {
|
||||||
@ -690,6 +782,9 @@ namespace ZL {
|
|||||||
if (node->textField) {
|
if (node->textField) {
|
||||||
textFields.push_back(node->textField);
|
textFields.push_back(node->textField);
|
||||||
}
|
}
|
||||||
|
if (node->staticImage) {
|
||||||
|
staticImages.push_back(node->staticImage);
|
||||||
|
}
|
||||||
for (auto& c : node->children) collectButtonsAndSliders(c);
|
for (auto& c : node->children) collectButtonsAndSliders(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,6 +883,7 @@ namespace ZL {
|
|||||||
prev.buttons = buttons;
|
prev.buttons = buttons;
|
||||||
prev.sliders = sliders;
|
prev.sliders = sliders;
|
||||||
prev.textFields = textFields;
|
prev.textFields = textFields;
|
||||||
|
prev.staticImages = staticImages;
|
||||||
prev.pressedButton = pressedButton;
|
prev.pressedButton = pressedButton;
|
||||||
prev.pressedSlider = pressedSlider;
|
prev.pressedSlider = pressedSlider;
|
||||||
prev.focusedTextField = focusedTextField;
|
prev.focusedTextField = focusedTextField;
|
||||||
@ -838,6 +934,7 @@ namespace ZL {
|
|||||||
buttons = s.buttons;
|
buttons = s.buttons;
|
||||||
sliders = s.sliders;
|
sliders = s.sliders;
|
||||||
textFields = s.textFields;
|
textFields = s.textFields;
|
||||||
|
staticImages = s.staticImages;
|
||||||
pressedButton = s.pressedButton;
|
pressedButton = s.pressedButton;
|
||||||
pressedSlider = s.pressedSlider;
|
pressedSlider = s.pressedSlider;
|
||||||
focusedTextField = s.focusedTextField;
|
focusedTextField = s.focusedTextField;
|
||||||
@ -872,6 +969,9 @@ namespace ZL {
|
|||||||
renderer.PushMatrix();
|
renderer.PushMatrix();
|
||||||
renderer.LoadIdentity();
|
renderer.LoadIdentity();
|
||||||
|
|
||||||
|
for (const auto& img : staticImages) {
|
||||||
|
img->draw(renderer);
|
||||||
|
}
|
||||||
for (const auto& b : buttons) {
|
for (const auto& b : buttons) {
|
||||||
b->draw(renderer);
|
b->draw(renderer);
|
||||||
}
|
}
|
||||||
@ -1261,6 +1361,13 @@ namespace ZL {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<UiStaticImage> UiManager::findStaticImage(const std::string& name) {
|
||||||
|
for (auto& img : staticImages) {
|
||||||
|
if (img->name == name) return img;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<UiTextView> UiManager::findTextView(const std::string& name) {
|
std::shared_ptr<UiTextView> UiManager::findTextView(const std::string& name) {
|
||||||
for (auto& tv : textViews) {
|
for (auto& tv : textViews) {
|
||||||
if (tv->name == name) return tv;
|
if (tv->name == name) return tv;
|
||||||
|
|||||||
@ -60,11 +60,13 @@ namespace ZL {
|
|||||||
|
|
||||||
enum class HorizontalGravity {
|
enum class HorizontalGravity {
|
||||||
Left,
|
Left,
|
||||||
|
Center,
|
||||||
Right
|
Right
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class VerticalGravity {
|
enum class VerticalGravity {
|
||||||
Bottom, // Обычно в OpenGL Y растет вверх, так что низ - это 0
|
Bottom, // Обычно в OpenGL Y растет вверх, так что низ - это 0
|
||||||
|
Center,
|
||||||
Top
|
Top
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -159,6 +161,17 @@ namespace ZL {
|
|||||||
void draw(Renderer& renderer) const;
|
void draw(Renderer& renderer) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct UiStaticImage {
|
||||||
|
std::string name;
|
||||||
|
UiRect rect;
|
||||||
|
std::shared_ptr<Texture> texture;
|
||||||
|
|
||||||
|
VertexRenderStruct mesh;
|
||||||
|
|
||||||
|
void buildMesh();
|
||||||
|
void draw(Renderer& renderer) const;
|
||||||
|
};
|
||||||
|
|
||||||
struct UiNode {
|
struct UiNode {
|
||||||
std::string name;
|
std::string name;
|
||||||
LayoutType layoutType = LayoutType::Frame;
|
LayoutType layoutType = LayoutType::Frame;
|
||||||
@ -185,6 +198,7 @@ namespace ZL {
|
|||||||
std::shared_ptr<UiSlider> slider;
|
std::shared_ptr<UiSlider> slider;
|
||||||
std::shared_ptr<UiTextView> textView;
|
std::shared_ptr<UiTextView> textView;
|
||||||
std::shared_ptr<UiTextField> textField;
|
std::shared_ptr<UiTextField> textField;
|
||||||
|
std::shared_ptr<UiStaticImage> staticImage;
|
||||||
|
|
||||||
// Анимации
|
// Анимации
|
||||||
struct AnimStep {
|
struct AnimStep {
|
||||||
@ -256,6 +270,8 @@ namespace ZL {
|
|||||||
bool setTextFieldCallback(const std::string& name, std::function<void(const std::string&, const std::string&)> cb);
|
bool setTextFieldCallback(const std::string& name, std::function<void(const std::string&, const std::string&)> cb);
|
||||||
std::string getTextFieldValue(const std::string& name);
|
std::string getTextFieldValue(const std::string& name);
|
||||||
|
|
||||||
|
std::shared_ptr<UiStaticImage> findStaticImage(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 pushMenuFromSavedRoot(std::shared_ptr<UiNode> newRoot);
|
||||||
bool popMenu();
|
bool popMenu();
|
||||||
@ -301,6 +317,7 @@ namespace ZL {
|
|||||||
std::vector<std::shared_ptr<UiSlider>> sliders;
|
std::vector<std::shared_ptr<UiSlider>> sliders;
|
||||||
std::vector<std::shared_ptr<UiTextView>> textViews;
|
std::vector<std::shared_ptr<UiTextView>> textViews;
|
||||||
std::vector<std::shared_ptr<UiTextField>> textFields;
|
std::vector<std::shared_ptr<UiTextField>> textFields;
|
||||||
|
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
|
||||||
|
|
||||||
std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
|
std::map<std::shared_ptr<UiNode>, std::vector<ActiveAnim>> nodeActiveAnims;
|
||||||
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)
|
std::map<std::pair<std::string, std::string>, std::function<void()>> animCallbacks; // key: (nodeName, animName)
|
||||||
@ -314,6 +331,7 @@ namespace ZL {
|
|||||||
std::vector<std::shared_ptr<UiButton>> buttons;
|
std::vector<std::shared_ptr<UiButton>> buttons;
|
||||||
std::vector<std::shared_ptr<UiSlider>> sliders;
|
std::vector<std::shared_ptr<UiSlider>> sliders;
|
||||||
std::vector<std::shared_ptr<UiTextField>> textFields;
|
std::vector<std::shared_ptr<UiTextField>> textFields;
|
||||||
|
std::vector<std::shared_ptr<UiStaticImage>> staticImages;
|
||||||
std::shared_ptr<UiButton> pressedButton;
|
std::shared_ptr<UiButton> pressedButton;
|
||||||
std::shared_ptr<UiSlider> pressedSlider;
|
std::shared_ptr<UiSlider> pressedSlider;
|
||||||
std::shared_ptr<UiTextField> focusedTextField;
|
std::shared_ptr<UiTextField> focusedTextField;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user