Fixing bugs in UI animations, added credits

This commit is contained in:
Vladislav Khorev committed 2026-07-15 18:14:01 +03:00
1 parent 9f17d51fc8
commit 159fac02f0
8 files changed
+864 -86

No files matched your search

+20
View File
@@ -215,6 +215,7 @@ namespace ZL {
portraitQuestJournalItemRoot = loadLocalizedUi("resources/w/ui/portrait_screen_journal_item.json", zipFile);
mainMenuRoot = loadLocalizedUi("resources/w/ui/screen_main_menu.json", zipFile);
aboutScreenRoot = loadLocalizedUi("resources/w/ui/screen_about.json", zipFile);
creditsScreenRoot = loadLocalizedUi("resources/w/ui/screen_credits.json", zipFile);
settingsScreenRoot = loadLocalizedUi("resources/w/ui/screen_settings.json", zipFile);
settingsGraphicsScreenRoot = loadLocalizedUi("resources/w/ui/screen_settings_graphics.json", zipFile);
languageScreenRoot = loadLocalizedUi("resources/w/ui/screen_language.json", zipFile);
@@ -344,6 +345,8 @@ namespace ZL {
topUiManager.clearMenuStack();
uiManager.replaceRoot(mainMenuRoot);
uiManager.setTextButtonCallback("menuStartButton", [this](const std::string&) {
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
enterGameplay();
@@ -353,6 +356,10 @@ namespace ZL {
showAboutScreen();
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuCreditsButton", [this](const std::string&) {
showCreditsScreen();
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.setTextButtonCallback("menuSettingsButton", [this](const std::string&) {
showSettingsScreen();
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
@@ -387,6 +394,19 @@ namespace ZL {
});
}
void MenuManager::showCreditsScreen() {
uiManager.pushMenuFromSavedRoot(creditsScreenRoot);
uiManager.setButtonCallback("creditsBackButton", [this](const std::string&) {
uiManager.popMenu();
uiManager.updateAllLayouts();
audioPlayer_.playSoundAsync("audio/751089__smallconfusion__mechanical-plastic-click-11.ogg");
});
uiManager.stopAnimationOnNode("creditsLinearLayout", "slowScroll");
uiManager.resetAnimationOnNode("creditsLinearLayout");
uiManager.startAnimationOnNode("creditsLinearLayout", "slowScroll");
}
void MenuManager::showSettingsScreen() {
uiManager.pushMenuFromSavedRoot(settingsScreenRoot);
uiManager.setTextButtonCallback("settingsBackButton", [this](const std::string&) {
+2
View File
@@ -172,6 +172,7 @@ namespace ZL {
//void closePortraitJournalItemCompletely();
void showAboutScreen();
void showCreditsScreen();
void showSettingsScreen();
void showSettingsGraphicsScreen();
void showLanguageScreen();
@@ -262,6 +263,7 @@ namespace ZL {
std::shared_ptr<UiNode> mainMenuRoot;
std::shared_ptr<UiNode> aboutScreenRoot;
std::shared_ptr<UiNode> creditsScreenRoot;
std::shared_ptr<UiNode> settingsScreenRoot;
std::shared_ptr<UiNode> settingsGraphicsScreenRoot;
std::shared_ptr<UiNode> languageScreenRoot;
+99 -40
View File
@@ -182,7 +182,7 @@ namespace ZL {
mesh.RefreshVBO();
}
void UiTextButton::draw(Renderer& renderer) const {
void UiTextButton::draw(Renderer& renderer, float accumX, float accumY) const {
// Draw background texture (optional)
const std::shared_ptr<Texture>* tex = nullptr;
@@ -221,16 +221,18 @@ namespace ZL {
? wrapTextByPixels(text, *textRenderer, rect.w - textPaddingX * 2.0f, scale)
: text;
float tx = rect.x + rect.w / 2.0f + animOffsetX;
// Прибавляем accumX и accumY
float tx = rect.x + rect.w / 2.0f + animOffsetX + accumX;
if (!textCentered) {
tx = rect.x + textPaddingX + animOffsetX;
tx = rect.x + textPaddingX + animOffsetX + accumX;
}
float ty;
if (topAligned) {
ty = rect.y + rect.h - textPaddingY - static_cast<float>(fontSize) + animOffsetY;
} else {
ty = rect.y + rect.h * 0.5f + textPaddingY + animOffsetY;
ty = rect.y + rect.h - textPaddingY - static_cast<float>(fontSize) + animOffsetY + accumY;
}
else {
ty = rect.y + rect.h * 0.5f + textPaddingY + animOffsetY + accumY;
}
textRenderer->drawText(displayText, tx, ty, scale, textCentered, color);
@@ -238,24 +240,19 @@ namespace ZL {
glEnable(GL_DEPTH_TEST);
}
void UiTextView::draw(Renderer& renderer) const {
void UiTextView::draw(Renderer& renderer, float accumX, float accumY) const {
(void)renderer;
if (!textRenderer || text.empty()) {
return;
}
const float scale = 1.0f;
// Backward compatibility:
// Old UI files, including the original inventory panel, positioned TextView
// around the rect center. If a TextView does not explicitly request wrapping,
// top alignment, padding or line limiting, keep that old behavior.
const bool usesModernRectText = wrap || topAligned || paddingX != 0.0f || paddingY != 0.0f || maxLines > 0;
if (!usesModernRectText) {
textRenderer->drawText(
text,
rect.x + rect.w * 0.5f,
rect.y + rect.h * 0.5f,
rect.x + rect.w * 0.5f + accumX,
rect.y + rect.h * 0.5f + accumY,
scale,
textCentered,
color
@@ -272,12 +269,10 @@ namespace ZL {
float ty = rect.y + rect.h * 0.5f;
if (topAligned) {
// TextRenderer expects a baseline position. This offset places the first
// visible line close to the top inside the TextView rectangle.
ty = rect.y + rect.h - paddingY - static_cast<float>(fontSize);
}
textRenderer->drawText(finalText, tx, ty, scale, textCentered, color);
textRenderer->drawText(finalText, tx + accumX, ty + accumY, scale, textCentered, color);
}
void UiSlider::buildTrackMesh() {
@@ -1480,60 +1475,61 @@ namespace ZL {
renderer.LoadIdentity();
renderer.RenderUniform1f("uAlpha", 1.0f); // ensure alpha is fully opaque unless a widget overrides it
std::function<void(const std::shared_ptr<UiNode>&)> drawNode =
[&](const std::shared_ptr<UiNode>& node) {
std::function<void(const std::shared_ptr<UiNode>&, float, float)> drawNode =
[&](const std::shared_ptr<UiNode>& node, float accumX, float accumY) {
if (!node || !node->visible) return;
// Считаем полное смещение для текущего узла и его потомков
float curAccumX = accumX + node->animOffsetX;
float curAccumY = accumY + node->animOffsetY;
renderer.PushMatrix();
renderer.TranslateMatrix({ node->screenRect.x + node->screenRect.w * 0.5f,
renderer.TranslateMatrix({ node->animOffsetX, node->animOffsetY, 0.0f });
renderer.TranslateMatrix({ node->screenRect.x + node->screenRect.w * 0.5f,
node->screenRect.y + node->screenRect.h * 0.5f, 0.0f });
renderer.ScaleMatrix({ node->scaleX, node->scaleY, 1.0f });
renderer.TranslateMatrix({ -(node->screenRect.x + node->screenRect.w * 0.5f),
renderer.TranslateMatrix({ -(node->screenRect.x + node->screenRect.w * 0.5f),
-(node->screenRect.y + node->screenRect.h * 0.5f), 0.0f });
// 1. Сначала изображения
if (node->staticImage) {
node->staticImage->draw(renderer);
}
// 1b. Chat bubble (composite widget)
if (node->chatBubble) {
node->chatBubble->draw(renderer, node->scaleX, node->scaleY, projW, projH);
}
// 2. Потом кнопки
if (node->button) {
node->button->draw(renderer);
}
if (node->textButton) {
node->textButton->draw(renderer);
// Передаем накопленные смещения
node->textButton->draw(renderer, curAccumX, curAccumY);
}
// 3. Потом слайдеры
if (node->slider) {
node->slider->draw(renderer);
}
// 4. Потом текстовые поля
if (node->textField) {
node->textField->draw(renderer);
}
// 5. ПОСЛЕДНИЙ - ТЕКСТ (поверх всего!)
if (node->textView) {
node->textView->draw(renderer);
// Передаем накопленные смещения
node->textView->draw(renderer, curAccumX, curAccumY);
}
// Рисуем детей
for (auto& child : node->children) {
drawNode(child);
// Прокидываем сумму дальше
drawNode(child, curAccumX, curAccumY);
}
renderer.PopMatrix();
};
drawNode(root);
drawNode(root, 0.0f, 0.0f);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
@@ -1625,6 +1621,12 @@ namespace ZL {
node->textButton->animScaleX = act.origScaleX;
node->textButton->animScaleY = act.origScaleY;
}
else
{
//logger() << "[animation] Resetting animation for node: " << node->name << " y=" << act.origOffsetY << std::endl;
node->animOffsetX = act.origOffsetX;
node->animOffsetY = act.origOffsetY;
}
act.stepIndex = 0;
act.elapsedMs = 0.0f;
act.stepStarted = false;
@@ -1650,6 +1652,12 @@ namespace ZL {
node->textButton->animOffsetX = step.toX;
node->textButton->animOffsetY = step.toY;
}
else
{
//logger() << "[animation] step.durationMs <= 0.0f: " << node->name << " y=" << step.toY << std::endl;
node->animOffsetX = step.toX;
node->animOffsetY = step.toY;
}
}
else if (step.type == "scale") {
if (node->button) {
@@ -1681,8 +1689,9 @@ namespace ZL {
act.origScaleY = node->textButton->animScaleY;
}
else {
act.origOffsetX = act.origOffsetY = 0.0f;
act.origScaleX = act.origScaleY = 1.0f;
//logger() << "[animation] !act.stepStarted && act.stepIndex == 0 && act.elapsedMs == 0.0f: " << node->name << " y=" << node->animOffsetY << std::endl;
act.origOffsetX = node->animOffsetX;
act.origOffsetY = node->animOffsetY;
}
}
@@ -1703,8 +1712,11 @@ namespace ZL {
act.startScaleY = node->textButton->animScaleY;
}
else {
act.startOffsetX = act.startOffsetY = 0.0f;
act.startScaleX = act.startScaleY = 1.0f;
//logger() << "[animation] !act.stepStarted && prevElapsed == 0.0f: " << node->name << " y=" << node->animOffsetY << std::endl;
act.startOffsetX = node->animOffsetX;
act.startOffsetY = node->animOffsetY;
act.startScaleX = node->scaleX;
act.startScaleY = node->scaleY;
}
if (step.type == "move") {
act.endOffsetX = step.toX;
@@ -1731,6 +1743,12 @@ namespace ZL {
node->textButton->animOffsetX = nx;
node->textButton->animOffsetY = ny;
}
else {
//logger() << "[animation] if (step.type ==move): " << node->name << " y=" << ny << std::endl;
node->animOffsetX = nx;
node->animOffsetY = ny;
}
}
else if (step.type == "scale") {
float sx = act.startScaleX + (act.endScaleX - act.startScaleX) * te;
@@ -1986,12 +2004,46 @@ namespace ZL {
aa.origScaleX = node->textButton->animScaleX;
aa.origScaleY = node->textButton->animScaleY;
}
else
{
//logger() << "[animation] startAnimationOnNode: " << node->name << " y=" << node->animOffsetY << std::endl;
aa.origOffsetX = node->animOffsetX;
aa.origOffsetY = node->animOffsetY;
}
auto cbIt = animCallbacks.find({ nodeName, animName });
if (cbIt != animCallbacks.end()) aa.onComplete = cbIt->second;
nodeActiveAnims[node].push_back(std::move(aa));
return true;
}
bool UiManager::resetAnimationOnNode(const std::string& nodeName)
{
if (!root) return false;
auto node = findNodeByName(root, nodeName);
if (!node) return false;
if (node->button) {
node->button->animOffsetX = 0;
node->button->animOffsetY = 0;
node->button->animScaleX = 1.0f;
node->button->animScaleY = 1.0f;
}
else if (node->textButton) {
node->textButton->animOffsetX = 0;
node->textButton->animOffsetY = 0;
node->textButton->animScaleX = 1.0f;
node->textButton->animScaleY = 1.0f;
}
else
{
//logger() << "[animation] startAnimationOnNode: " << node->name << " y=" << node->animOffsetY << std::endl;
node->animOffsetX = 0;
node->animOffsetY = 0;
}
return true;
}
bool UiManager::stopAnimationOnNode(const std::string& nodeName, const std::string& animName) {
if (!root) return false;
auto node = findNodeByName(root, nodeName);
@@ -2042,6 +2094,13 @@ namespace ZL {
aa.origScaleX = n->textButton->animScaleX;
aa.origScaleY = n->textButton->animScaleY;
}
else
{
//logger() << "[animation] startAnimation: " << n->name << " y=" << n->animOffsetY << std::endl;
aa.origOffsetX = n->animOffsetX;
aa.origOffsetY = n->animOffsetY;
}
auto cbIt = animCallbacks.find({ n->name, animName });
if (cbIt != animCallbacks.end()) aa.onComplete = cbIt->second;
nodeActiveAnims[n].push_back(std::move(aa));
+5 -2
View File
@@ -187,7 +187,7 @@ namespace ZL {
float animScaleY = 1.0f;
void buildMesh();
void draw(Renderer& renderer) const;
void draw(Renderer& renderer, float accumX = 0.0f, float accumY = 0.0f) const;
};
struct UiTextView {
@@ -206,7 +206,7 @@ namespace ZL {
std::unique_ptr<TextRenderer> textRenderer;
void draw(Renderer& renderer) const;
void draw(Renderer& renderer, float accumX = 0.0f, float accumY = 0.0f) const;
};
struct UiTextField {
@@ -313,6 +313,8 @@ namespace ZL {
float scaleX = 1.0f;
float scaleY = 1.0f;
float animOffsetX = 0.0f;
float animOffsetY = 0.0f;
bool visible = true;
@@ -475,6 +477,7 @@ namespace ZL {
void update(float deltaMs);
void startAnimation(const std::string& animName);
bool startAnimationOnNode(const std::string& nodeName, const std::string& animName);
bool resetAnimationOnNode(const std::string& nodeName);
bool stopAnimationOnNode(const std::string& nodeName, const std::string& animName);
bool setAnimationCallback(const std::string& nodeName, const std::string& animName, std::function<void()> cb);
void updateAllLayouts();