#include "background.h" #include // for SDL_BLENDMODE_BLEND #include // for SDL_PIXELFORMAT_RGBA8888 #include // for max, min #include // for basic_string #include "asset.h" // for Asset #include "param.h" // for param // Constructor Background::Background(SDL_Renderer *renderer) : renderer_(renderer), buildings_texture_(std::make_shared(renderer, Asset::get()->get("game_buildings.png"))), top_clouds_texture_(std::make_shared(renderer, Asset::get()->get("game_clouds1.png"))), bottom_clouds_texture_(std::make_shared(renderer, Asset::get()->get("game_clouds2.png"))), grass_texture_(std::make_shared(renderer, Asset::get()->get("game_grass.png"))), gradients_texture_(std::make_shared(renderer, Asset::get()->get("game_sky_colors.png"))) { // Inicializa variables gradient_number_ = 0; alpha_ = 0; clouds_speed_ = 0; transition_ = 0; counter_ = 0; 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; color_ = {param.background.attenuate_color.r, param.background.attenuate_color.g, param.background.attenuate_color.b}; alpha_color_text_ = alpha_color_text_temp_ = param.background.attenuate_alpha; 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; constexpr float top_clouds_speed = 0.1f; constexpr float bottom_clouds_speed = 0.05f; top_clouds_sprite_a_ = std::make_unique(0, top_clouds_y, rect_.w, top_clouds_texture_->getHeight(), -top_clouds_speed, 0.0f, 0.0f, 0.0f, top_clouds_texture_); top_clouds_sprite_b_ = std::make_unique(rect_.w, top_clouds_y, rect_.w, top_clouds_texture_->getHeight(), -top_clouds_speed, 0.0f, 0.0f, 0.0f, top_clouds_texture_); bottom_clouds_sprite_a_ = std::make_unique(0, bottom_clouds_y, rect_.w, bottom_clouds_texture_->getHeight(), -bottom_clouds_speed, 0.0f, 0.0f, 0.0f, bottom_clouds_texture_); bottom_clouds_sprite_b_ = std::make_unique(rect_.w, bottom_clouds_y, rect_.w, bottom_clouds_texture_->getHeight(), -bottom_clouds_speed, 0.0f, 0.0f, 0.0f, bottom_clouds_texture_); buildings_sprite_ = std::make_unique(0, 0, buildings_texture_->getWidth(), buildings_texture_->getHeight(), buildings_texture_); gradient_sprite_ = std::make_unique(0, 0, rect_.w, rect_.h, gradients_texture_); grass_sprite_ = std::make_unique(0, 0, grass_texture_->getWidth(), grass_texture_->getHeight() / 2, grass_texture_); // Inicializa objetos top_clouds_sprite_a_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight()); top_clouds_sprite_b_->setSpriteClip(0, 0, top_clouds_texture_->getWidth(), top_clouds_texture_->getHeight()); bottom_clouds_sprite_a_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight()); bottom_clouds_sprite_b_->setSpriteClip(0, 0, bottom_clouds_texture_->getWidth(), bottom_clouds_texture_->getHeight()); buildings_sprite_->setPosY(base_ - buildings_sprite_->getHeight()); grass_sprite_->setPosY(base_ - grass_sprite_->getHeight()); // 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(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); // 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 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; } // Ajusta el valor de la variable void Background::setSrcRect(SDL_Rect value) { src_rect_ = value; } // Ajusta el valor de la variable void Background::setDstRect(SDL_Rect value) { dst_rect_ = value; } // Establece el color_ de atenuación void Background::setColor(Color color) { color_ = color; // Colorea la textura auto temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, color_texture_); SDL_SetRenderDrawColor(renderer_, color_.r, color_.g, 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_->move(); top_clouds_sprite_b_->move(); bottom_clouds_sprite_a_->move(); bottom_clouds_sprite_b_->move(); // 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()); } }