càrrega de recursos no bloquejant

This commit is contained in:
2026-04-14 18:10:28 +02:00
parent d493ebf4f0
commit 8706b2c7fb
15 changed files with 356 additions and 118 deletions

View File

@@ -67,7 +67,8 @@ Resource::Resource(LoadingMode mode)
Screen::get()->show();
if (loading_mode_ == LoadingMode::PRELOAD) {
loading_text_ = Screen::get()->getText();
load();
// Ya NO llamamos load() aquí: Director bombea beginLoad() + loadStep()
// desde iterate() para mantener vivo el bucle SDL3 durante la carga.
} else {
// En modo lazy, cargamos lo mínimo indispensable
initResourceLists();
@@ -413,31 +414,128 @@ void Resource::clear() {
demos_.clear();
}
// Carga todos los recursos del juego y muestra el progreso de carga
// Carga síncrona completa: usado por Resource::reload() (hot-reload en debug).
// En arranque normal la carga la bombea Director::iterate() vía loadStep().
void Resource::load() {
// Prepara la gestión del progreso de carga
beginLoad();
while (!loadStep(INT_MAX)) {
// Presupuesto infinito: una sola pasada carga todo
}
Screen::get()->setVSync(saved_vsync_);
}
// Prepara el estado del cargador incremental. No carga nada todavía.
void Resource::beginLoad() {
calculateTotalResources();
initProgressBar();
// Muerstra la ventana y desactiva el sincronismo vertical
auto* screen = Screen::get();
auto vsync = Screen::getVSync();
screen->setVSync(false);
saved_vsync_ = Screen::getVSync();
Screen::get()->setVSync(false); // Maximiza FPS durante el preload
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** LOADING RESOURCES");
loadSounds(); // Carga sonidos
loadMusics(); // Carga músicas
loadTextures(); // Carga texturas
loadTextFiles(); // Carga ficheros de texto
loadAnimations(); // Carga animaciones
loadDemoData(); // Carga datos de demo
createText(); // Crea objetos de texto
createTextTextures(); // Crea texturas a partir de texto
createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** RESOURCES LOADED");
stage_ = LoadStage::SOUNDS;
stage_index_ = 0;
}
// Restablece el sincronismo vertical a su valor original
screen->setVSync(vsync);
auto Resource::isLoadDone() const -> bool {
return stage_ == LoadStage::DONE;
}
// Bombea la máquina de etapas hasta agotar el presupuesto de tiempo o completar la carga.
// Devuelve true cuando ya no queda nada por cargar.
auto Resource::loadStep(int budget_ms) -> bool {
if (stage_ == LoadStage::DONE) { return true; }
const Uint64 start_ns = SDL_GetTicksNS();
const Uint64 budget_ns = static_cast<Uint64>(budget_ms) * 1'000'000ULL;
while (stage_ != LoadStage::DONE) {
switch (stage_) {
case LoadStage::SOUNDS: {
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
if (stage_index_ == 0) { sounds_.clear(); }
if (stage_index_ >= list.size()) {
stage_ = LoadStage::MUSICS;
stage_index_ = 0;
break;
}
loadOneSound(stage_index_++);
break;
}
case LoadStage::MUSICS: {
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
if (stage_index_ == 0) { musics_.clear(); }
if (stage_index_ >= list.size()) {
stage_ = LoadStage::TEXTURES;
stage_index_ = 0;
break;
}
loadOneMusic(stage_index_++);
break;
}
case LoadStage::TEXTURES: {
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
if (stage_index_ == 0) { textures_.clear(); }
if (stage_index_ >= list.size()) {
stage_ = LoadStage::TEXT_FILES;
stage_index_ = 0;
break;
}
loadOneTexture(stage_index_++);
break;
}
case LoadStage::TEXT_FILES: {
auto list = Asset::get()->getListByType(Asset::Type::FONT);
if (stage_index_ == 0) { text_files_.clear(); }
if (stage_index_ >= list.size()) {
stage_ = LoadStage::ANIMATIONS;
stage_index_ = 0;
break;
}
loadOneTextFile(stage_index_++);
break;
}
case LoadStage::ANIMATIONS: {
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
if (stage_index_ == 0) { animations_.clear(); }
if (stage_index_ >= list.size()) {
stage_ = LoadStage::DEMO_DATA;
stage_index_ = 0;
break;
}
loadOneAnimation(stage_index_++);
break;
}
case LoadStage::DEMO_DATA: {
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
if (stage_index_ == 0) { demos_.clear(); }
if (stage_index_ >= list.size()) {
stage_ = LoadStage::CREATE_TEXT;
stage_index_ = 0;
break;
}
loadOneDemoData(stage_index_++);
break;
}
case LoadStage::CREATE_TEXT:
createText();
stage_ = LoadStage::CREATE_TEXT_TEXTURES;
break;
case LoadStage::CREATE_TEXT_TEXTURES:
createTextTextures();
stage_ = LoadStage::CREATE_PLAYER_TEXTURES;
break;
case LoadStage::CREATE_PLAYER_TEXTURES:
createPlayerTextures();
stage_ = LoadStage::DONE;
break;
case LoadStage::DONE:
break;
}
if ((SDL_GetTicksNS() - start_ns) >= budget_ns) { break; }
}
return stage_ == LoadStage::DONE;
}
// Recarga todos los recursos (limpia y vuelve a cargar)
@@ -450,96 +548,78 @@ void Resource::reload() {
}
}
// Carga los sonidos del juego
void Resource::loadSounds() {
// Carga un sonido concreto desde la lista de assets
void Resource::loadOneSound(size_t idx) {
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
sounds_.clear();
for (const auto& l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
auto audio_data = loadAudioData(l);
JA_Sound_t* sound = nullptr;
if (!audio_data.data.empty()) {
sound = JA_LoadSound(audio_data.data.data(), audio_data.data.size());
} else {
sound = JA_LoadSound(l.c_str());
}
if (sound == nullptr) {
std::cout << "Sound load failed: " << name << '\n';
}
sounds_.emplace_back(name, sound);
const auto& path = list[idx];
auto name = getFileName(path);
updateLoadingProgress(name);
auto audio_data = loadAudioData(path);
JA_Sound_t* sound = nullptr;
if (!audio_data.data.empty()) {
sound = JA_LoadSound(audio_data.data.data(), audio_data.data.size());
} else {
sound = JA_LoadSound(path.c_str());
}
if (sound == nullptr) {
std::cout << "Sound load failed: " << name << '\n';
}
sounds_.emplace_back(name, sound);
}
// Carga las músicas del juego
void Resource::loadMusics() {
// Carga una música concreta desde la lista de assets
void Resource::loadOneMusic(size_t idx) {
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
musics_.clear();
for (const auto& l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
auto audio_data = loadAudioData(l);
JA_Music_t* music = nullptr;
if (!audio_data.data.empty()) {
music = JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
} else {
music = JA_LoadMusic(l.c_str());
}
if (music == nullptr) {
std::cout << "Music load failed: " << name << '\n';
}
musics_.emplace_back(name, music);
const auto& path = list[idx];
auto name = getFileName(path);
updateLoadingProgress(name);
auto audio_data = loadAudioData(path);
JA_Music_t* music = nullptr;
if (!audio_data.data.empty()) {
music = JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
} else {
music = JA_LoadMusic(path.c_str());
}
if (music == nullptr) {
std::cout << "Music load failed: " << name << '\n';
}
musics_.emplace_back(name, music);
}
// Carga las texturas del juego
void Resource::loadTextures() {
// Carga una textura concreta desde la lista de assets
void Resource::loadOneTexture(size_t idx) {
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
textures_.clear();
for (const auto& l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
textures_.emplace_back(name, std::make_shared<Texture>(Screen::get()->getRenderer(), l));
}
const auto& path = list[idx];
auto name = getFileName(path);
updateLoadingProgress(name);
textures_.emplace_back(name, std::make_shared<Texture>(Screen::get()->getRenderer(), path));
}
// Carga los ficheros de texto del juego
void Resource::loadTextFiles() {
// Carga un fichero de texto concreto desde la lista de assets
void Resource::loadOneTextFile(size_t idx) {
auto list = Asset::get()->getListByType(Asset::Type::FONT);
text_files_.clear();
for (const auto& l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
text_files_.emplace_back(name, Text::loadFile(l));
}
const auto& path = list[idx];
auto name = getFileName(path);
updateLoadingProgress(name);
text_files_.emplace_back(name, Text::loadFile(path));
}
// Carga las animaciones del juego
void Resource::loadAnimations() {
// Carga una animación concreta desde la lista de assets
void Resource::loadOneAnimation(size_t idx) {
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
animations_.clear();
for (const auto& l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
animations_.emplace_back(name, loadAnimationsFromFile(l));
}
const auto& path = list[idx];
auto name = getFileName(path);
updateLoadingProgress(name);
animations_.emplace_back(name, loadAnimationsFromFile(path));
}
// Carga los datos para el modo demostración
void Resource::loadDemoData() {
// Carga un fichero de datos de demo concreto desde la lista de assets
void Resource::loadOneDemoData(size_t idx) {
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
demos_.clear();
for (const auto& l : list) {
auto name = getFileName(l);
updateLoadingProgress(name);
demos_.emplace_back(loadDemoDataFromFile(l));
}
const auto& path = list[idx];
auto name = getFileName(path);
updateLoadingProgress(name);
demos_.emplace_back(loadDemoDataFromFile(path));
}
// Crea las texturas de jugadores con todas sus variantes de paleta
@@ -756,12 +836,16 @@ void Resource::renderProgress() {
screen->start();
screen->clean();
// Si la pantalla de carga está desactivada, dejamos todo en negro
// Si la pantalla de carga está desactivada, dejamos todo en negro.
// wait_for_input solo tiene efecto cuando la pantalla está visible.
if (!Options::loading.show) {
screen->coreRender();
return;
}
// Estamos en la fase de espera explícita al usuario tras terminar la carga
const bool WAITING_FOR_INPUT = isLoadDone() && Options::loading.wait_for_input;
auto text_color = param.resource.color;
auto bar_color = param.resource.color.DARKEN(100);
const auto TEXT_HEIGHT = loading_text_->getCharacterSize();
@@ -774,13 +858,17 @@ void Resource::renderProgress() {
SDL_SetRenderDrawColor(renderer, bar_color.r, bar_color.g, bar_color.b, bar_color.a);
SDL_RenderRect(renderer, &loading_wired_rect_);
// Escribe el nombre del recurso que se está cargando, centrado sobre la barra
if (Options::loading.show_resource_name && !loading_resource_name_.empty()) {
// Texto centrado sobre la barra: mientras carga, el nombre del recurso;
// al terminar en modo wait_for_input, el prompt traducido.
const std::string OVER_BAR_TEXT = WAITING_FOR_INPUT
? Lang::getText("[RESOURCE] PRESS_TO_CONTINUE")
: loading_resource_name_;
if ((Options::loading.show_resource_name || WAITING_FOR_INPUT) && !OVER_BAR_TEXT.empty()) {
loading_text_->writeDX(
Text::CENTER | Text::COLOR,
loading_wired_rect_.x + (loading_wired_rect_.w / 2),
loading_wired_rect_.y - TEXT_HEIGHT - 2,
loading_resource_name_,
OVER_BAR_TEXT,
1,
text_color);
}
@@ -829,12 +917,12 @@ void Resource::initProgressBar() {
loading_full_rect_ = {.x = BAR_X_POSITION, .y = BAR_Y_POSITION, .w = FULL_BAR_WIDTH, .h = BAR_HEIGHT};
}
// Actualiza el progreso de carga y muestra la barra
// Actualiza el estado del progreso. No renderiza: el repintado lo hace
// Preload::iterate una vez por frame llamando a renderProgress().
void Resource::updateLoadingProgress(std::string name) {
loading_resource_name_ = std::move(name);
loading_count_.increase();
updateProgressBar();
renderProgress();
}
// Actualiza la barra de estado

View File

@@ -42,6 +42,15 @@ class Resource {
// --- Métodos de recarga de recursos ---
void reload(); // Recarga todos los recursos
// --- Cargador incremental ---
// beginLoad prepara el estado; loadStep carga recursos hasta agotar el presupuesto;
// devuelve true cuando ya no queda nada. renderProgress se llama una vez por frame
// desde la escena Preload.
void beginLoad();
auto loadStep(int budget_ms) -> bool;
[[nodiscard]] auto isLoadDone() const -> bool;
void renderProgress();
// --- Método para obtener el modo de carga actual ---
[[nodiscard]] auto getLoadingMode() const -> LoadingMode { return loading_mode_; }
@@ -146,13 +155,24 @@ class Resource {
SDL_FRect loading_wired_rect_;
SDL_FRect loading_full_rect_;
// --- Estado del cargador incremental ---
enum class LoadStage {
SOUNDS,
MUSICS,
TEXTURES,
TEXT_FILES,
ANIMATIONS,
DEMO_DATA,
CREATE_TEXT,
CREATE_TEXT_TEXTURES,
CREATE_PLAYER_TEXTURES,
DONE
};
LoadStage stage_{LoadStage::DONE};
size_t stage_index_{0};
bool saved_vsync_{false}; // Vsync previo a beginLoad, restaurado por finishBoot/load
// --- Métodos internos de carga y gestión ---
void loadSounds(); // Carga los sonidos
void loadMusics(); // Carga las músicas
void loadTextures(); // Carga las texturas
void loadTextFiles(); // Carga los ficheros de texto
void loadAnimations(); // Carga las animaciones
void loadDemoData(); // Carga los datos para el modo demostración
void loadDemoDataQuiet(); // Carga los datos de demo sin mostrar progreso (para modo lazy)
void loadEssentialResources(); // Carga recursos esenciales en modo lazy
void loadEssentialTextures(); // Carga solo las texturas esenciales (fuentes)
@@ -176,11 +196,18 @@ class Resource {
// --- Métodos internos para gestionar el progreso ---
void calculateTotalResources(); // Calcula el número de recursos para cargar
void renderProgress(); // Muestra el progreso de carga
void updateLoadingProgress(std::string name); // Actualiza el progreso de carga
void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso
void updateProgressBar(); // Actualiza la barra de estado
// --- Helpers del cargador incremental (cargan un único recurso) ---
void loadOneSound(size_t idx);
void loadOneMusic(size_t idx);
void loadOneTexture(size_t idx);
void loadOneTextFile(size_t idx);
void loadOneAnimation(size_t idx);
void loadOneDemoData(size_t idx);
// --- Constructores y destructor privados (singleton) ---
explicit Resource(LoadingMode mode); // Constructor privado con modo de carga
~Resource(); // Destructor privado