Working on loading screen
This commit is contained in:
parent
1c2c80f0ea
commit
44f5136f1d
117
src/Game.cpp
117
src/Game.cpp
@ -44,15 +44,11 @@ namespace ZL
|
||||
std::cout << "Resources.zip loaded successfully--0" << std::endl;
|
||||
if (s_instance) {
|
||||
s_instance->resourcesDownloadProgress = 1.0f;
|
||||
/*s_instance->mainThreadHandler.EnqueueMainThreadTask([&]() {
|
||||
// Builds the loadSteps queue (cheap, no GL work); the actual loading
|
||||
// happens one step per frame in Game::update() so the browser tab
|
||||
// keeps repainting/responding instead of freezing for the whole load.
|
||||
s_instance->setupPart2();
|
||||
});*/
|
||||
std::cout << "s_instance setup part 2" << std::endl;
|
||||
s_instance->setupPart2();
|
||||
|
||||
}
|
||||
std::cout << "s_instance end setup" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
void Game::onResourcesZipError(unsigned /*handle*/, void* /*userData*/, int /*httpStatus*/) {
|
||||
@ -107,12 +103,11 @@ namespace ZL
|
||||
#endif
|
||||
loadingTexture = renderer.textureManager.LoadFromPng("resources/loading.png", "");
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
loadingProgressBarFrameTexture = renderer.textureManager.LoadFromPng(
|
||||
"resources/loadingProgressBarFrame.png", "", true);
|
||||
loadingProgressBarTexture = renderer.textureManager.LoadFromPng(
|
||||
"resources/loadingProgressBar.png", "", true);
|
||||
#endif
|
||||
|
||||
float minDimension;
|
||||
float width = Environment::projectionWidth;
|
||||
float height = Environment::projectionHeight;
|
||||
@ -137,76 +132,54 @@ namespace ZL
|
||||
emscripten_async_wget2("resources.zip", "resources.zip", "GET", nullptr, nullptr,
|
||||
onResourcesZipLoaded, onResourcesZipError, onResourcesZipProgress);
|
||||
#else
|
||||
mainThreadHandler.EnqueueMainThreadTask([this]() {
|
||||
std::cout << "Load resurces step 2" << std::endl;
|
||||
this->setupPart2();
|
||||
std::cout << "Load resurces step 3" << std::endl;
|
||||
});
|
||||
// Builds the loadSteps queue (cheap, no GL work); the actual loading
|
||||
// happens one step per frame in Game::update() so the window keeps
|
||||
// pumping events / repainting instead of freezing for the whole load.
|
||||
setupPart2();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void Game::setupPart2()
|
||||
{
|
||||
std::cout << "Load resurces step 12x1" << std::endl;
|
||||
// Builds an ordered queue of small closures instead of doing the work here.
|
||||
// Game::update() drains one closure per frame, so the event loop / browser
|
||||
// paint gets a turn between steps instead of the window freezing for the
|
||||
// whole duration of loading. Building the queue itself is cheap (no GL calls).
|
||||
|
||||
// Must happen before createLocations() below: some location scripts
|
||||
// Must happen before the location step below: some location scripts
|
||||
// (e.g. resources/start_dorm.lua) start a dialogue as soon as they're
|
||||
// loaded, so the language needs to be known before that first load.
|
||||
loadSteps.push([this]() {
|
||||
ZL::loadLanguageSetting();
|
||||
});
|
||||
|
||||
|
||||
loadSteps.push([this]() {
|
||||
#ifdef EMSCRIPTEN
|
||||
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x2" << std::endl;
|
||||
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x3" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("planetBake", "resources/shaders/planet_bake.vertex", "resources/shaders/planet_bake_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x4" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("planetStone", "resources/shaders/planet_stone.vertex", "resources/shaders/planet_stone_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x5" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("planetLand", "resources/shaders/planet_land.vertex", "resources/shaders/planet_land_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x6" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("spark", "resources/shaders/spark.vertex", "resources/shaders/spark_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x7" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("skinning", "resources/shaders/skinning.vertex", "resources/shaders/default_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x8" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("fog", "resources/shaders/fog.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x9" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("fog_skinning", "resources/shaders/fog_skinning.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x10" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("darklands_fog", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x11" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("darklands_fog_skinning", "resources/shaders/darklands_fog_skinning.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x12" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("darklands_flash", "resources/shaders/default.vertex", "resources/shaders/darklands_flash_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x13" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("cutsceneFade", "resources/shaders/default.vertex", "resources/shaders/cutscene_fade_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x14" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("cutsceneBlack", "resources/shaders/default.vertex", "resources/shaders/cutscene_black_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x15" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("shadow_depth", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x16" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("shadow_depth_skinning", "resources/shaders/shadow_depth_skinning.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x17" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("default_shadow", "resources/shaders/default_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x18" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("skinning_shadow", "resources/shaders/skinning_shadow.vertex", "resources/shaders/default_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x19" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("fog_shadow", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x20" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("fog_skinning_shadow", "resources/shaders/fog_skinning_shadow.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x21" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x22" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x23" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x24" << std::endl;
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
std::cout << "Load resurces step 12x25" << std::endl;
|
||||
|
||||
#else
|
||||
renderer.shaderManager.AddShaderFromFiles("env_sky", "resources/shaders/env_sky.vertex", "resources/shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "resources/shaders/defaultAtmosphere.vertex", "resources/shaders/defaultAtmosphere_desktop.fragment", CONST_ZIP_FILE);
|
||||
@ -232,9 +205,10 @@ namespace ZL
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||
|
||||
#endif
|
||||
});
|
||||
|
||||
loadSteps.push([this]() {
|
||||
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_aiperi.png", CONST_ZIP_FILE);
|
||||
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_ghost.png", CONST_ZIP_FILE);
|
||||
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_hero_neutral.png", CONST_ZIP_FILE);
|
||||
@ -243,15 +217,21 @@ namespace ZL
|
||||
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_student_boy.png", CONST_ZIP_FILE);
|
||||
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_student_girl.png", CONST_ZIP_FILE);
|
||||
renderer.textureManager.LoadFromPng("resources/dialogue/portrait_teacher.png", CONST_ZIP_FILE);
|
||||
});
|
||||
|
||||
std::cout << "Load resurces step 4" << std::endl;
|
||||
|
||||
loadSteps.push([this]() {
|
||||
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
|
||||
|
||||
Character::nameTranslations.loadFromFile("resources/config/translate_names.json");
|
||||
});
|
||||
|
||||
// createLocations() is also used by the full-reset path (see performFullReset()),
|
||||
// so it stays a single synchronous function rather than being split into
|
||||
// per-location queue steps; it's queued here as one coarse step.
|
||||
loadSteps.push([this]() {
|
||||
createLocations();
|
||||
});
|
||||
|
||||
loadSteps.push([this]() {
|
||||
// Wire inventory callbacks: tutorial tracking + toast notifications.
|
||||
gameState.inventory.onItemAdded = [this](const std::string& itemId) {
|
||||
menuManager.onItemPickedUp(itemId);
|
||||
@ -286,15 +266,14 @@ namespace ZL
|
||||
menuManager.skipCutsceneFunc = [this]() {
|
||||
if (currentLocation()) currentLocation()->dialogueSystem.skipCutscene();
|
||||
};
|
||||
});
|
||||
|
||||
std::cout << "Load resurces step 13" << std::endl;
|
||||
|
||||
loadSteps.push([this]() {
|
||||
try {
|
||||
menuManager.setup(gameState.inventory, CONST_ZIP_FILE);
|
||||
std::cout << "UI loaded successfully" << std::endl;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
|
||||
std::cerr << "Failed to load UI: " << e.what() << std::endl;
|
||||
}
|
||||
|
||||
@ -311,6 +290,21 @@ namespace ZL
|
||||
menuManager.onLoadGame = [this](int slot) { loadGame(slot); };
|
||||
menuManager.getSlotInfoFunc = [this](int slot) { return readSlotInfo(slot); };
|
||||
|
||||
// Lets MenuManager run a heavy operation (e.g. reloadLocalizedGameContent())
|
||||
// as a chunked sequence instead of one blocking call: the closures are
|
||||
// drained one per frame by update(), with the loading screen shown for
|
||||
// the duration, exactly like the initial resource load.
|
||||
menuManager.runAsLoadingSequence = [this](std::vector<std::function<void()>> steps) {
|
||||
for (auto& step : steps) loadSteps.push(std::move(step));
|
||||
loadSteps.push([this]() { loadingCompleted = true; });
|
||||
loadStepsTotal = loadSteps.size();
|
||||
loadStepsDone = 0;
|
||||
showLoadingProgressBar = false;
|
||||
loadingCompleted = false;
|
||||
};
|
||||
});
|
||||
|
||||
loadSteps.push([this]() {
|
||||
if (audioPlayer->init()) {
|
||||
audioPlayer->setMusicVolume(100);
|
||||
audioPlayer->setSoundVolume(80);
|
||||
@ -319,14 +313,19 @@ namespace ZL
|
||||
else {
|
||||
std::cout << "Audio initialization failed" << std::endl;
|
||||
}
|
||||
});
|
||||
|
||||
loadSteps.push([this]() {
|
||||
menuManager.loadSettings();
|
||||
|
||||
audioPlayer->crossFadeMusicAsync("audio/main menu final.ogg");
|
||||
});
|
||||
|
||||
loadSteps.push([this]() {
|
||||
loadingCompleted = true;
|
||||
});
|
||||
|
||||
|
||||
loadStepsTotal = loadSteps.size();
|
||||
loadStepsDone = 0;
|
||||
}
|
||||
|
||||
void Game::rearmDormOneShotCallbacks() {
|
||||
@ -860,8 +859,7 @@ namespace ZL
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
if (loadingProgressBarFrameTexture && loadingProgressBarTexture) {
|
||||
if (showLoadingProgressBar && loadingProgressBarFrameTexture && loadingProgressBarTexture) {
|
||||
const float W = Environment::projectionWidth;
|
||||
const float H = Environment::projectionHeight;
|
||||
static constexpr float kBarW = 400.0f;
|
||||
@ -891,7 +889,7 @@ namespace ZL
|
||||
}
|
||||
|
||||
// Rebuild fill mesh when progress changes
|
||||
const float p = std::min(resourcesDownloadProgress, 1.0f);
|
||||
const float p = (std::min)(currentLoadingProgress(), 1.0f);
|
||||
if (p != loadingBarLastRenderedProgress) {
|
||||
loadingBarLastRenderedProgress = p;
|
||||
if (p > 0.001f) {
|
||||
@ -932,7 +930,6 @@ namespace ZL
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
#endif
|
||||
|
||||
CheckGlError(__FILE__, __LINE__);
|
||||
}
|
||||
@ -1282,6 +1279,14 @@ namespace ZL
|
||||
|
||||
}
|
||||
}
|
||||
// Drain one loading step per frame so the window/tab keeps pumping events
|
||||
// and repainting between steps instead of freezing for the whole load.
|
||||
if (!loadingCompleted && !loadSteps.empty()) {
|
||||
loadSteps.front()();
|
||||
loadSteps.pop();
|
||||
loadStepsDone++;
|
||||
}
|
||||
|
||||
render();
|
||||
|
||||
mainThreadHandler.processMainThreadTasks();
|
||||
|
||||
25
src/Game.h
25
src/Game.h
@ -10,6 +10,7 @@
|
||||
#include "items/Item.h"
|
||||
#include "items/InteractiveObject.h"
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@ -49,6 +50,22 @@ namespace ZL {
|
||||
VertexRenderStruct loadingMesh;
|
||||
bool loadingCompleted = false;
|
||||
|
||||
// setupPart2() populates this queue with one closure per loading step instead of
|
||||
// running the work itself; Game::update() drains one step per frame so the window
|
||||
// keeps pumping events / repainting (and, on Emscripten, the browser keeps the tab
|
||||
// responsive) instead of freezing for the whole duration of loading.
|
||||
std::queue<std::function<void()>> loadSteps;
|
||||
size_t loadStepsTotal = 0;
|
||||
size_t loadStepsDone = 0;
|
||||
float currentLoadingProgress() const {
|
||||
return loadStepsTotal ? (float)loadStepsDone / (float)loadStepsTotal : resourcesDownloadProgress;
|
||||
}
|
||||
|
||||
// The progress bar only reflects the initial resource load (its steps are
|
||||
// pre-counted upfront). Later loadSteps runs, like MenuManager's localized
|
||||
// content reload, don't have a meaningful denominator, so the bar is hidden.
|
||||
bool showLoadingProgressBar = true;
|
||||
|
||||
GameState gameState;
|
||||
|
||||
EditorMode editorMode = EditorMode::None;
|
||||
@ -138,8 +155,13 @@ namespace ZL {
|
||||
static void onResourcesZipLoaded(unsigned handle, void* userData, const char* filename);
|
||||
static void onResourcesZipError(unsigned handle, void* userData, int httpStatus);
|
||||
static void onResourcesZipProgress(unsigned handle, void* userData, int percentComplete);
|
||||
|
||||
#endif
|
||||
// Only meaningful during the Emscripten zip-download phase, before loadSteps
|
||||
// exists (see currentLoadingProgress() above); harmless/unused on other platforms.
|
||||
float resourcesDownloadProgress = 0.0f;
|
||||
|
||||
// Progress bar assets/meshes, loaded and drawn on every platform so setupPart2's
|
||||
// step-by-step progress is visible during desktop/Android loading too.
|
||||
std::shared_ptr<Texture> loadingProgressBarTexture;
|
||||
std::shared_ptr<Texture> loadingProgressBarFrameTexture;
|
||||
VertexRenderStruct loadingProgressBarFrameMesh;
|
||||
@ -147,7 +169,6 @@ namespace ZL {
|
||||
float loadingBarLastRenderedProgress = -1.0f;
|
||||
float loadingBarLastW = -1.0f;
|
||||
float loadingBarLastH = -1.0f;
|
||||
#endif
|
||||
|
||||
// Screen-flash transition state (shared by darklands and night transitions)
|
||||
float darklandsFlashAlpha = 0.0f;
|
||||
|
||||
@ -88,9 +88,11 @@ namespace ZL
|
||||
|
||||
// Load static game objects
|
||||
gameObjects = GameObjectLoader::loadAndCreateGameObjects(params.gameObjectsJsonPath, renderer, CONST_ZIP_FILE);
|
||||
SDL_PumpEvents();
|
||||
|
||||
// Load interactive objects
|
||||
interactiveObjects = GameObjectLoader::loadAndCreateInteractiveObjects(params.interactiveObjectsJsonPath, renderer, CONST_ZIP_FILE);
|
||||
SDL_PumpEvents();
|
||||
|
||||
//auto playerTexture = std::make_shared<Texture>(CreateTextureDataFromPng("resources/w/gg/IMG_20260413_182354_992.png", CONST_ZIP_FILE));
|
||||
//auto playerTexture = renderer.textureManager.LoadFromPng("resources/w/gg/UniV_Grid_2K_Base_color.png", CONST_ZIP_FILE);
|
||||
@ -140,6 +142,7 @@ namespace ZL
|
||||
player->setupHitSparks(sparkTexture);
|
||||
player->audioPlayer = audioPlayer;
|
||||
std::cout << "Load resurces step 9" << std::endl;
|
||||
SDL_PumpEvents();
|
||||
|
||||
// Load NPCs from JSON. Aggressive (canAttack) NPCs need a player target
|
||||
// and hit sparks; peaceful NPCs don't take damage from the player so
|
||||
@ -155,6 +158,8 @@ namespace ZL
|
||||
}
|
||||
}
|
||||
|
||||
SDL_PumpEvents();
|
||||
|
||||
loadTeleportZones(params.teleportsJsonPath, CONST_ZIP_FILE);
|
||||
loadTriggerZones(params.triggerZonesJsonPath, CONST_ZIP_FILE);
|
||||
loadPointLights(params.lightsJsonPath, CONST_ZIP_FILE);
|
||||
@ -167,6 +172,7 @@ namespace ZL
|
||||
//#endif
|
||||
|
||||
setupNavigation(params.navigationJsonPaths);
|
||||
SDL_PumpEvents();
|
||||
|
||||
dialogueSystem.init(renderer, CONST_ZIP_FILE);
|
||||
dialogueSystem.loadDatabase(params.dialoguesJsonPath);
|
||||
@ -175,6 +181,8 @@ namespace ZL
|
||||
dialogueSystem.loadCutsceneTranslations("resources/dialogue/translate_cutscenes.json");
|
||||
dialogueSystem.setQuestJournal(journal);
|
||||
|
||||
SDL_PumpEvents();
|
||||
|
||||
npcNameText = std::make_unique<TextRenderer>();
|
||||
if (!npcNameText->init(renderer, "resources/fonts/DroidSans.ttf", 24, CONST_ZIP_FILE)) {
|
||||
std::cerr << "Failed to init NPC name TextRenderer" << std::endl;
|
||||
@ -405,6 +413,9 @@ namespace ZL
|
||||
navigationMaps.resize(paths.size());
|
||||
|
||||
for (size_t i = 0; i < paths.size(); ++i) {
|
||||
// Service the OS message queue between builds so the window doesn't
|
||||
// get flagged as "not responding" while loading many navigation grids.
|
||||
SDL_PumpEvents();
|
||||
navigationMaps[i].build(paths[i], CONST_ZIP_FILE);
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <SDL.h>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -119,6 +120,9 @@ namespace ZL {
|
||||
}
|
||||
|
||||
std::shared_ptr<UiNode> MenuManager::loadLocalizedUi(const std::string& basePath, const std::string& zipFile) {
|
||||
// Service the OS message queue between UI screens so the window doesn't
|
||||
// get flagged as "not responding" while loading the ~50 UI layout files.
|
||||
SDL_PumpEvents();
|
||||
static const std::string marker = "resources/w/ui/";
|
||||
if (basePath.rfind(marker, 0) == 0) {
|
||||
const std::string localizedPath = "resources/w/ui_" + languageToCode(g_currentLanguage) + "/" + basePath.substr(marker.size());
|
||||
@ -507,9 +511,6 @@ namespace ZL {
|
||||
}
|
||||
|
||||
void MenuManager::reloadLocalizedGameContent() {
|
||||
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
|
||||
gameState_.questJournal.reloadDefinitionsPreservingState("resources/config/quests.json", CONST_ZIP_FILE);
|
||||
|
||||
// Screens below the Language screen on the menu stack hold onto their old
|
||||
// (pre-reload) UiNode instances, so simply reopening Language on top of
|
||||
// them would leave Main Menu / Settings / the pause menu showing stale
|
||||
@ -525,6 +526,19 @@ namespace ZL {
|
||||
for (int i = 0; i < depth; ++i) uiManager.popMenu();
|
||||
}
|
||||
|
||||
// Unloading/reloading ~50 UI screens and their textures is heavy enough to
|
||||
// freeze the window for a noticeable moment, so run it the same way the
|
||||
// initial resource load runs: a queue of closures drained one per frame
|
||||
// with the loading screen shown, instead of one big blocking call here.
|
||||
std::vector<std::function<void()>> steps;
|
||||
|
||||
steps.push_back([this]() {
|
||||
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
|
||||
gameState_.questJournal.reloadDefinitionsPreservingState("resources/config/quests.json", CONST_ZIP_FILE);
|
||||
});
|
||||
|
||||
steps.push_back([this]() {
|
||||
renderer.textureManager.UnloadByPrefix("resources/w/ui");
|
||||
loadUiRoots(CONST_ZIP_FILE);
|
||||
|
||||
toastTextureQuestNew = loadLocalizedTexture("resources/w/ui/img/toast/quest_new001.png", CONST_ZIP_FILE);
|
||||
@ -532,9 +546,17 @@ namespace ZL {
|
||||
toastTextureQuestFailed = loadLocalizedTexture("resources/w/ui/img/toast/quest_failed001.png", CONST_ZIP_FILE);
|
||||
toastTextureItemAdded = loadLocalizedTexture("resources/w/ui/img/toast/item_received001.png", CONST_ZIP_FILE);
|
||||
toastTextureItemRemoved = loadLocalizedTexture("resources/w/ui/img/toast/item_removed001.png", CONST_ZIP_FILE);
|
||||
});
|
||||
|
||||
|
||||
steps.push_back([this]() {
|
||||
showMainMenu();
|
||||
});
|
||||
|
||||
if (runAsLoadingSequence) {
|
||||
runAsLoadingSequence(std::move(steps));
|
||||
} else {
|
||||
for (auto& step : steps) step();
|
||||
}
|
||||
}
|
||||
|
||||
void MenuManager::showLoadGameScreen()
|
||||
|
||||
@ -79,6 +79,13 @@ namespace ZL {
|
||||
std::function<void(int)> onLoadGame;
|
||||
std::function<SaveSlotInfo(int)> getSlotInfoFunc;
|
||||
|
||||
// Set by Game: runs the given closures one per frame with the loading
|
||||
// screen shown (same mechanism as the initial resource load), so a heavy
|
||||
// operation like reloadLocalizedGameContent() doesn't block the window
|
||||
// for its whole duration. Game re-enables normal rendering once all the
|
||||
// closures have run.
|
||||
std::function<void(std::vector<std::function<void()>>)> runAsLoadingSequence;
|
||||
|
||||
// Called when a chat message bubble should be shown (text + direction)
|
||||
void onChatBubbleReady(const std::string& text, bool incoming);
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "utils/Utils.h"
|
||||
#include <Eigen/Geometry>
|
||||
#include "../Character.h"
|
||||
#include <SDL.h>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
@ -195,6 +196,9 @@ namespace ZL {
|
||||
std::unordered_map<std::string, LoadedGameObject> gameObjects;
|
||||
|
||||
for (const auto& data : loadFromJson(jsonPath, zipPath)) {
|
||||
// Service the OS message queue between loads so the window doesn't
|
||||
// get flagged as "not responding" during long location loads.
|
||||
SDL_PumpEvents();
|
||||
std::cout << "Loading game object: " << data.name << std::endl;
|
||||
try {
|
||||
LoadedGameObject obj = buildLoadedObject(data, renderer, zipPath);
|
||||
@ -225,6 +229,7 @@ namespace ZL {
|
||||
return interactiveObjects;
|
||||
|
||||
for (const auto& data : loadInteractiveFromJson(jsonPath, zipPath)) {
|
||||
SDL_PumpEvents();
|
||||
std::cout << "Loading interactive object: " << data.base.name << std::endl;
|
||||
try {
|
||||
InteractiveObject intObj = InteractiveObject::createFromState(
|
||||
@ -359,6 +364,7 @@ namespace ZL {
|
||||
std::vector<std::unique_ptr<Character>> npcs;
|
||||
|
||||
for (const auto& npcData : loadNpcsFromJson(jsonPath, zipPath)) {
|
||||
SDL_PumpEvents();
|
||||
std::cout << "Loading NPC: " << npcData.name << std::endl;
|
||||
try {
|
||||
auto npc = Character::createFromState(npcData.toCharacterState(), renderer, zipPath.c_str());
|
||||
|
||||
@ -528,8 +528,20 @@ namespace ZL
|
||||
|
||||
void TextureManager::Unload(const std::string& fileName, const std::string& zipFile)
|
||||
{
|
||||
//For now, we should keep all textures in memory, to make game load faster after restart
|
||||
//textureMap.erase(MakeKey(fileName, zipFile));
|
||||
textureMap.erase(MakeKey(fileName, zipFile));
|
||||
}
|
||||
|
||||
void TextureManager::UnloadByPrefix(const std::string& prefix)
|
||||
{
|
||||
for (auto& it = textureMap.begin(); it != textureMap.end(); ) {
|
||||
if (it->first.substr(0, prefix.size()) == prefix) {
|
||||
std::cout << "Unloading texture with prefix: " << it->first << std::endl;
|
||||
it = textureMap.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextureManager::UnloadAll()
|
||||
|
||||
@ -76,6 +76,7 @@ namespace ZL
|
||||
#endif
|
||||
|
||||
void Unload(const std::string& fileName, const std::string& zipFile = "");
|
||||
void UnloadByPrefix(const std::string& prefix);
|
||||
void UnloadAll();
|
||||
|
||||
private:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user