#define _USE_MATH_DEFINES #include "background.h" #include // Para SDL_SetRenderTarget, SDL_FRect, SDL_Creat... #include // Para clamp, max #include // Para cos, sin, M_PI #include // Para basic_string #include "moving_sprite.h" // Para MovingSprite #include "param.h" // Para Param, ParamBackground, param #include "resource.h" // Para Resource #include "screen.h" // Para Screen #include "sprite.h" // Para Sprite #include "texture.h" // Para Texture // Constructor Background::Background() : renderer_(Screen::get()->getRenderer()), buildings_texture_(Resource::get()->getTexture("game_buildings.png")), top_clouds_texture_(Resource::get()->getTexture("game_clouds1.png")), bottom_clouds_texture_(Resource::get()->getTexture("game_clouds2.png")), grass_texture_(Resource::get()->getTexture("game_grass.png")), gradients_texture_(Resource::get()->getTexture("game_sky_colors.png")), sun_texture_(Resource::get()->getTexture("game_sun.png")), moon_texture_(Resource::get()->getTexture("game_moon.png")), rect_(SDL_FRect{0, 0, static_cast(gradients_texture_->getWidth() / 2), static_cast(gradients_texture_->getHeight() / 2)}), src_rect_({0, 0, 320, 240}), dst_rect_({0, 0, 320, 240}), base_(rect_.h), attenuate_color_(Color(param.background.attenuate_color.r, param.background.attenuate_color.g, param.background.attenuate_color.b)), alpha_color_text_(param.background.attenuate_color.a), alpha_color_text_temp_(param.background.attenuate_color.a) { // Precalcula rutas createSunPath(); createMoonPath(); // Inicializa variables { gradient_rect_[0] = {0, 0, rect_.w, rect_.h}; gradient_rect_[1] = {rect_.w, 0, rect_.w, rect_.h}; gradient_rect_[2] = {0, rect_.h, rect_.w, rect_.h}; gradient_rect_[3] = {rect_.w, rect_.h, rect_.w, rect_.h}; const float TOP_CLOUDS_TEXTURE_HEIGHT = top_clouds_texture_->getHeight() / 4; const float BOTTOM_CLOUDS_TEXTURE_HEIGHT = bottom_clouds_texture_->getHeight() / 4; for (int i = 0; i < 4; ++i) { top_clouds_rect_[i] = {0, i * TOP_CLOUDS_TEXTURE_HEIGHT, static_cast(top_clouds_texture_->getWidth()), TOP_CLOUDS_TEXTURE_HEIGHT}; bottom_clouds_rect_[i] = {0, i * BOTTOM_CLOUDS_TEXTURE_HEIGHT, static_cast(bottom_clouds_texture_->getWidth()), BOTTOM_CLOUDS_TEXTURE_HEIGHT}; } } // Crea los sprites { const float TOP_CLOUDS_Y = base_ - 165; const float BOTTOM_CLOUDS_Y = base_ - 101; top_clouds_sprite_a_ = std::make_unique(top_clouds_texture_, (SDL_FRect){0, TOP_CLOUDS_Y, rect_.w, static_cast(top_clouds_texture_->getHeight())}); top_clouds_sprite_b_ = std::make_unique(top_clouds_texture_, (SDL_FRect){rect_.w, TOP_CLOUDS_Y, rect_.w, static_cast(top_clouds_texture_->getHeight())}); bottom_clouds_sprite_a_ = std::make_unique(bottom_clouds_texture_, (SDL_FRect){0, BOTTOM_CLOUDS_Y, rect_.w, static_cast(bottom_clouds_texture_->getHeight())}); bottom_clouds_sprite_b_ = std::make_unique(bottom_clouds_texture_, (SDL_FRect){rect_.w, BOTTOM_CLOUDS_Y, rect_.w, static_cast(bottom_clouds_texture_->getHeight())}); buildings_sprite_ = std::make_unique(buildings_texture_); gradient_sprite_ = std::make_unique(gradients_texture_, 0, 0, rect_.w, rect_.h); grass_sprite_ = std::make_unique(grass_texture_, 0, 0, grass_texture_->getWidth(), grass_texture_->getHeight() / 2); sun_sprite_ = std::make_unique(sun_texture_); moon_sprite_ = std::make_unique(moon_texture_); } // Inicializa objetos { constexpr float TOP_CLOUDS_SPEED = 0.1f; constexpr float BOTTOM_CLOUDS_SPEED = 0.05f; top_clouds_sprite_a_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight()); top_clouds_sprite_a_->setVelX(-TOP_CLOUDS_SPEED); top_clouds_sprite_b_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight()); top_clouds_sprite_b_->setVelX(-TOP_CLOUDS_SPEED); bottom_clouds_sprite_a_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight()); bottom_clouds_sprite_a_->setVelX(-BOTTOM_CLOUDS_SPEED); bottom_clouds_sprite_b_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight()); bottom_clouds_sprite_b_->setVelX(-BOTTOM_CLOUDS_SPEED); buildings_sprite_->setY(base_ - buildings_sprite_->getHeight()); grass_sprite_->setY(base_ - grass_sprite_->getHeight()); sun_sprite_->setPosition(sun_path_.front()); moon_sprite_->setPosition(moon_path_.front()); } // Crea la textura para componer el fondo canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h); SDL_SetTextureBlendMode(canvas_, SDL_BLENDMODE_BLEND); // Crea la textura para atenuar el fondo color_texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h); SDL_SetTextureBlendMode(color_texture_, SDL_BLENDMODE_BLEND); setColor(attenuate_color_); SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_); } // Destructor Background::~Background() { SDL_DestroyTexture(canvas_); SDL_DestroyTexture(color_texture_); } // Actualiza la lógica del objeto void Background::update() { // Actualiza el valor de alpha_ updateAlphaColorTexture(); // Actualiza las nubes updateClouds(); // Calcula el frame de la hierba grass_sprite_->setSpriteClip(0, (10 * (counter_ / 20 % 2)), 320, 10); // Calcula el valor de alpha_ alpha_ = std::max((255 - (int)(255 * transition_)), 0); // Mueve el sol sun_sprite_->setPosition(sun_path_.at(sun_index_)); moon_sprite_->setPosition(moon_path_.at(moon_index_)); // Incrementa el contador ++counter_; // Compone todos los elementos del fondo en la textura fillCanvas(); } // Dibuja el gradiente de fondo void Background::renderGradient() { // Dibuja el gradiente de detras gradients_texture_->setAlpha(255); gradient_sprite_->setSpriteClip(gradient_rect_[(gradient_number_ + 1) % 4]); gradient_sprite_->render(); // Dibuja el gradiente de delante con una opacidad cada vez menor gradients_texture_->setAlpha(alpha_); gradient_sprite_->setSpriteClip(gradient_rect_[gradient_number_]); gradient_sprite_->render(); } // Dibuja las nubes de arriba void Background::renderTopClouds() { // Dibuja el primer conjunto de nubes, las de detras top_clouds_texture_->setAlpha(255); top_clouds_sprite_a_->setSpriteClip(top_clouds_rect_[(gradient_number_ + 1) % 4]); top_clouds_sprite_b_->setSpriteClip(top_clouds_rect_[(gradient_number_ + 1) % 4]); top_clouds_sprite_a_->render(); top_clouds_sprite_b_->render(); // Dibuja el segundo conjunto de nubes, las de delante top_clouds_texture_->setAlpha(alpha_); top_clouds_sprite_a_->setSpriteClip(top_clouds_rect_[gradient_number_]); top_clouds_sprite_b_->setSpriteClip(top_clouds_rect_[gradient_number_]); top_clouds_sprite_a_->render(); top_clouds_sprite_b_->render(); } // Dibuja las nubes de abajo void Background::renderBottomClouds() { // Dibuja el primer conjunto de nubes, las de detras bottom_clouds_texture_->setAlpha(255); bottom_clouds_sprite_a_->setSpriteClip(bottom_clouds_rect_[(gradient_number_ + 1) % 4]); bottom_clouds_sprite_b_->setSpriteClip(bottom_clouds_rect_[(gradient_number_ + 1) % 4]); bottom_clouds_sprite_a_->render(); bottom_clouds_sprite_b_->render(); // Dibuja el segundo conjunto de nubes, las de delante bottom_clouds_texture_->setAlpha(alpha_); bottom_clouds_sprite_a_->setSpriteClip(bottom_clouds_rect_[gradient_number_]); bottom_clouds_sprite_b_->setSpriteClip(bottom_clouds_rect_[gradient_number_]); bottom_clouds_sprite_a_->render(); bottom_clouds_sprite_b_->render(); } // Compone todos los elementos del fondo en la textura void Background::fillCanvas() { // Cambia el destino del renderizador auto temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, canvas_); // Dibuja el gradiente de fondo renderGradient(); // Dibuja los astros sun_sprite_->render(); moon_sprite_->render(); // Dibuja las nubes de arriba renderTopClouds(); // Dibuja las nubes de abajo renderBottomClouds(); // Dibuja los edificios buildings_sprite_->render(); // Dibuja la hierba grass_sprite_->render(); // Deja el renderizador apuntando donde estaba SDL_SetRenderTarget(renderer_, temp); } // Dibuja el objeto void Background::render() { // Fondo SDL_RenderTexture(renderer_, canvas_, &src_rect_, &dst_rect_); // Atenuación SDL_RenderTexture(renderer_, color_texture_, &src_rect_, &dst_rect_); } // Ajusta el valor de la variable void Background::setCloudsSpeed(float value) { clouds_speed_ = value; } // Ajusta el valor de la variable void Background::setGradientNumber(int value) { gradient_number_ = value % 4; } // Ajusta el valor de la variable void Background::setTransition(float value) { transition_ = std::clamp(value, 0.0f, 1.0f); } // Establece la posición del objeto void Background::setPos(SDL_FRect pos) { dst_rect_ = pos; // Si cambian las medidas del destino, hay que cambiar las del origen para evitar deformar la imagen src_rect_.x = 0; src_rect_.y = rect_.h - pos.h; src_rect_.w = pos.w; src_rect_.h = pos.h; } // Establece el color_ de atenuación void Background::setColor(Color color) { attenuate_color_ = color; // Colorea la textura auto temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, color_texture_); SDL_SetRenderDrawColor(renderer_, attenuate_color_.r, attenuate_color_.g, attenuate_color_.b, 255); SDL_RenderClear(renderer_); SDL_SetRenderTarget(renderer_, temp); } // Establece la transparencia de la atenuación void Background::setAlpha(int alpha) { // Evita que se asignen valores fuera de rango alpha_ = std::clamp(alpha, 0, 255); // Guarda el valor actual y establece el nuevo valor alpha_color_text_temp_ = alpha_color_text_; alpha_color_text_ = alpha_; } // Actualiza el valor de alpha_ void Background::updateAlphaColorTexture() { if (alpha_color_text_ == alpha_color_text_temp_) { return; } else { alpha_color_text_ > alpha_color_text_temp_ ? ++alpha_color_text_temp_ : --alpha_color_text_temp_; SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_temp_); } } // Actualiza las nubes void Background::updateClouds() { // Aplica la velocidad calculada a las nubes top_clouds_sprite_a_->setVelX(clouds_speed_); top_clouds_sprite_b_->setVelX(clouds_speed_); bottom_clouds_sprite_a_->setVelX(clouds_speed_ / 2); bottom_clouds_sprite_b_->setVelX(clouds_speed_ / 2); // Mueve las nubes top_clouds_sprite_a_->update(); top_clouds_sprite_b_->update(); bottom_clouds_sprite_a_->update(); bottom_clouds_sprite_b_->update(); // Calcula el offset de las nubes if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth()) { top_clouds_sprite_a_->setPosX(top_clouds_sprite_a_->getWidth()); } if (top_clouds_sprite_b_->getPosX() < -top_clouds_sprite_b_->getWidth()) { top_clouds_sprite_b_->setPosX(top_clouds_sprite_b_->getWidth()); } if (bottom_clouds_sprite_a_->getPosX() < -bottom_clouds_sprite_a_->getWidth()) { bottom_clouds_sprite_a_->setPosX(bottom_clouds_sprite_a_->getWidth()); } if (bottom_clouds_sprite_b_->getPosX() < -bottom_clouds_sprite_b_->getWidth()) { bottom_clouds_sprite_b_->setPosX(bottom_clouds_sprite_b_->getWidth()); } } // Precalcula el vector con el recorrido del sol void Background::createSunPath() { constexpr float CENTER_X = 170; const float CENTER_Y = base_ - 80; constexpr float RADIUS = 120; // Generar puntos de la curva desde 90 a 180 grados for (double theta = M_PI / 2; theta <= M_PI; theta += 0.01) { float x = CENTER_X + (RADIUS * cos(theta)); float y = CENTER_Y - (RADIUS * sin(theta)); sun_path_.push_back({x, y}); } // Agregar puntos en línea recta después de la curva constexpr int EXTRA_PIXELS = 40; SDL_FPoint last_point = sun_path_.back(); for (int i = 1; i <= EXTRA_PIXELS; ++i) { sun_path_.push_back({last_point.x, last_point.y + i}); } } // Precalcula el vector con el recorrido de la luna void Background::createMoonPath() { constexpr float CENTER_X = 100; const float CENTER_Y = base_ - 50; constexpr float RADIUS = 140; // Generar puntos de la curva desde 0 a 90 grados for (double theta = 0; theta <= M_PI / 2; theta += 0.01) { float x = CENTER_X + (RADIUS * cos(theta)); float y = CENTER_Y - (RADIUS * sin(theta)); moon_path_.push_back({x, y}); } } // Establece la posición del sol void Background::setSunProgression(float progress) { progress = std::clamp(progress, 0.0f, 1.0f); sun_index_ = static_cast(progress * (sun_path_.size() - 1)); } // Establece la posición de la luna void Background::setMoonProgression(float progress) { progress = std::clamp(progress, 0.0f, 1.0f); moon_index_ = static_cast(progress * (moon_path_.size() - 1)); }