time-based: Balloon amb dual-API update/move/state/animation/bounce(dt_s), vels/gravetat en px/s i px/s2

This commit is contained in:
2026-05-19 17:17:56 +02:00
parent 93af6dd58d
commit 91c5b9d2b2
2 changed files with 300 additions and 84 deletions
+240 -46
View File
@@ -1,6 +1,7 @@
#include "game/entities/balloon.h"
#include <cmath> // for std::fabs
#include <algorithm> // for std::max
#include <cmath> // for std::fabs
#include "core/rendering/animatedsprite.h" // for AnimatedSprite
#include "core/rendering/movingsprite.h" // for MovingSprite
@@ -23,13 +24,20 @@ Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 c
size_ = SIZE_1;
power_ = 1;
// Inicializa los valores de velocidad y gravedad
// Inicializa los valores de velocidad y gravedad (frame-based)
this->vel_x_ = velx;
vel_y_ = 0;
max_vel_y_ = 3.0F;
gravity_ = 0.09F;
default_vel_y_ = 2.6F;
// Time-based equivalents (x60 per vel, x3600 per gravetat)
vel_x_s_ = velx * 60.0F;
vel_y_s_ = 0.0F;
max_vel_y_s_ = 180.0F;
gravity_s_ = 324.0F;
default_vel_y_s_ = 156.0F;
// Puntos que da el globo al ser destruido
score_ = SCORE_1;
@@ -39,173 +47,177 @@ Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 c
break;
case BALLOON_2:
// Alto y ancho del objeto
width_ = WIDTH_2;
height_ = WIDTH_2;
size_ = SIZE_2;
power_ = 3;
// Inicializa los valores de velocidad y gravedad
this->vel_x_ = velx;
vel_y_ = 0;
max_vel_y_ = 3.0F;
gravity_ = 0.10F;
default_vel_y_ = 3.5F;
// Puntos que da el globo al ser destruido
vel_x_s_ = velx * 60.0F;
vel_y_s_ = 0.0F;
max_vel_y_s_ = 180.0F;
gravity_s_ = 360.0F;
default_vel_y_s_ = 210.0F;
score_ = SCORE_2;
// Amenaza que genera el globo
menace_ = 2;
break;
case BALLOON_3:
// Alto y ancho del objeto
width_ = WIDTH_3;
height_ = WIDTH_3;
size_ = SIZE_3;
power_ = 7;
// Inicializa los valores de velocidad y gravedad
this->vel_x_ = velx;
vel_y_ = 0;
max_vel_y_ = 3.0F;
gravity_ = 0.10F;
default_vel_y_ = 4.50F;
// Puntos que da el globo al ser destruido
vel_x_s_ = velx * 60.0F;
vel_y_s_ = 0.0F;
max_vel_y_s_ = 180.0F;
gravity_s_ = 360.0F;
default_vel_y_s_ = 270.0F;
score_ = SCORE_3;
// Amenaza que genera el globo
menace_ = 4;
break;
case BALLOON_4:
// Alto y ancho del objeto
width_ = WIDTH_4;
height_ = WIDTH_4;
size_ = SIZE_4;
power_ = 15;
// Inicializa los valores de velocidad y gravedad
this->vel_x_ = velx;
vel_y_ = 0;
max_vel_y_ = 3.0F;
gravity_ = 0.10F;
default_vel_y_ = 4.95F;
// Puntos que da el globo al ser destruido
vel_x_s_ = velx * 60.0F;
vel_y_s_ = 0.0F;
max_vel_y_s_ = 180.0F;
gravity_s_ = 360.0F;
default_vel_y_s_ = 297.0F;
score_ = SCORE_4;
// Amenaza que genera el globo
menace_ = 8;
break;
case HEXAGON_1:
// Alto y ancho del objeto
width_ = WIDTH_1;
height_ = WIDTH_1;
size_ = SIZE_1;
power_ = 1;
// Inicializa los valores de velocidad y gravedad
this->vel_x_ = velx;
vel_y_ = std::fabs(velx) * 2;
max_vel_y_ = std::fabs(velx) * 2;
gravity_ = 0.00F;
default_vel_y_ = std::fabs(velx) * 2;
// Puntos que da el globo al ser destruido
vel_x_s_ = velx * 60.0F;
vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
max_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
gravity_s_ = 0.0F;
default_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
score_ = SCORE_1;
// Amenaza que genera el globo
menace_ = 1;
break;
case HEXAGON_2:
// Alto y ancho del objeto
width_ = WIDTH_2;
height_ = WIDTH_2;
size_ = SIZE_2;
power_ = 3;
// Inicializa los valores de velocidad y gravedad
this->vel_x_ = velx;
vel_y_ = std::fabs(velx) * 2;
max_vel_y_ = std::fabs(velx) * 2;
gravity_ = 0.00F;
default_vel_y_ = std::fabs(velx) * 2;
// Puntos que da el globo al ser destruido
vel_x_s_ = velx * 60.0F;
vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
max_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
gravity_s_ = 0.0F;
default_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
score_ = SCORE_2;
// Amenaza que genera el globo
menace_ = 2;
break;
case HEXAGON_3:
// Alto y ancho del objeto
width_ = WIDTH_3;
height_ = WIDTH_3;
size_ = SIZE_3;
power_ = 7;
// Inicializa los valores de velocidad y gravedad
this->vel_x_ = velx;
vel_y_ = std::fabs(velx) * 2;
max_vel_y_ = std::fabs(velx) * 2;
gravity_ = 0.00F;
default_vel_y_ = std::fabs(velx) * 2;
// Puntos que da el globo al ser destruido
vel_x_s_ = velx * 60.0F;
vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
max_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
gravity_s_ = 0.0F;
default_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
score_ = SCORE_3;
// Amenaza que genera el globo
menace_ = 4;
break;
case HEXAGON_4:
// Alto y ancho del objeto
width_ = WIDTH_4;
height_ = WIDTH_4;
size_ = SIZE_4;
power_ = 15;
// Inicializa los valores de velocidad y gravedad
this->vel_x_ = velx;
vel_y_ = std::fabs(velx) * 2;
max_vel_y_ = std::fabs(velx) * 2;
gravity_ = 0.00F;
default_vel_y_ = std::fabs(velx) * 2;
// Puntos que da el globo al ser destruido
vel_x_s_ = velx * 60.0F;
vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
max_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
gravity_s_ = 0.0F;
default_vel_y_s_ = std::fabs(velx) * 2.0F * 60.0F;
score_ = SCORE_4;
// Amenaza que genera el globo
menace_ = 8;
break;
case POWER_BALL:
// Alto y ancho del objeto
width_ = WIDTH_4;
height_ = WIDTH_4;
size_ = 4;
power_ = 0;
// Inicializa los valores de velocidad y gravedad
this->vel_x_ = velx;
vel_y_ = 0;
max_vel_y_ = 3.0F;
gravity_ = 0.10F;
default_vel_y_ = 4.95F;
vel_x_s_ = velx * 60.0F;
vel_y_s_ = 0.0F;
max_vel_y_s_ = 180.0F;
gravity_s_ = 360.0F;
default_vel_y_s_ = 297.0F;
// Puntos que da el globo al ser destruido
score_ = 0;
@@ -257,10 +269,15 @@ Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 c
// Inicializa variables
stopped_ = true;
stopped_counter_ = 0;
stopped_counter_s_ = 0.0F;
blinking_ = false;
visible_ = true;
creation_counter_ = creationtimer;
creation_counter_ini_ = creationtimer;
creation_counter_s_ = static_cast<float>(creationtimer) / 60.0F;
creation_counter_ini_s_ = creation_counter_s_;
creation_phase_s_ = 0.0F;
bounce_phase_s_ = 0.0F;
popping_ = false;
// Valores iniciales dependentes del timer
@@ -420,6 +437,49 @@ void Balloon::move() {
}
}
// Actualiza la posición y estados del globo (time-based).
// Reemplaça l'acumulador `travel_y_` per integració contínua: gravetat i
// posició s'apliquen escalades per `speed_` (tempo del joc) cada tick,
// equivalent en mitjana a l'original però sense l'efecte chunky.
void Balloon::move(float dt_s) {
if (!isStopped()) {
// Eix X
pos_x_ += vel_x_s_ * speed_ * dt_s;
if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ + width_ > PLAY_AREA_RIGHT)) {
pos_x_ -= vel_x_s_ * speed_ * dt_s;
vel_x_s_ = -vel_x_s_;
vel_x_ = -vel_x_; // mantenir frame-based sincronitzat (UB-free per a sprite->switchRotate)
sprite_->switchRotate();
if (kind_ != POWER_BALL) { bounceStart(); }
}
// Eix Y
pos_y_ += vel_y_s_ * speed_ * dt_s;
if (pos_y_ < PLAY_AREA_TOP) {
pos_y_ = PLAY_AREA_TOP;
vel_y_s_ = -vel_y_s_;
vel_y_ = -vel_y_;
if (kind_ != POWER_BALL) { bounceStart(); }
}
if (pos_y_ + height_ > PLAY_AREA_BOTTOM) {
pos_y_ = PLAY_AREA_BOTTOM - height_;
vel_y_s_ = -default_vel_y_s_;
vel_y_ = -default_vel_y_;
if (kind_ != POWER_BALL) { bounceStart(); }
}
// Gravetat contínua (el tempo `speed_` escala també la gravetat per
// mantenir el mateix patró de rebot que la versió frame-based).
vel_y_s_ += gravity_s_ * speed_ * dt_s;
sprite_->setPosX(getPosX());
sprite_->setPosY(getPosY());
}
}
// Deshabilita el globo y pone a cero todos los valores
void Balloon::disable() {
being_created_ = false;
@@ -430,13 +490,20 @@ void Balloon::disable() {
counter_ = 0;
creation_counter_ = 0;
creation_counter_ini_ = 0;
creation_counter_s_ = 0.0F;
creation_counter_ini_s_ = 0.0F;
creation_phase_s_ = 0.0F;
bounce_phase_s_ = 0.0F;
default_vel_y_ = 0.0F;
default_vel_y_s_ = 0.0F;
enabled_ = false;
gravity_ = 0.0F;
gravity_s_ = 0.0F;
height_ = 0;
invulnerable_ = false;
kind_ = 0;
max_vel_y_ = 0.0F;
max_vel_y_s_ = 0.0F;
menace_ = 0;
popping_ = false;
pos_x_ = 0.0F;
@@ -447,9 +514,12 @@ void Balloon::disable() {
speed_ = 0;
stopped_ = false;
stopped_counter_ = 0;
stopped_counter_s_ = 0.0F;
travel_y_ = 0;
vel_x_ = 0.0F;
vel_y_ = 0.0F;
vel_x_s_ = 0.0F;
vel_y_s_ = 0.0F;
visible_ = false;
width_ = 0;
sprite_->clear();
@@ -478,6 +548,20 @@ void Balloon::update() {
}
}
// Actualiza al globo (time-based)
void Balloon::update(float dt_s) {
if (enabled_) {
// MovingSprite::update(dt_s) avança la rotació (entre altres). La posició
// del sprite la posa move(dt_s) directament des de pos_x_/pos_y_.
sprite_->MovingSprite::update(dt_s);
move(dt_s);
updateAnimation(dt_s);
updateColliders();
updateState(dt_s);
updateBounce(dt_s);
}
}
// Actualiza los estados del globo
void Balloon::updateState() {
if (isPopping()) {
@@ -493,6 +577,19 @@ void Balloon::updateState() {
}
}
// Actualiza los estados del globo (time-based)
void Balloon::updateState(float dt_s) {
if (isPopping()) {
updateStatePopping();
}
if (isBeingCreated()) {
updateStateBeingCreated(dt_s);
} else if (isStopped()) {
updateStateStopped(dt_s);
}
}
// Rama de updateState: globo explotando
void Balloon::updateStatePopping() {
setInvulnerable(true);
@@ -540,6 +637,40 @@ void Balloon::updateStateBeingCreated() {
}
}
// Rama de updateState: globo creándose (time-based). Manté el chunk pattern
// original: cada CREATION_STEP_S s'aplica un step de drift.
void Balloon::updateStateBeingCreated(float dt_s) {
setStop(true);
setInvulnerable(true);
if (creation_counter_s_ > 0.0F) {
creation_phase_s_ += dt_s;
while (creation_phase_s_ >= CREATION_STEP_S) {
creation_phase_s_ -= CREATION_STEP_S;
pos_y_ += 1.0F;
pos_x_ += vel_x_; // drift idèntic a l'original (vel_x_ en px/frame)
if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ > (PLAY_AREA_RIGHT - width_))) {
pos_x_ -= vel_x_;
vel_x_ = -vel_x_;
vel_x_s_ = -vel_x_s_;
}
sprite_->setPosX(getPosX());
sprite_->setPosY(getPosY());
updateColliders();
}
creation_counter_s_ = std::max(0.0F, creation_counter_s_ - dt_s);
creation_counter_ = static_cast<Uint16>(creation_counter_s_ * 60.0F);
} else {
setBeingCreated(false);
setStop(false);
setVisible(true);
setInvulnerable(false);
}
}
// Rama de updateState: globo detenido (no creándose)
void Balloon::updateStateStopped() {
// Reduce el contador
@@ -552,6 +683,16 @@ void Balloon::updateStateStopped() {
}
}
// Rama de updateState: globo detenido (time-based)
void Balloon::updateStateStopped(float dt_s) {
if (stopped_counter_s_ > 0.0F) {
stopped_counter_s_ = std::max(0.0F, stopped_counter_s_ - dt_s);
stopped_counter_ = static_cast<Uint16>(stopped_counter_s_ * 60.0F);
} else if (!isPopping()) {
setStop(false);
}
}
// Establece la animación correspondiente al estado
void Balloon::updateAnimation() {
std::string creating_animation = "blue";
@@ -577,6 +718,30 @@ void Balloon::updateAnimation() {
sprite_->animate();
}
// Establece la animación correspondiente (time-based)
void Balloon::updateAnimation(float dt_s) {
std::string creating_animation = "blue";
std::string normal_animation = "orange";
if (kind_ == POWER_BALL) {
creating_animation = "powerball";
normal_animation = "powerball";
} else if (getClass() == HEXAGON_CLASS) {
creating_animation = "red";
normal_animation = "green";
}
if (isPopping()) {
sprite_->setCurrentAnimation("pop");
} else if (isBeingCreated()) {
sprite_->setCurrentAnimation(creating_animation);
} else {
sprite_->setCurrentAnimation(normal_animation);
}
sprite_->animate(dt_s);
}
// Comprueba si el globo está habilitado
auto Balloon::isEnabled() const -> bool {
return enabled_;
@@ -610,6 +775,7 @@ auto Balloon::getHeight() const -> int {
// Establece el valor de la variable
void Balloon::setVelY(float vel_y) {
this->vel_y_ = vel_y;
this->vel_y_s_ = vel_y * 60.0F;
}
// Establece el valor de la variable
@@ -709,6 +875,7 @@ auto Balloon::isPopping() const -> bool {
// Establece el valor de la variable
void Balloon::setStoppedTimer(Uint16 time) {
stopped_counter_ = time;
stopped_counter_s_ = static_cast<float>(time) / 60.0F;
}
// Obtiene del valor de la variable
@@ -779,4 +946,31 @@ void Balloon::updateBounce() {
bounceStop();
}
}
}
// Aplica l'efecte de rebote (time-based). Manté la mateixa cadència: cada
// `bouncing_.speed` "frames" (a 60Hz) avança un slot del vector w/h.
void Balloon::updateBounce(float dt_s) {
if (!bouncing_.enabled) { return; }
bounce_phase_s_ += dt_s;
const float STEP_S = static_cast<float>(bouncing_.speed) * BOUNCE_STEP_S;
while (bounce_phase_s_ >= STEP_S) {
bounce_phase_s_ -= STEP_S;
bouncing_.counter++;
}
const int IDX = bouncing_.counter / bouncing_.speed;
if (IDX > (MAX_BOUNCE - 1)) {
bounceStop();
bounce_phase_s_ = 0.0F;
return;
}
bouncing_.zoom_width = bouncing_.w[IDX];
bouncing_.zoom_height = bouncing_.h[IDX];
sprite_->setZoomW(bouncing_.zoom_width);
sprite_->setZoomH(bouncing_.zoom_height);
bouncing_.desp_x = (sprite_->getSpriteClip().w - (sprite_->getSpriteClip().w * bouncing_.zoom_width));
bouncing_.desp_y = (sprite_->getSpriteClip().h - (sprite_->getSpriteClip().h * bouncing_.zoom_height));
}