Files

165 lines
5.2 KiB
C++

#include "tiled_bg.h"
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_DestroyTexture, SDL_FRect, SDL_GetRenderTarget, SDL_RenderTexture, SDL_PixelFormat, SDL_TextureAccess
#include <algorithm>
#include <cmath> // Para sin, pow
#include <cstdlib> // Para rand
#include <memory> // Para allocator, unique_ptr, make_unique
#include <numbers> // 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<TiledBGMode>(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<Sprite>(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<int>(desp_) % TILE_WIDTH;
window_.y = static_cast<int>(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<int>(std::cos(angle_rad) * 128);
window_.y = 128 + static_cast<int>(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;
}
}