time-based: migrada escena Intro (dual-API a MovingSprite/SmartSprite/Writer, constants a 60Hz)

This commit is contained in:
2026-05-18 22:46:41 +02:00
parent f1a6636222
commit 2b57bfa4dd
8 changed files with 218 additions and 90 deletions
+64 -43
View File
@@ -12,9 +12,29 @@
#include "core/rendering/smartsprite.h" // for SmartSprite
#include "core/rendering/writer.h" // for Writer
#include "core/resources/resource.h"
#include "core/system/delta_time.hpp"
#include "game/defaults.hpp" // for GAMECANVAS_CENTER_X, GAMECANVAS_FIRST_QU...
#include "utils/utils.h" // for Section, Color
// ===========================================================================
// Time-based. Tots els valors precalculats: velocitats en px/s, acceleracions
// en px/s^2, durades en segons. La cadència real de la versió frame-based era
// empíricament ~60 Hz (el gate `> 15 ms` esperava al següent múltiple del
// refresh del SO, típicament 16.67 ms). Per això la conversió és:
// vel px/tick → vel * 60 = px/s
// acc px/tick² → acc * 3600 = px/s²
// counter frames → counter/60 = segons
// ===========================================================================
namespace {
// Durades comunes (segons). Arrodonides amunt respecte a la conversió
// exacta des de frames per a tenir valors més "bonics" i una mica més
// de respir visual.
constexpr float BITMAP_REMAINING_TIME_S = 0.5F; // de 0.333 (20/60) ⇒ 0.5
constexpr float BITMAP_FALLING_REMAINING_TIME_S = 5.0F; // de 4.167 (250/60) ⇒ 5.0
constexpr float TEXT_REMAINING_TIME_S = 3.0F; // 180/60 ⇒ ja és exacte
} // namespace
// Constructor
Intro::Intro(SDL_Renderer *renderer, Section *section) {
// Copia los punteros
@@ -30,8 +50,6 @@ Intro::Intro(SDL_Renderer *renderer, Section *section) {
// Inicializa variables
section->name = SECTION_PROG_INTRO;
section->subsection = 0;
ticks_ = 0;
ticks_speed_ = 15;
scene_ = 1;
// Inicializa los bitmaps de la intro
@@ -40,62 +58,69 @@ Intro::Intro(SDL_Renderer *renderer, Section *section) {
auto *ss = new SmartSprite(texture_, renderer);
ss->setWidth(128);
ss->setHeight(96);
ss->setEnabledCounter(20);
ss->setRemainingTime(BITMAP_REMAINING_TIME_S);
ss->setDestX(GAMECANVAS_CENTER_X - 64);
ss->setDestY(GAMECANVAS_FIRST_QUARTER_Y - 24);
bitmaps_.push_back(ss);
}
// bitmap 0: entra des de l'esquerra, accelerant cap a la dreta
bitmaps_[0]->setPosX(-128);
bitmaps_[0]->setPosY(GAMECANVAS_FIRST_QUARTER_Y - 24);
bitmaps_[0]->setVelX(0.0F);
bitmaps_[0]->setVelY(0.0F);
bitmaps_[0]->setAccelX(0.6F);
bitmaps_[0]->setAccelX(2160.0F); // 0.6 px/tick² ⇒ 0.6 * 3600 px/s²
bitmaps_[0]->setAccelY(0.0F);
bitmaps_[0]->setSpriteClip(0, 0, 128, 96);
// bitmap 1: entra des de la dreta amb velocitat negativa i accelera més
bitmaps_[1]->setPosX(GAMECANVAS_WIDTH);
bitmaps_[1]->setPosY(GAMECANVAS_FIRST_QUARTER_Y - 24);
bitmaps_[1]->setVelX(-1.0F);
bitmaps_[1]->setVelX(-60.0F); // -1 px/tick ⇒ -60 px/s
bitmaps_[1]->setVelY(0.0F);
bitmaps_[1]->setAccelX(-0.3F);
bitmaps_[1]->setAccelX(-1080.0F); // -0.3 px/tick² ⇒ -1080 px/s²
bitmaps_[1]->setAccelY(0.0F);
bitmaps_[1]->setSpriteClip(128, 0, 128, 96);
// bitmap 2: cau des de dalt; queda visible més temps (escena "GRITO")
bitmaps_[2]->setPosX(GAMECANVAS_CENTER_X - 64);
bitmaps_[2]->setPosY(-96);
bitmaps_[2]->setVelX(0.0F);
bitmaps_[2]->setVelY(3.0F);
bitmaps_[2]->setAccelX(0.1F);
bitmaps_[2]->setAccelY(0.3F);
bitmaps_[2]->setVelY(180.0F); // 3 px/tick ⇒ 180 px/s
bitmaps_[2]->setAccelX(360.0F); // 0.1 px/tick² ⇒ 360 px/s²
bitmaps_[2]->setAccelY(1080.0F); // 0.3 px/tick² ⇒ 1080 px/s²
bitmaps_[2]->setSpriteClip(0, 96, 128, 96);
bitmaps_[2]->setEnabledCounter(250);
bitmaps_[2]->setRemainingTime(BITMAP_FALLING_REMAINING_TIME_S);
// bitmap 3: puja lentament des de baix (reflexió)
bitmaps_[3]->setPosX(GAMECANVAS_CENTER_X - 64);
bitmaps_[3]->setPosY(GAMECANVAS_HEIGHT);
bitmaps_[3]->setVelX(0.0F);
bitmaps_[3]->setVelY(-0.7F);
bitmaps_[3]->setVelY(-42.0F); // -0.7 px/tick ⇒ -42 px/s
bitmaps_[3]->setAccelX(0.0F);
bitmaps_[3]->setAccelY(0.0F);
bitmaps_[3]->setSpriteClip(128, 96, 128, 96);
// bitmap 4: cau des de dalt (mateix que bitmap 2, sense temps allargat)
bitmaps_[4]->setPosX(GAMECANVAS_CENTER_X - 64);
bitmaps_[4]->setPosY(-96);
bitmaps_[4]->setVelX(0.0F);
bitmaps_[4]->setVelY(3.0F);
bitmaps_[4]->setAccelX(0.1F);
bitmaps_[4]->setAccelY(0.3F);
bitmaps_[4]->setVelY(180.0F);
bitmaps_[4]->setAccelX(360.0F);
bitmaps_[4]->setAccelY(1080.0F);
bitmaps_[4]->setSpriteClip(0, 192, 128, 96);
// bitmap 5: entra des de la dreta lentament
bitmaps_[5]->setPosX(GAMECANVAS_WIDTH);
bitmaps_[5]->setPosY(GAMECANVAS_FIRST_QUARTER_Y - 24);
bitmaps_[5]->setVelX(-0.7F);
bitmaps_[5]->setVelX(-42.0F); // -0.7 px/tick ⇒ -42 px/s
bitmaps_[5]->setVelY(0.0F);
bitmaps_[5]->setAccelX(0.0F);
bitmaps_[5]->setAccelY(0.0F);
bitmaps_[5]->setSpriteClip(128, 192, 128, 96);
// Inicializa los textos de la intro
// Inicializa los textos de la intro. Time-based: setSecondsPerChar.
// Conversió: frames_per_char / 60 = segons_per_char.
const int TOTAL_TEXTS = 9;
for (int i = 0; i < TOTAL_TEXTS; ++i) {
auto *w = new Writer(text_);
@@ -103,51 +128,53 @@ Intro::Intro(SDL_Renderer *renderer, Section *section) {
w->setPosY(GAMECANVAS_HEIGHT - (BLOCK * 6));
w->setKerning(-1);
w->setEnabled(false);
w->setEnabledCounter(180);
w->setRemainingTime(TEXT_REMAINING_TIME_S);
texts_.push_back(w);
}
// Un dia qualsevol de l'any 2000
texts_[0]->setCaption(Lang::get()->getText(27));
texts_[0]->setSpeed(8);
texts_[0]->setSecondsPerChar(0.15F); // de 0.1333 (8/60) ⇒ 0.15
// Tot esta tranquil a la UPV
texts_[1]->setCaption(Lang::get()->getText(28));
texts_[1]->setSpeed(8);
texts_[1]->setSecondsPerChar(0.15F);
// Fins que un desaprensiu...
texts_[2]->setCaption(Lang::get()->getText(29));
texts_[2]->setSpeed(12);
texts_[2]->setSecondsPerChar(0.2F); // 12/60 ⇒ ja és 0.2
// HEY! ME ANE A FERME UN CORTAET...
texts_[3]->setCaption(Lang::get()->getText(30));
texts_[3]->setSpeed(8);
texts_[3]->setSecondsPerChar(0.15F);
// UAAAAAAAAAAAAA!!!
texts_[4]->setCaption(Lang::get()->getText(31));
texts_[4]->setSpeed(1);
texts_[4]->setSecondsPerChar(0.02F); // de 0.0167 (1/60) ⇒ 0.02
// Espera un moment...
texts_[5]->setCaption(Lang::get()->getText(32));
texts_[5]->setSpeed(16);
texts_[5]->setSecondsPerChar(0.3F); // de 0.2667 (16/60) ⇒ 0.3
// Si resulta que no tinc solt!
texts_[6]->setCaption(Lang::get()->getText(33));
texts_[6]->setSpeed(2);
texts_[6]->setSecondsPerChar(0.05F); // de 0.0333 (2/60) ⇒ 0.05
// MERDA DE MAQUINA!
texts_[7]->setCaption(Lang::get()->getText(34));
texts_[7]->setSpeed(3);
texts_[7]->setSecondsPerChar(0.05F); // 3/60 ⇒ ja és 0.05
// Blop... blop... blop...
texts_[8]->setCaption(Lang::get()->getText(35));
texts_[8]->setSpeed(16);
texts_[8]->setSecondsPerChar(0.3F);
for (auto *t : texts_) {
t->center(GAMECANVAS_CENTER_X);
}
Audio::get()->playMusic(music_, 0);
DeltaTime::reset();
}
// Destructor
@@ -332,26 +359,19 @@ void Intro::updateScene6() {
}
// Actualiza las variables del objeto
void Intro::update() {
void Intro::update(float dt_s) {
Audio::update();
checkInput();
if (SDL_GetTicks() - ticks_ > ticks_speed_) {
// Actualiza el contador de ticks
ticks_ = SDL_GetTicks();
// Actualiza los objetos
for (auto *bitmap : bitmaps_) {
bitmap->update();
}
for (auto *t : texts_) {
t->update();
}
// Actualiza las escenas de la intro
updateScenes();
for (auto *bitmap : bitmaps_) {
bitmap->update(dt_s);
}
for (auto *t : texts_) {
t->update(dt_s);
}
updateScenes();
}
// Dibuja el objeto en pantalla
@@ -386,7 +406,8 @@ void Intro::run() {
// Ejecuta un frame
void Intro::iterate() {
update();
const float DELTA_TIME_S = DeltaTime::tick();
update(DELTA_TIME_S);
render();
}