#include "background.h" #include // Para SDL_BLENDMODE_BLEND #include // Para SDL_PIXELFORMAT_RGBA8888 #include // Para clamp, max #include #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_({0, 0, gradients_texture_->getWidth() / 2, 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_alpha), alpha_color_text_temp_(param.background.attenuate_alpha) { // 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 int top_clouds_texture_height = top_clouds_texture_->getHeight() / 4; const int 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, top_clouds_texture_->getWidth(), top_clouds_texture_height}; bottom_clouds_rect_[i] = {0, i * bottom_clouds_texture_height, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_height}; } } // Crea los sprites { const int top_clouds_y = base_ - 165; const int bottom_clouds_y = base_ - 101; top_clouds_sprite_a_ = std::make_unique(top_clouds_texture_, (SDL_Rect){0, top_clouds_y, rect_.w, top_clouds_texture_->getHeight()}); top_clouds_sprite_b_ = std::make_unique(top_clouds_texture_, (SDL_Rect){rect_.w, top_clouds_y, rect_.w, top_clouds_texture_->getHeight()}); bottom_clouds_sprite_a_ = std::make_unique(bottom_clouds_texture_, (SDL_Rect){0, bottom_clouds_y, rect_.w, bottom_clouds_texture_->getHeight()}); bottom_clouds_sprite_b_ = std::make_unique(bottom_clouds_texture_, (SDL_Rect){rect_.w, bottom_clouds_y, rect_.w, 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_ updateAlphaColorText(); // 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_RenderCopy(renderer_, canvas_, &src_rect_, &dst_rect_); // Atenuación SDL_RenderCopy(renderer_, color_texture_, &src_rect_, &dst_rect_); } // Vuelve a cargar las texturas void Background::reloadTextures() { buildings_texture_->reLoad(); top_clouds_texture_->reLoad(); bottom_clouds_texture_->reLoad(); grass_texture_->reLoad(); gradients_texture_->reLoad(); } // 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_Rect 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::updateAlphaColorText() { 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 int OFFSET_X = 94; // Desplazamiento en la textura del sol hasta el sol constexpr int OFFSET_Y = 48; // Desplazamiento en la textura del sol hasta el sol constexpr int CENTER_X = 270; const int center_y = base_ - 30; constexpr int RADIUS = 130; const int EXTRA_PIXELS = 30; // Píxeles adicionales para la línea recta // Generar puntos de la curva desde 90 a 180 grados for (double theta = M_PI / 2; theta <= M_PI; theta += 0.01) { int x = CENTER_X + static_cast(RADIUS * cos(theta)); int y = center_y - static_cast(RADIUS * sin(theta)); // Nota: y está invertido en la pantalla sun_path_.push_back({x - OFFSET_X, y - OFFSET_Y}); } // Agregar puntos en línea recta después de la curva SDL_Point 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 int CENTER_X = 100; const int center_y = base_ - 50; constexpr int RADIUS = 140; // Generar puntos de la curva desde 0 a 90 grados for (double theta = 0; theta <= M_PI / 2; theta += 0.01) { int x = CENTER_X + static_cast(RADIUS * cos(theta)); int y = center_y - static_cast(RADIUS * sin(theta)); // Nota: y está invertido en la pantalla 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)); }