#include "tiled_bg.h" #include // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_FRect, SDL_GetRenderTarget, SDL_RenderTexture, SDL_PixelFormat, SDL_TextureAccess #include #include // Para sin, pow #include // Para rand #include // Para allocator, unique_ptr, make_unique #include // Para pi #include "resource.h" // Para Resource #include "screen.h" // Para Screen #include "sprite.h" // Para Sprite // Constructor TiledBG::TiledBG(SDL_FRect pos, TiledBGMode mode) : renderer_(Screen::get()->getRenderer()), pos_(pos), mode_(mode == TiledBGMode::RANDOM ? static_cast(rand() % 2) : mode) { // Crea la textura para el mosaico de fondo canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, pos_.w * 2, pos_.h * 2); // Rellena la textura con el contenido fillTexture(); // Inicializa variables switch (mode_) { case TiledBGMode::STATIC: window_ = {.x = 0, .y = 0, .w = pos_.w, .h = pos_.h}; speed_ = 0.0F; break; case TiledBGMode::DIAGONAL: window_ = {.x = 0, .y = 0, .w = pos_.w, .h = pos_.h}; break; case TiledBGMode::CIRCLE: window_ = {.x = 128, .y = 128, .w = pos_.w, .h = pos_.h}; break; default: window_ = {.x = 0, .y = 0, .w = pos_.w, .h = pos_.h}; break; } } // Destructor TiledBG::~TiledBG() { SDL_DestroyTexture(canvas_); } // Rellena la textura con el contenido void TiledBG::fillTexture() { // Crea los objetos para pintar en la textura de fondo auto tile = std::make_unique(Resource::get()->getTexture("title_bg_tile.png"), (SDL_FRect){0, 0, TILE_WIDTH, TILE_HEIGHT}); // Prepara para dibujar sobre la textura auto *temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, canvas_); // Rellena la textura con el tile const auto I_MAX = pos_.w * 2 / TILE_WIDTH; const auto J_MAX = pos_.h * 2 / TILE_HEIGHT; tile->setSpriteClip(0, 0, TILE_WIDTH, TILE_HEIGHT); for (int i = 0; i < I_MAX; ++i) { for (int j = 0; j < J_MAX; ++j) { tile->setX(i * TILE_WIDTH); tile->setY(j * TILE_HEIGHT); tile->render(); } } // Vuelve a colocar el renderizador como estaba SDL_SetRenderTarget(renderer_, temp); } // Pinta la clase en pantalla void TiledBG::render() { SDL_RenderTexture(renderer_, canvas_, &window_, &pos_); } // Actualiza la lógica de la clase (time-based) void TiledBG::update(float delta_time) { updateSpeedChange(delta_time); updateDesp(delta_time); updateStop(delta_time); switch (mode_) { case TiledBGMode::DIAGONAL: { // El tileado de fondo se desplaza en diagonal window_.x = static_cast(desp_) % TILE_WIDTH; window_.y = static_cast(desp_) % TILE_HEIGHT; break; } case TiledBGMode::CIRCLE: { // El tileado de fondo se desplaza en circulo const float angle_rad = (desp_ * std::numbers::pi / 180.0F); window_.x = 128 + static_cast(std::cos(angle_rad) * 128); window_.y = 128 + static_cast(std::sin(-angle_rad) * 96); break; } default: break; } } // Detiene el desplazamiento de forma ordenada (time-based) void TiledBG::updateStop(float delta_time) { if (stopping_) { const int UMBRAL = STOP_THRESHOLD_FACTOR * speed_; // Desacelerar si estamos cerca de completar el ciclo (ventana a punto de regresar a 0) if (window_.x >= TILE_WIDTH - UMBRAL) { // Aplicar desaceleración time-based float frame_rate = 60.0F; float deceleration_per_ms = std::pow(DECELERATION_FACTOR, frame_rate * delta_time / 1000.0F); speed_ /= deceleration_per_ms; // Asegura que no baje demasiado speed_ = std::max(speed_, MIN_SPEED); } // Si estamos en 0, detener if (window_.x == 0) { speed_ = 0.0F; stopping_ = false; // Desactivamos el estado de "stopping" } } } // Cambia la velocidad gradualmente en X segundos void TiledBG::changeSpeedTo(float target_speed, float duration_s) { if (duration_s <= 0.0f) { // Si la duración es 0 o negativa, cambia inmediatamente speed_ = target_speed; changing_speed_ = false; return; } // Configurar el cambio gradual changing_speed_ = true; initial_speed_ = speed_; target_speed_ = target_speed; change_duration_s_ = duration_s; change_timer_s_ = 0.0f; } // Actualiza el cambio gradual de velocidad (time-based) void TiledBG::updateSpeedChange(float delta_time) { if (!changing_speed_) { return; } change_timer_s_ += delta_time; if (change_timer_s_ >= change_duration_s_) { // Cambio completado speed_ = target_speed_; changing_speed_ = false; } else { // Interpolación lineal entre velocidad inicial y objetivo float progress = change_timer_s_ / change_duration_s_; speed_ = initial_speed_ + (target_speed_ - initial_speed_) * progress; } }