370 lines
14 KiB
C++
370 lines
14 KiB
C++
#define _USE_MATH_DEFINES
|
|
#include "background.h"
|
|
|
|
#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_FRect, SDL_Creat...
|
|
|
|
#include <algorithm> // Para clamp, max
|
|
#include <cmath> // Para cos, sin, M_PI
|
|
#include <string> // 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<float>(gradients_texture_->getWidth() / 2), static_cast<float>(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<float>(top_clouds_texture_->getWidth()), TOP_CLOUDS_TEXTURE_HEIGHT};
|
|
bottom_clouds_rect_[i] = {0, i * BOTTOM_CLOUDS_TEXTURE_HEIGHT, static_cast<float>(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<MovingSprite>(top_clouds_texture_, (SDL_FRect){0, TOP_CLOUDS_Y, rect_.w, static_cast<float>(top_clouds_texture_->getHeight())});
|
|
top_clouds_sprite_b_ = std::make_unique<MovingSprite>(top_clouds_texture_, (SDL_FRect){rect_.w, TOP_CLOUDS_Y, rect_.w, static_cast<float>(top_clouds_texture_->getHeight())});
|
|
|
|
bottom_clouds_sprite_a_ = std::make_unique<MovingSprite>(bottom_clouds_texture_, (SDL_FRect){0, BOTTOM_CLOUDS_Y, rect_.w, static_cast<float>(bottom_clouds_texture_->getHeight())});
|
|
bottom_clouds_sprite_b_ = std::make_unique<MovingSprite>(bottom_clouds_texture_, (SDL_FRect){rect_.w, BOTTOM_CLOUDS_Y, rect_.w, static_cast<float>(bottom_clouds_texture_->getHeight())});
|
|
|
|
buildings_sprite_ = std::make_unique<Sprite>(buildings_texture_);
|
|
gradient_sprite_ = std::make_unique<Sprite>(gradients_texture_, 0, 0, rect_.w, rect_.h);
|
|
grass_sprite_ = std::make_unique<Sprite>(grass_texture_, 0, 0, grass_texture_->getWidth(), grass_texture_->getHeight() / 2);
|
|
sun_sprite_ = std::make_unique<Sprite>(sun_texture_);
|
|
moon_sprite_ = std::make_unique<Sprite>(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
|
|
constexpr double STEP = 0.01;
|
|
const int NUM_STEPS = static_cast<int>((M_PI - M_PI / 2) / STEP) + 1;
|
|
|
|
for (int i = 0; i < NUM_STEPS; ++i) {
|
|
double theta = M_PI / 2 + i * STEP;
|
|
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
|
|
constexpr double STEP = 0.01;
|
|
const int NUM_STEPS = static_cast<int>((M_PI / 2 - 0) / STEP) + 1;
|
|
|
|
for (int i = 0; i < NUM_STEPS; ++i) {
|
|
double theta = 0 + i * STEP;
|
|
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<size_t>(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<size_t>(progress * (moon_path_.size() - 1));
|
|
} |