time-based: migrada escena Title (AnimatedSprite/Fade amb dual-API, counters a acumuladors)
This commit is contained in:
@@ -8,88 +8,95 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// Normalitza CRLF: fitxers .ani amb terminadors de Windows fan que
|
||||
// line == "[animation]" no faci match i el parser entri en bucle
|
||||
// infinit / no carregui cap animació.
|
||||
void stripCr(std::string &s) {
|
||||
if (!s.empty() && s.back() == '\r') {
|
||||
s.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void parseFramesList(const std::string &value, Animation &buffer, int frame_width, int frame_height, int frames_per_row, int max_tiles) {
|
||||
std::stringstream ss(value);
|
||||
std::string tmp;
|
||||
SDL_Rect rect = {0, 0, frame_width, frame_height};
|
||||
while (getline(ss, tmp, ',')) {
|
||||
const int NUM_TILE = std::stoi(tmp) > max_tiles ? 0 : std::stoi(tmp);
|
||||
rect.x = (NUM_TILE % frames_per_row) * frame_width;
|
||||
rect.y = (NUM_TILE / frames_per_row) * frame_height;
|
||||
buffer.frames.push_back(rect);
|
||||
}
|
||||
}
|
||||
|
||||
void parseAnimationField(const std::string &line, int pos, Animation &buffer, int frame_width, int frame_height, int frames_per_row, int max_tiles, const std::string &filename) {
|
||||
const std::string KEY = line.substr(0, pos);
|
||||
const std::string VALUE = line.substr(pos + 1, line.length());
|
||||
if (KEY == "name") {
|
||||
buffer.name = VALUE;
|
||||
} else if (KEY == "speed") {
|
||||
buffer.speed = std::stoi(VALUE);
|
||||
} else if (KEY == "loop") {
|
||||
buffer.loop = std::stoi(VALUE);
|
||||
} else if (KEY == "frames") {
|
||||
parseFramesList(VALUE, buffer, frame_width, frame_height, frames_per_row, max_tiles);
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << KEY.c_str() << "\"" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
auto parseAnimationBlock(std::istream &file, int frame_width, int frame_height, int frames_per_row, int max_tiles, const std::string &filename) -> Animation {
|
||||
Animation buffer;
|
||||
buffer.speed = 0;
|
||||
buffer.loop = -1;
|
||||
buffer.counter = 0;
|
||||
buffer.current_frame = 0;
|
||||
buffer.completed = false;
|
||||
|
||||
std::string line;
|
||||
do {
|
||||
if (!std::getline(file, line)) {
|
||||
break;
|
||||
// Normalitza CRLF: fitxers .ani amb terminadors de Windows fan que
|
||||
// line == "[animation]" no faci match i el parser entri en bucle
|
||||
// infinit / no carregui cap animació.
|
||||
void stripCr(std::string &s) {
|
||||
if (!s.empty() && s.back() == '\r') {
|
||||
s.pop_back();
|
||||
}
|
||||
stripCr(line);
|
||||
int pos = line.find('=');
|
||||
if (pos != (int)std::string::npos) {
|
||||
parseAnimationField(line, pos, buffer, frame_width, frame_height, frames_per_row, max_tiles, filename);
|
||||
}
|
||||
|
||||
void parseFramesList(const std::string &value, Animation &buffer, int frame_width, int frame_height, int frames_per_row, int max_tiles) {
|
||||
std::stringstream ss(value);
|
||||
std::string tmp;
|
||||
SDL_Rect rect = {0, 0, frame_width, frame_height};
|
||||
while (getline(ss, tmp, ',')) {
|
||||
const int NUM_TILE = std::stoi(tmp) > max_tiles ? 0 : std::stoi(tmp);
|
||||
rect.x = (NUM_TILE % frames_per_row) * frame_width;
|
||||
rect.y = (NUM_TILE / frames_per_row) * frame_height;
|
||||
buffer.frames.push_back(rect);
|
||||
}
|
||||
} while (line != "[/animation]");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void parseGlobalField(const std::string &line, int pos, int &frames_per_row, int &frame_width, int &frame_height, int &max_tiles, const Texture *texture, const std::string &filename) {
|
||||
const std::string KEY = line.substr(0, pos);
|
||||
const std::string VALUE = line.substr(pos + 1, line.length());
|
||||
if (KEY == "framesPerRow") {
|
||||
frames_per_row = std::stoi(VALUE);
|
||||
} else if (KEY == "frameWidth") {
|
||||
frame_width = std::stoi(VALUE);
|
||||
} else if (KEY == "frameHeight") {
|
||||
frame_height = std::stoi(VALUE);
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << KEY.c_str() << "\"" << '\n';
|
||||
}
|
||||
|
||||
if (frames_per_row == 0 && frame_width > 0) {
|
||||
frames_per_row = texture->getWidth() / frame_width;
|
||||
void parseAnimationField(const std::string &line, int pos, Animation &buffer, int frame_width, int frame_height, int frames_per_row, int max_tiles, const std::string &filename) {
|
||||
const std::string KEY = line.substr(0, pos);
|
||||
const std::string VALUE = line.substr(pos + 1, line.length());
|
||||
if (KEY == "name") {
|
||||
buffer.name = VALUE;
|
||||
} else if (KEY == "speed") {
|
||||
buffer.speed = std::stoi(VALUE);
|
||||
// Time-based: el valor del .ani s'expressa en "ticks per frame
|
||||
// d'animació" (assumint 60 Hz). El camp `speed` (int) es manté per al
|
||||
// fallback frame-based; el nou `step_duration_s` (float) és el que
|
||||
// gasta animate(dt).
|
||||
buffer.step_duration_s = static_cast<float>(buffer.speed) / 60.0F;
|
||||
} else if (KEY == "loop") {
|
||||
buffer.loop = std::stoi(VALUE);
|
||||
} else if (KEY == "frames") {
|
||||
parseFramesList(VALUE, buffer, frame_width, frame_height, frames_per_row, max_tiles);
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << KEY.c_str() << "\"" << '\n';
|
||||
}
|
||||
}
|
||||
if (max_tiles == 0 && frame_width > 0 && frame_height > 0) {
|
||||
const int W = texture->getWidth() / frame_width;
|
||||
const int H = texture->getHeight() / frame_height;
|
||||
max_tiles = W * H;
|
||||
|
||||
auto parseAnimationBlock(std::istream &file, int frame_width, int frame_height, int frames_per_row, int max_tiles, const std::string &filename) -> Animation {
|
||||
Animation buffer;
|
||||
buffer.speed = 0;
|
||||
buffer.step_duration_s = 0.0F;
|
||||
buffer.loop = -1;
|
||||
buffer.counter = 0;
|
||||
buffer.current_frame = 0;
|
||||
buffer.completed = false;
|
||||
buffer.time_accumulator_s = 0.0F;
|
||||
|
||||
std::string line;
|
||||
do {
|
||||
if (!std::getline(file, line)) {
|
||||
break;
|
||||
}
|
||||
stripCr(line);
|
||||
int pos = line.find('=');
|
||||
if (pos != (int)std::string::npos) {
|
||||
parseAnimationField(line, pos, buffer, frame_width, frame_height, frames_per_row, max_tiles, filename);
|
||||
}
|
||||
} while (line != "[/animation]");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void parseGlobalField(const std::string &line, int pos, int &frames_per_row, int &frame_width, int &frame_height, int &max_tiles, const Texture *texture, const std::string &filename) {
|
||||
const std::string KEY = line.substr(0, pos);
|
||||
const std::string VALUE = line.substr(pos + 1, line.length());
|
||||
if (KEY == "framesPerRow") {
|
||||
frames_per_row = std::stoi(VALUE);
|
||||
} else if (KEY == "frameWidth") {
|
||||
frame_width = std::stoi(VALUE);
|
||||
} else if (KEY == "frameHeight") {
|
||||
frame_height = std::stoi(VALUE);
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << KEY.c_str() << "\"" << '\n';
|
||||
}
|
||||
|
||||
if (frames_per_row == 0 && frame_width > 0) {
|
||||
frames_per_row = texture->getWidth() / frame_width;
|
||||
}
|
||||
if (max_tiles == 0 && frame_width > 0 && frame_height > 0) {
|
||||
const int W = texture->getWidth() / frame_width;
|
||||
const int H = texture->getHeight() / frame_height;
|
||||
max_tiles = W * H;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -234,6 +241,31 @@ void AnimatedSprite::animate() {
|
||||
}
|
||||
}
|
||||
|
||||
// Time-based: avança l'acumulador i calcula el frame actual a partir de
|
||||
// `step_duration_s`. La lògica de loop/completed és la mateixa que la
|
||||
// variant frame-based.
|
||||
void AnimatedSprite::animate(float dt_s) {
|
||||
Animation &anim = animation_[current_animation_];
|
||||
if (!enabled_ || anim.step_duration_s <= 0.0F) {
|
||||
return;
|
||||
}
|
||||
|
||||
anim.time_accumulator_s += dt_s;
|
||||
anim.current_frame = static_cast<int>(anim.time_accumulator_s / anim.step_duration_s);
|
||||
|
||||
if (anim.current_frame >= (int)anim.frames.size()) {
|
||||
if (anim.loop == -1) {
|
||||
anim.current_frame = anim.frames.size();
|
||||
anim.completed = true;
|
||||
} else {
|
||||
anim.time_accumulator_s = 0.0F;
|
||||
anim.current_frame = anim.loop;
|
||||
}
|
||||
} else {
|
||||
setSpriteClip(anim.frames[anim.current_frame]);
|
||||
}
|
||||
}
|
||||
|
||||
// Obtiene el numero de frames de la animación actual
|
||||
auto AnimatedSprite::getNumFrames() -> int {
|
||||
return (int)animation_[current_animation_].frames.size();
|
||||
@@ -350,6 +382,12 @@ void AnimatedSprite::update() {
|
||||
MovingSprite::update();
|
||||
}
|
||||
|
||||
// Time-based: animate(dt) + move(dt)
|
||||
void AnimatedSprite::update(float dt_s) {
|
||||
animate(dt_s);
|
||||
MovingSprite::update(dt_s);
|
||||
}
|
||||
|
||||
// Establece el rectangulo para un frame de una animación
|
||||
void AnimatedSprite::setAnimationFrames(Uint8 index_animation, Uint8 index_frame, int x, int y, int w, int h) {
|
||||
animation_[index_animation].frames.push_back({x, y, w, h});
|
||||
|
||||
@@ -12,11 +12,13 @@ class Texture;
|
||||
struct Animation {
|
||||
std::string name; // Nombre de la animacion
|
||||
std::vector<SDL_Rect> frames; // Cada uno de los frames que componen la animación
|
||||
int speed; // Velocidad de la animación
|
||||
int speed; // Velocidad de la animación (frame-based: ticks per frame)
|
||||
float step_duration_s; // Time-based: segons per frame d'animació (derivat de speed al parse: speed/60)
|
||||
int loop; // Indica a que frame vuelve la animación al terminar. -1 para que no vuelva
|
||||
bool completed; // Indica si ha finalizado la animación
|
||||
int current_frame; // Frame actual
|
||||
int counter; // Contador para las animaciones
|
||||
int counter; // Contador per a les animacions (frame-based)
|
||||
float time_accumulator_s; // Acumulador de temps (time-based)
|
||||
};
|
||||
|
||||
struct AnimatedSpriteData {
|
||||
@@ -37,7 +39,8 @@ class AnimatedSprite : public MovingSprite {
|
||||
|
||||
~AnimatedSprite() override; // Destructor
|
||||
|
||||
void animate(); // Calcula el frame correspondiente a la animación actual
|
||||
void animate(); // Calcula el frame correspondiente a la animación actual (frame-based)
|
||||
void animate(float dt_s); // Calcula el frame correspondiente a la animación actual (time-based)
|
||||
auto getNumFrames() -> int; // Obtiene el numero de frames de la animación actual
|
||||
void setCurrentFrame(int num); // Establece el frame actual de la animación
|
||||
void setAnimationCounter(const std::string &name, int num); // Establece el valor del contador
|
||||
@@ -56,13 +59,14 @@ class AnimatedSprite : public MovingSprite {
|
||||
auto getAnimationClip(const std::string &name = "default", Uint8 index = 0) -> SDL_Rect; // Devuelve el rectangulo de una animación y frame concreto
|
||||
auto getAnimationClip(int index_a = 0, Uint8 index_f = 0) -> SDL_Rect;
|
||||
|
||||
auto getIndex(const std::string &name) -> int; // Obtiene el indice de la animación a partir del nombre
|
||||
auto getIndex(const std::string &name) -> int; // Obtiene el indice de la animación a partir del nombre
|
||||
auto loadFromVector(const std::vector<std::string> *source) -> bool; // Carga la animación desde un vector
|
||||
|
||||
void setCurrentAnimation(const std::string &name = "default"); // Establece la animacion actual
|
||||
void setCurrentAnimation(int index = 0);
|
||||
|
||||
void update() override; // Actualiza las variables del objeto
|
||||
void update() override; // Actualiza las variables del objeto (frame-based)
|
||||
void update(float dt_s) override; // Actualiza las variables del objeto (time-based)
|
||||
|
||||
void setAnimationFrames(Uint8 index_animation, Uint8 index_frame, int x, int y, int w, int h); // OLD - Establece el rectangulo para un frame de una animación
|
||||
void setAnimationCounter(int value); // OLD - Establece el contador para todas las animaciones
|
||||
|
||||
@@ -31,6 +31,7 @@ void Fade::init(Uint8 r, Uint8 g, Uint8 b) {
|
||||
enabled_ = false;
|
||||
finished_ = false;
|
||||
counter_ = 0;
|
||||
elapsed_s_ = 0.0F;
|
||||
r_ = r;
|
||||
g_ = g;
|
||||
b_ = b;
|
||||
@@ -162,11 +163,25 @@ void Fade::update() {
|
||||
}
|
||||
}
|
||||
|
||||
// Time-based. Per a no haver de refactoritzar `render()` tot d'un cop
|
||||
// (encara hi ha codi frame-based que també escriu counter_), aquí derivem
|
||||
// `counter_` a partir de `elapsed_s_` usant la cadència de referència de la
|
||||
// versió antiga (60 Hz). Així el comportament visual del fade és idèntic.
|
||||
// Quan tot el codi sigui time-based, render passarà a usar elapsed_s_ amb
|
||||
// constants en segons i counter_ desapareixerà.
|
||||
void Fade::update(float dt_s) {
|
||||
if (!enabled_) { return; }
|
||||
elapsed_s_ += dt_s;
|
||||
constexpr float FADE_STEPS_PER_S = 60.0F;
|
||||
counter_ = static_cast<Uint16>(elapsed_s_ * FADE_STEPS_PER_S);
|
||||
}
|
||||
|
||||
// Activa el fade
|
||||
void Fade::activateFade() {
|
||||
enabled_ = true;
|
||||
finished_ = false;
|
||||
counter_ = 0;
|
||||
elapsed_s_ = 0.0F;
|
||||
squares_drawn_ = 0;
|
||||
last_square_ticks_ = 0;
|
||||
fullscreen_done_ = false;
|
||||
|
||||
@@ -18,7 +18,8 @@ class Fade {
|
||||
|
||||
void init(Uint8 r, Uint8 g, Uint8 b); // Inicializa las variables
|
||||
void render(); // Pinta una transición en pantalla
|
||||
void update(); // Actualiza las variables internas
|
||||
void update(); // Actualiza las variables internas (frame-based)
|
||||
void update(float dt_s); // Actualiza las variables internas (time-based)
|
||||
void activateFade(); // Activa el fade
|
||||
|
||||
[[nodiscard]] auto hasEnded() const -> bool; // Comprueba si ha terminado la transicion
|
||||
@@ -34,7 +35,8 @@ class Fade {
|
||||
SDL_Renderer *renderer_ = nullptr; // El renderizador de la ventana
|
||||
SDL_Texture *backbuffer_ = nullptr; // Textura para usar como backbuffer
|
||||
Type fade_type_{Type::FULLSCREEN}; // Tipo de fade a realizar
|
||||
Uint16 counter_ = 0; // Contador interno
|
||||
Uint16 counter_ = 0; // Contador intern (frame-based)
|
||||
float elapsed_s_ = 0.0F; // Acumulador de temps (time-based)
|
||||
bool enabled_ = false; // Indica si el fade está activo
|
||||
bool finished_ = false; // Indica si ha terminado la transición
|
||||
Uint8 r_ = 0, g_ = 0, b_ = 0; // Colores para el fade
|
||||
|
||||
Reference in New Issue
Block a user