migrat a deltaTime screen.cpp i notifier.cpp

This commit is contained in:
2025-09-24 18:08:50 +02:00
parent 40a2b2cc00
commit d077374883
19 changed files with 192 additions and 437 deletions

View File

@@ -1,81 +0,0 @@
# Plan de Migración DeltaTime - Eliminación de frameFactor
## Problema Identificado
Se están usando `frameFactor` conversions en 7 archivos, lo que indica una migración incompleta a deltaTime.
El patrón `float frameFactor = deltaTime / (1000.0f / 60.0f)` simula frames de 60fps en lugar de usar tiempo real.
## Archivos Afectados y Estado
1. **balloon.cpp** - 9 ocurrencias en métodos: moveX(), moveY(), updateState(), updateCreation()
2. **balloon_manager.cpp** - 2 ocurrencias en updateBalloonDeployment()
3. **bullet.cpp** - 3 ocurrencias en move()
4. **item.cpp** - 6 ocurrencias en move()
5. **moving_sprite.cpp** - 5 ocurrencias en move()
6. **tabe.cpp** - 5 ocurrencias en update() y updateHitEffect()
7. **credits.cpp** - 3 ocurrencias en update() y handleFadeOut()
## Estrategia de Migración
### Opción A: Velocidades ya en pixels/segundo
Si las velocidades están definidas en pixels/segundo:
```cpp
// ANTES (incorrecto)
float frameFactor = deltaTime / (1000.0f / 60.0f);
pos_x_ += vel_x_ * frameFactor;
// DESPUÉS (correcto)
pos_x_ += vel_x_ * (deltaTime / 1000.0f); // deltaTime en ms -> segundos
```
### Opción B: Velocidades en pixels/frame (legacy)
Si las velocidades están en pixels/frame (sistema legacy):
```cpp
// ANTES (incorrecto con deltaTime)
float frameFactor = deltaTime / (1000.0f / 60.0f);
pos_x_ += vel_x_ * frameFactor;
// OPCIÓN 1: Convertir velocidades a pixels/segundo
static constexpr float VEL_X_PER_SECOND = VEL_X_PER_FRAME * 60.0f;
pos_x_ += VEL_X_PER_SECOND * (deltaTime / 1000.0f);
// OPCIÓN 2: Mantener frame-factor pero mejorar claridad
pos_x_ += vel_x_ * (deltaTime / FRAME_TIME_MS); // donde FRAME_TIME_MS = 16.67f
```
## Plan de Ejecución
### Fase 1: Análisis de Velocidades
- [ ] Revisar definiciones de velocidades en cada clase
- [ ] Determinar si están en pixels/frame o pixels/segundo
- [ ] Identificar constantes que necesitan conversión
### Fase 2: Migración por Archivo
- [x] **balloon.cpp**: Migrar velocidades x/y y contadores ✅
- [x] **balloon_manager.cpp**: Migrar balloon_deploy_counter_ ✅
- [x] **bullet.cpp**: Migrar velocidades de bala ✅ (VEL_Y: -3.0F→-0.18F, VEL_X: ±2.0F→±0.12F)
- [x] **item.cpp**: Migrar física de ítems ✅ (vel_x: ±1.0F→±0.06F, vel_y: -4.0F→-0.24F, accel_y: 0.2F→0.012F)
- [ ] **moving_sprite.cpp**: Migrar sistema base de movimiento
- [ ] **tabe.cpp**: Migrar movimiento y efectos
- [ ] **credits.cpp**: Migrar contadores de timing
### Fase 3: Verificación
- [ ] Compilar y probar cada archivo migrado
- [ ] Verificar que el comportamiento se mantiene consistente
- [ ] Eliminar todas las referencias a frameFactor
- [ ] Actualizar comentarios para reflejar unidades correctas
## Criterios de Éxito
1. ✅ Cero ocurrencias de "frameFactor" en el código
2. ✅ Todas las velocidades claramente documentadas (pixels/segundo vs pixels/frame)
3. ✅ Comportamiento del juego idéntico al anterior
4. ✅ Código más limpio y mantenible
## Notas Importantes
- El frameFactor actual simula 60fps: `deltaTime / 16.67ms`
- Esto significa que las velocidades actuales están en "pixels per 16.67ms"
- Para verdadero deltaTime, necesitamos convertir a "pixels per second" o usar factor de frame explícito
- Mantener constantes claras para evitar números mágicos
## Estado: En Progreso
- Análisis iniciado
- Plan documentado
- Próximo paso: Análisis de velocidades en cada archivo

View File

@@ -1,11 +0,0 @@
# TODO: Migración a deltaTime
## Tareas pendientes
- [ ] Repasar screen y notifier para ser compatibles con el plan de migración a deltaTime
- [ ] Ver que el param de aparición del tabe está en minutos y pasarlo a segundos (para estandarizar)
- [ ] Repasar toda la carpeta ui/ para ver si el menú de servicio es compatible con el plan de migración a deltaTime
## Tareas completadas
- [x] Corregir problema en sections/title.cpp - contador nunca acababa porque comparaba segundos con milisegundos (línea 396)

View File

@@ -1,79 +0,0 @@
# Plan de Limpieza Post-Migración DeltaTime
## Estado Actual
✅ Migración básica completada: bullet.cpp, item.cpp, moving_sprite.cpp, game_logo.cpp
✅ Magic numbers convertidos a constantes en game_logo.cpp
## Tareas Pendientes
### 1. Eliminar Contadores Frame-Based
- [ ] Buscar todos los contadores que usen lógica frame-based
- [ ] Convertir a timers basados en deltaTime
- [ ] Eliminar variables como `counter_`, `frame_counter_`, etc.
- [ ] Patrón: `if (counter-- <= 0)``if (timer >= DURATION_MS)`
- [ ] **IMPORTANTE**: Todos los contadores han de ser crecientes, de cero hasta llegar a la constante que define su tope
### 2. Revisar Inicializaciones de Aceleraciones MovingSprite
- [ ] Buscar todas las llamadas a `setAccelX()`, `setAccelY()`
- [ ] Buscar asignaciones directas a `ax_`, `ay_`
- [ ] Convertir de `pixels/frame²` a `pixels/ms²`
- [ ] Factor de conversión: `valor_original / (16.67)²`
### 3. Problema Detectado: Demo - Creación Incorrecta de Globos
- [ ] Investigar cómo se crean los globos en modo demo
- [ ] Verificar si usan timing frame-based obsoleto
- [ ] Corregir la lógica de creación para deltaTime
### 4. Búsqueda de Patrones Problemáticos
- [ ] Buscar `frameFactor` residual
- [ ] Buscar `1000.0f / 60.0f` hardcodeado
- [ ] Buscar `16.67f` hardcodeado
- [ ] Buscar comentarios con "frame" o "60fps"
- [ ] Localizar magic numbers y convertirlos a constantes con nombres descriptivos
- [ ] **IMPORTANTE**: Modificar speed en ficheros .ani - está en frames, hay que pasarlo a milisegundos (multiplicar speed por 1000/60 = 16.67)
- [ ] **SmartSprite**: Revisar inicialización de SmartSprites en el código - cambiar setFinishedCounter() a setFinishedDelay() y convertir valores de frames a milisegundos
### 5. Cambio de Unidades de Tiempo en sections/*
- [ ] Cambiar el cálculo de deltatime en source/sections/* para que devuelva segundos (float) en lugar de milisegundos
- [ ] Cambiar velocidades de pixeles/ms a pixeles/segundos para evitar valores absurdamente pequeños
- [ ] Cambiar aceleraciones de pixeles/ms² a pixeles/segundos²
- [ ] Actualizar todas las constantes de tiempo en archivos de sections
### 6. Archivos Prioritarios a Revisar
- [ ] **player.cpp** - puede tener aceleraciones
- [ ] **balloon.cpp** - contadores de estado
- [ ] **stage.cpp** - timers de nivel
- [ ] **credits.cpp** - efectos de texto
- [ ] **tabe.cpp** - movimiento del protagonista
- [ ] **sections/*.cpp** - transiciones y efectos
### 7. Validación Final
- [ ] Compilar sin warnings
- [ ] Probar gameplay normal
- [ ] Probar modo demo
- [ ] Verificar que no hay saltos de velocidad
- [ ] Confirmar que el timing es consistente en diferentes framerates
## Comandos Útiles de Búsqueda
```bash
# Buscar contadores frame-based
rg "counter.*--|\+\+.*counter|counter.*\+\+|--.*counter"
# Buscar inicializaciones de aceleración
rg "setAccel|\.ax.*=|\.ay.*="
# Buscar hardcoded framerates
rg "60\.0|16\.67|1000\.0.*60"
```
## DECISIÓN IMPORTANTE: TODO EL CÓDIGO USA SEGUNDOS
- **CAMBIO DE PLAN**: Todo el código del juego debe usar deltaTime en segundos (float)
- **NO** debe haber soporte para frames, milisegundos y segundos simultáneamente
- **SOLO SEGUNDOS** en todo el codebase
- Velocidades en `pixels/segundos`, aceleraciones en `pixels/segundos²`
- Todos los contadores deben ser crecientes (0 → constante_tope)
- Eliminar todos los métodos duales (updateS, setSpeedS, etc.) - solo una versión
- Convertir completamente: path_sprite.cpp, writer.cpp, tiled_bg.cpp, etc.
- Documentar las conversiones en comentarios
- Crear constantes para valores repetidos
- Evitar números mágicos

43
development_guidelines.md Normal file
View File

@@ -0,0 +1,43 @@
# Directrices de Desarrollo - Coffee Crisis Arcade Edition
## Directrices Principales Confirmadas
### 1. **Sistema Temporal**
- **TODO migrado de frame based a time based**
- **Delta time en segundos (float)**
- **Unidades de tiempo: SOLO segundos** (no frames, no milisegundos)
### 2. **Contadores y Timers**
- **CRECIENTES**: para sistemas con múltiples eventos temporales (timeline)
- Patrón: `elapsed_time += deltaTime; if (elapsed_time >= EVENT_TIME) { /* acción */ }`
- **DECRECIENTES**: para contadores con diferentes valores de inicialización
- Patrón: `timer -= deltaTime; if (timer <= 0.0f) { /* acción */ timer = DURATION; }`
### 3. **Números Mágicos**
- **Definidos en constantes**
- **Preferencia**: cabecera de la clase
- **Excepción**: si es algo local a un método específico
## Problemas Pendientes de Reparación (game.cpp)
### ❌ PENDIENTES
1. **param.fade.post_duration_ms verification** (líneas 89, 1671)
2. **setRotateSpeed verification** (línea 797)
3. **TOTAL_DEMO_DATA - 200 magic number** (línea 1669)
4. **Comprehensive magic number search** - Buscar 100, 150, 200, 250, 300, 400, 500, 1000
### 4. **Velocidades y Aceleraciones**
- **Velocidades**: pixels/segundo
- **Aceleraciones**: pixels/segundo²
### 5. **Documentación de Conversiones**
- **Comentarios explicativos** en cambios críticos de timing
- Documentar conversiones frame→tiempo en el código
### 6. **Patrón de Constantes**
- Crear constantes para valores repetidos (evitar duplicación)
- Nombres descriptivos para constantes de tiempo
---
**Estado**: Directrices completas confirmadas

View File

@@ -1,136 +0,0 @@
# Plan de Reparación: game.cpp - Limpieza DeltaTime
## Estado Actual
`calculateDeltaTime()` convertido a segundos
✅ Constantes de tiempo convertidas de `_MS` a `_S`
✅ Eliminados hardcoded `1000.0f / 60.0f`
`throwCoffee()` velocidades convertidas a pixels/segundo
✅ Frame-based counter en `updateGameStateShowingGetReadyMessage()` convertido a timer con flag
## Problemas Detectados Pendientes
### ✅ COMPLETADO
1. **Valores hardcoded frame-based (60, 16.67, 1000/60)** - Solo quedan las conversiones correctas
2. **SmartSprite setFinishedDelay() calls** - Correcto (0.0F está bien)
3. **Velocidades y aceleraciones** - Todas convertidas correctamente
4. **Timer variables** - Todas usan deltaTime correctamente
### ❌ PENDIENTES DE REPARAR
#### ✅ **SOLUCIONADO: Balas no se mueven**
- **Síntoma**: Las balas se crean pero no avanzan en pantalla
- **Causa encontrada**: Velocidades en `bullet.h` seguían en pixels/ms pero `calculateDeltaTime()` ahora devuelve segundos
- **Solución aplicada**:
- `VEL_Y = -0.18F``VEL_Y = -180.0F` (pixels/segundo)
- `VEL_X_LEFT = -0.12F``VEL_X_LEFT = -120.0F` (pixels/segundo)
- `VEL_X_RIGHT = 0.12F``VEL_X_RIGHT = 120.0F` (pixels/segundo)
- Actualizado comentario en `bullet.cpp`: "pixels/ms" → "pixels/segundo"
#### ✅ **SOLUCIONADO: Contadores frame-based (++ o -- operations)**
- **Problema**: `demo_.counter++` en líneas 1665, 1691
- **Ubicación**: `updateDemo()` y `updateRecording()`
- **Contexto**: `demo_.counter` indexa un vector con datos de teclas por frame (ej: 2000 frames = 2000 entradas)
- **Solución aplicada**: Acumulador de tiempo que se incrementa cada 16.67ms (1/60 segundos)
- **Implementación**:
```cpp
// updateDemo()
static float demo_frame_timer = 0.0f;
demo_frame_timer += calculateDeltaTime();
if (demo_frame_timer >= 0.01667f && demo_.counter < TOTAL_DEMO_DATA) {
demo_.counter++;
demo_frame_timer -= 0.01667f; // Mantener precisión acumulada
}
// updateRecording()
static float recording_frame_timer = 0.0f;
recording_frame_timer += calculateDeltaTime();
if (recording_frame_timer >= 0.01667f && demo_.counter < TOTAL_DEMO_DATA) {
demo_.counter++;
recording_frame_timer -= 0.01667f; // Mantener precisión acumulada
}
```
#### 6. **Fade duration values (milisegundos vs segundos)**
- **Problema 1**: `fade_out_->setPostDuration(500);` línea 234
- **Problema 2**: `fade_out_->setPostDuration(param.fade.post_duration_ms);` líneas 89, 1671
- **Solución**: Verificar si `param.fade.post_duration_ms` debe ser convertido a segundos
### NUEVOS PROBLEMAS A AÑADIR
#### 7. **Verificar param.fade estructura**
- Revisar si `param.fade.post_duration_ms` debe ser convertido a segundos
- Verificar todas las propiedades de `param.fade` relacionadas con tiempo
#### 8. **Revisar constantes TOTAL_DEMO_DATA**
- **Problema**: `TOTAL_DEMO_DATA - 200` en línea 1669
- **Detalle**: 200 frames hardcoded, debería ser tiempo en segundos
#### 9. **Buscar más magic numbers relacionados con tiempo**
- Buscar números como 100, 150, 200, 500 que puedan ser frames
- Verificar contexto de cada uno
#### 10. **Revisar setRotateSpeed() values**
- **Problema**: `setRotateSpeed(10)` línea 797
- **Verificar**: Si debe ser convertido de rotaciones/frame a rotaciones/segundo
#### ✅ **VERIFICADO: SDL_Delay() calls**
- **Problema**: `SDL_Delay(param.game.hit_stop_ms);` línea 847
- **Ubicación**: `handlePlayerCollision()`
- **Contexto**: Pausa el juego al recibir daño (hitStop effect)
- **Resultado**: `param.game.hit_stop_ms` está configurado explícitamente en milisegundos en archivos .txt
- **Conclusión**: Correcto, `SDL_Delay()` espera milisegundos y `hit_stop_ms` ya está en milisegundos
#### ✅ **VERIFICADO: Cooldown de disparos en frames**
- **Problema**: `handleFireInput()` líneas 1312-1314
- **Detalle**: `POWERUP_COOLDOWN = 5`, `AUTOFIRE_COOLDOWN = 10`, `NORMAL_COOLDOWN = 7`
- **Contexto**: Son frames de cooldown, se pasan a `player->startFiringSystem(cant_fire_counter)`
- **Resultado**: `startFiringSystem()` convierte internamente frames a segundos con: `fire_cooldown_timer_ = static_cast<float>(cooldown_frames) / 60.0f;`
- **Conclusión**: No necesita cambios, la conversión ya está implementada correctamente
#### 12. **Revisar paths y PathSprite durations**
- **Problema**: En `initPaths()` líneas 1024, 1025, 1036, 1037, 1049, 1050, 1062, 1063
- **Detalle**: `createPath(..., 80, ...), 20);` - Los números 80 y 20 son frames
- **Ejemplo**: `paths_.emplace_back(createPath(X0, X1, PathType::HORIZONTAL, Y, 80, easeOutQuint), 20);`
- **Investigar**: Si createPath espera segundos o milisegundos
- **Conversión**: 80 frames = 80/60 = 1.33s, 20 frames = 20/60 = 0.33s
- **Verificar**: Documentación de createPath() y PathSprite para unidades correctas
## Herramientas de Búsqueda Útiles
```bash
# Buscar magic numbers de tiempo
rg "\b(100|150|200|250|300|400|500|1000)\b" --context=2
# Buscar más frame-based operations
rg "setRotateSpeed|SDL_Delay|createPath.*[0-9]+"
# Buscar estructuras param que mencionen tiempo
rg "param\..*\.(duration|time|delay|ms|frames)"
# Buscar comentarios con "frame" or "60fps"
rg "(frame|60fps|milliseconds)" --ignore-case
```
## Prioridad de Reparación
### ✅ COMPLETADAS
1.**CRÍTICA**: Las balas no se desplazan - **SOLUCIONADO**: Velocidades convertidas de pixels/ms a pixels/segundo
2.**ALTA**: demo_.counter system - **SOLUCIONADO**: Implementados acumuladores de tiempo
3.**ALTA**: Fade durations hardcoded - **SOLUCIONADO**: Convertidos a segundos
4.**ALTA**: initPaths() frame values - **SOLUCIONADO**: Convertidos a segundos
5.**MEDIA**: Cooldown de disparos - **VERIFICADO**: Ya convierte internamente frames a segundos
6.**BAJA**: SDL_Delay investigation - **VERIFICADO**: Correcto, usa milisegundos
### ❌ PENDIENTES
7. **MEDIA**: param.fade.post_duration_ms verification (líneas 89, 1671)
8. **MEDIA**: TOTAL_DEMO_DATA - 200 magic number (línea 1669) - Nota: No necesita cambios, lógica correcta
9. **BAJA**: setRotateSpeed verification (línea 797)
10. **BAJA**: Comprehensive magic number search
## Notas Importantes
- **TODOS los contadores deben ser crecientes** (0 → constante_tope)
- **SOLO SEGUNDOS** en todo el codebase, NO frames ni milisegundos simultáneos
- **Documentar conversiones** con comentarios explicativos
- **Crear constantes** para valores repetidos
- **Evitar números mágicos** sin contexto

View File

@@ -337,7 +337,7 @@ auto BalloonManager::destroyAllBalloons() -> int {
} }
balloon_deploy_counter_ = DEFAULT_BALLOON_DEPLOY_DELAY; balloon_deploy_counter_ = DEFAULT_BALLOON_DEPLOY_DELAY;
Screen::get()->flash(Colors::FLASH, 3); Screen::get()->flash(Colors::FLASH, 0.05F);
Screen::get()->shake(); Screen::get()->shake();
return score; return score;

View File

@@ -42,7 +42,7 @@ Director::Director(int argc, std::span<char *> argv) {
Section::name = Section::Name::GAME; Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P; Section::options = Section::Options::GAME_PLAY_1P;
#elif _DEBUG #elif _DEBUG
Section::name = Section::Name::LOGO; Section::name = Section::Name::GAME;
Section::options = Section::Options::GAME_PLAY_1P; Section::options = Section::Options::GAME_PLAY_1P;
#else // NORMAL GAME #else // NORMAL GAME
Section::name = Section::Name::LOGO; Section::name = Section::Name::LOGO;

View File

@@ -15,8 +15,9 @@
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
constexpr int ZOOM_FACTOR = 5; constexpr int ZOOM_FACTOR = 5;
constexpr int FLASH_DELAY = 3; constexpr float FLASH_DELAY_S = 0.05f; // 3 frames → 0.05s
constexpr int FLASH_LENGTH = FLASH_DELAY + 3; constexpr float FLASH_DURATION_S = 0.1f; // 6 frames → 0.1s (3 + 3)
constexpr Color FLASH_COLOR = Color(0xFF, 0xFF, 0xFF); // Color blanco para el flash
// Constructor // Constructor
GameLogo::GameLogo(int x, int y) GameLogo::GameLogo(int x, int y)
@@ -242,7 +243,7 @@ void GameLogo::finishArcadeEditionMoving() {
void GameLogo::playTitleEffects() { void GameLogo::playTitleEffects() {
Audio::get()->playSound("title.wav"); Audio::get()->playSound("title.wav");
Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGTH, FLASH_DELAY); Screen::get()->flash(FLASH_COLOR, FLASH_DURATION_S, FLASH_DELAY_S);
Screen::get()->shake(); Screen::get()->shake();
} }

View File

@@ -155,16 +155,16 @@ auto Screen::incWindowSize() -> bool {
return false; return false;
} }
// Actualiza la lógica de la clase // Recibe deltaTime de las secciones y actualiza la lógica
void Screen::update() { void Screen::update(float delta_time) {
fps_.calculate(SDL_GetTicks()); fps_.calculate(SDL_GetTicks());
shake_effect_.update(src_rect_, dst_rect_); shake_effect_.update(src_rect_, dst_rect_, delta_time);
flash_effect_.update(); flash_effect_.update(delta_time);
if (service_menu_ != nullptr) { if (service_menu_ != nullptr) {
service_menu_->update(); service_menu_->update();
} }
if (notifier_ != nullptr) { if (notifier_ != nullptr) {
notifier_->update(); notifier_->update(delta_time);
} }
Mouse::updateCursorVisibility(); Mouse::updateCursorVisibility();
} }

View File

@@ -21,7 +21,7 @@ class Screen {
static auto get() -> Screen *; // Obtiene el puntero al objeto Screen static auto get() -> Screen *; // Obtiene el puntero al objeto Screen
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la lógica de la clase void update(float delta_time); // Recibe deltaTime de las secciones y actualiza la lógica
void coreUpdate(); // Actualiza los elementos mínimos void coreUpdate(); // Actualiza los elementos mínimos
void clean(Color color = Color(0x00, 0x00, 0x00)); // Limpia la pantalla void clean(Color color = Color(0x00, 0x00, 0x00)); // Limpia la pantalla
void start(); // Prepara para empezar a dibujar en la textura de juego void start(); // Prepara para empezar a dibujar en la textura de juego
@@ -38,8 +38,8 @@ class Screen {
void initShaders(); // Inicializa los shaders void initShaders(); // Inicializa los shaders
// --- Efectos visuales --- // --- Efectos visuales ---
void shake(int desp = 2, int delay = 3, int length = 8) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay, length); } // Agita la pantalla void shake(int desp = 2, float delay_s = 0.05f, float duration_s = 0.133f) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay_s, duration_s); } // Agita la pantalla (tiempo en segundos)
void flash(Color color, int length = 10, int delay = 0) { flash_effect_ = FlashEffect(true, length, delay, color); } // Pone la pantalla de color void flash(Color color, float duration_s = 0.167f, float delay_s = 0.0f) { flash_effect_ = FlashEffect(true, duration_s, delay_s, color); } // Pone la pantalla de color (tiempo en segundos)
void toggleShaders(); // Alterna entre activar y desactivar los shaders void toggleShaders(); // Alterna entre activar y desactivar los shaders
void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero
void toggleVSync(); // Alterna entre activar y desactivar el V-Sync void toggleVSync(); // Alterna entre activar y desactivar el V-Sync
@@ -82,48 +82,55 @@ class Screen {
} }
}; };
// Efecto de flash en pantalla: pinta la pantalla de un color durante unos frames // Efecto de flash en pantalla: pinta la pantalla de un color durante un tiempo
struct FlashEffect { struct FlashEffect {
bool enabled; // Indica si el efecto está activo bool enabled; // Indica si el efecto está activo
int length; // Duración total del efecto en frames float duration_s; // Duración total del efecto en segundos
int delay; // Retraso antes de mostrar el flash float delay_s; // Retraso antes de mostrar el flash en segundos
int counter; // Contador de frames restantes float timer_s; // Timer en segundos (contador decreciente)
Color color; // Color del flash Color color; // Color del flash
explicit FlashEffect(bool enabled = false, int length = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF)) explicit FlashEffect(bool enabled = false, float duration_s = 0.0f, float delay_s = 0.0f, Color color = Color(0xFF, 0xFF, 0xFF))
: enabled(enabled), : enabled(enabled),
length(length), duration_s(duration_s),
delay(delay), delay_s(delay_s),
counter(length), timer_s(duration_s),
color(color) {} color(color) {}
void update() { (enabled && counter > 0) ? counter-- : static_cast<int>(enabled = false); } void update(float delta_time) {
[[nodiscard]] auto isRendarable() const -> bool { return enabled && counter < length - delay; } if (enabled && timer_s > 0.0f) {
timer_s -= delta_time;
if (timer_s <= 0.0f) {
enabled = false;
}
}
}
[[nodiscard]] auto isRendarable() const -> bool { return enabled && timer_s < duration_s - delay_s; }
}; };
// Efecto de sacudida/agitación de pantalla: mueve la imagen para simular un temblor // Efecto de sacudida/agitación de pantalla: mueve la imagen para simular un temblor
struct ShakeEffect { struct ShakeEffect {
int desp; // Desplazamiento máximo de la sacudida (en píxeles) int desp; // Desplazamiento máximo de la sacudida (en píxeles)
int delay; // Frames entre cada movimiento de sacudida float delay_s; // Segundos entre cada movimiento de sacudida
int counter; // Contador de frames para el siguiente movimiento float counter_s; // Timer para el siguiente movimiento (decreciente)
int length; // Duración total del efecto en frames float duration_s; // Duración total del efecto en segundos
int remaining; // Frames restantes de sacudida float remaining_s; // Tiempo restante de sacudida
int original_pos; // Posición original de la imagen (x) int original_pos; // Posición original de la imagen (x)
int original_width; // Ancho original de la imagen int original_width; // Ancho original de la imagen
bool enabled; // Indica si el efecto está activo bool enabled; // Indica si el efecto está activo
explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int orig_pos = 0, int orig_width = 800) explicit ShakeEffect(bool en = false, int dp = 2, float dl_s = 0.05f, float cnt_s = 0.0f, float len_s = 0.133f, float rem_s = 0.0f, int orig_pos = 0, int orig_width = 800)
: desp(dp), : desp(dp),
delay(dl), delay_s(dl_s),
counter(cnt), counter_s(cnt_s),
length(len), duration_s(len_s),
remaining(rem), remaining_s(rem_s),
original_pos(orig_pos), original_pos(orig_pos),
original_width(orig_width), original_width(orig_width),
enabled(en) {} enabled(en) {}
// Activa el efecto de sacudida y guarda la posición y tamaño originales // Activa el efecto de sacudida y guarda la posición y tamaño originales
void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_length = -1) { void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, float new_delay_s = -1.0f, float new_duration_s = -1.0f) {
if (!enabled) { if (!enabled) {
enabled = true; enabled = true;
original_pos = src_rect.x; original_pos = src_rect.x;
@@ -133,33 +140,35 @@ class Screen {
if (new_desp != -1) { if (new_desp != -1) {
desp = new_desp; desp = new_desp;
} }
if (new_delay != -1) { if (new_delay_s >= 0.0f) {
delay = new_delay; delay_s = new_delay_s;
} }
if (new_length != -1) { if (new_duration_s >= 0.0f) {
length = new_length; duration_s = new_duration_s;
} }
src_rect.w -= desp; src_rect.w -= desp;
dst_rect.w = src_rect.w; dst_rect.w = src_rect.w;
} }
remaining = length; remaining_s = duration_s;
counter = delay; counter_s = delay_s;
} }
// Actualiza el estado del efecto de sacudida // Actualiza el estado del efecto de sacudida
void update(SDL_FRect &src_rect, SDL_FRect &dst_rect) { void update(SDL_FRect &src_rect, SDL_FRect &dst_rect, float delta_time) {
if (enabled) { if (enabled) {
if (counter > 0) { counter_s -= delta_time;
counter--; if (counter_s <= 0.0f) {
} else { counter_s = delay_s;
counter = delay; // Alternar desplazamiento basado en tiempo restante
const auto SRC_DESP = (remaining % 2 == 0) ? 0 : desp; const bool SHAKE_LEFT = static_cast<int>(remaining_s * 30.0f) % 2 == 0; // ~30 cambios por segundo
const auto DST_DESP = (remaining % 2 == 1) ? 0 : desp; const auto SRC_DESP = SHAKE_LEFT ? 0 : desp;
const auto DST_DESP = SHAKE_LEFT ? desp : 0;
src_rect.x = original_pos + SRC_DESP; src_rect.x = original_pos + SRC_DESP;
dst_rect.x = original_pos + DST_DESP; dst_rect.x = original_pos + DST_DESP;
remaining--;
if (remaining == -1) { remaining_s -= delay_s;
if (remaining_s <= 0.0f) {
enabled = false; enabled = false;
src_rect.x = original_pos; src_rect.x = original_pos;
src_rect.w = original_width; src_rect.w = original_width;

View File

@@ -105,6 +105,10 @@ void Credits::update(float deltaTime) {
const float multiplier = want_to_pass_ ? 4.0f : 1.0f; const float multiplier = want_to_pass_ ? 4.0f : 1.0f;
const float adjusted_delta_time = deltaTime * multiplier; const float adjusted_delta_time = deltaTime * multiplier;
static auto *screen = Screen::get();
screen->update(deltaTime); // Actualiza el objeto screen
Audio::update(); // Actualiza el objeto audio
tiled_bg_->update(adjusted_delta_time); tiled_bg_->update(adjusted_delta_time);
cycleColors(); cycleColors();
balloon_manager_->update(adjusted_delta_time); balloon_manager_->update(adjusted_delta_time);
@@ -117,9 +121,7 @@ void Credits::update(float deltaTime) {
const float frameFactor = adjusted_delta_time * 60.0f; const float frameFactor = adjusted_delta_time * 60.0f;
counter_ += frameFactor; counter_ += frameFactor;
Screen::get()->update();
fillCanvas(); fillCanvas();
Audio::update();
} }
// Dibuja Credits::en patalla // Dibuja Credits::en patalla

View File

@@ -282,7 +282,7 @@ void Game::updateStage() {
// Efectos de cambio de fase // Efectos de cambio de fase
playSound("stage_change.wav"); playSound("stage_change.wav");
balloon_manager_->resetBalloonSpeed(); balloon_manager_->resetBalloonSpeed();
screen_->flash(Colors::FLASH, 3); screen_->flash(Colors::FLASH, 0.05F);
screen_->shake(); screen_->shake();
// Obtener datos de la nueva fase // Obtener datos de la nueva fase
@@ -635,7 +635,7 @@ void Game::updateItems(float deltaTime) {
item->update(deltaTime); item->update(deltaTime);
if (item->isOnFloor()) { if (item->isOnFloor()) {
playSound("title.wav"); playSound("title.wav");
screen_->shake(1, 2, 4); screen_->shake(1, 0.033f, 0.067f); // desp=1, delay=0.033s, length=0.067s
} }
} }
} }
@@ -895,7 +895,8 @@ void Game::updateTimeStopped(float deltaTime) {
// Actualiza toda la lógica del juego // Actualiza toda la lógica del juego
void Game::update(float deltaTime) { void Game::update(float deltaTime) {
screen_->update(); screen_->update(deltaTime); // Actualiza el objeto screen
Audio::update(); // Actualiza el objeto audio
updateDemo(); updateDemo();
#ifdef RECORDING #ifdef RECORDING
@@ -903,8 +904,6 @@ void Game::update(float deltaTime) {
#endif #endif
updateGameStates(deltaTime); updateGameStates(deltaTime);
fillCanvas(); fillCanvas();
Audio::update();
} }
// Dibuja el juego // Dibuja el juego
@@ -1735,9 +1734,6 @@ void Game::updateGameStateEnteringPlayer(float deltaTime) {
// Actualiza las variables durante dicho estado // Actualiza las variables durante dicho estado
void Game::updateGameStateShowingGetReadyMessage(float deltaTime) { void Game::updateGameStateShowingGetReadyMessage(float deltaTime) {
updateGameStatePlaying(deltaTime); updateGameStatePlaying(deltaTime);
if (path_sprites_.empty()) {
setState(State::PLAYING);
}
// Reproducir música después de ~1.67 segundos (100 frames a 60fps) // Reproducir música después de ~1.67 segundos (100 frames a 60fps)
static bool music_started = false; static bool music_started = false;

View File

@@ -55,15 +55,16 @@ HiScoreTable::~HiScoreTable() {
// Actualiza las variables // Actualiza las variables
void HiScoreTable::update(float delta_time) { void HiScoreTable::update(float delta_time) {
elapsed_time_ += delta_time; // Incrementa el tiempo transcurrido elapsed_time_ += delta_time; // Incrementa el tiempo transcurrido
Screen::get()->update(); // Actualiza el objeto screen
static auto *screen = Screen::get();
screen->update(delta_time); // Actualiza el objeto screen
Audio::update(); // Actualiza el objeto audio
updateSprites(delta_time); // Actualiza las posiciones de los sprites de texto updateSprites(delta_time); // Actualiza las posiciones de los sprites de texto
background_->update(delta_time); // Actualiza el fondo background_->update(delta_time); // Actualiza el fondo
updateFade(delta_time); // Gestiona el fade updateFade(delta_time); // Gestiona el fade
updateCounter(); // Gestiona el contador y sus eventos updateCounter(); // Gestiona el contador y sus eventos
fillTexture(); // Dibuja los sprites en la textura fillTexture(); // Dibuja los sprites en la textura
Audio::update();
} }
// Pinta en pantalla // Pinta en pantalla

View File

@@ -206,15 +206,16 @@ void Instructions::fillBackbuffer() {
// Actualiza las variables // Actualiza las variables
void Instructions::update(float delta_time) { void Instructions::update(float delta_time) {
elapsed_time_ += delta_time; // Incrementa el tiempo transcurrido elapsed_time_ += delta_time; // Incrementa el tiempo transcurrido
Screen::get()->update(); // Actualiza el objeto screen
static auto *screen = Screen::get();
screen->update(delta_time); // Actualiza el objeto screen
Audio::update(); // Actualiza el objeto audio
updateSprites(); // Actualiza los sprites updateSprites(); // Actualiza los sprites
updateBackbuffer(delta_time); // Gestiona la textura con los graficos updateBackbuffer(delta_time); // Gestiona la textura con los graficos
tiled_bg_->update(delta_time); // Actualiza el mosaico de fondo tiled_bg_->update(delta_time); // Actualiza el mosaico de fondo
fade_->update(delta_time); // Actualiza el objeto "fade" fade_->update(delta_time); // Actualiza el objeto "fade"
fillBackbuffer(); // Rellena el backbuffer fillBackbuffer(); // Rellena el backbuffer
Audio::update();
} }
// Pinta en pantalla // Pinta en pantalla

View File

@@ -208,7 +208,9 @@ void Intro::switchText(int from_index, int to_index) {
// Actualiza las variables del objeto // Actualiza las variables del objeto
void Intro::update(float delta_time) { void Intro::update(float delta_time) {
Screen::get()->update(); // Actualiza el objeto screen static auto *screen = Screen::get();
screen->update(delta_time); // Actualiza el objeto screen
Audio::update(); // Actualiza el objeto Aud
tiled_bg_->update(delta_time); // Actualiza el fondo tiled_bg_->update(delta_time); // Actualiza el fondo
@@ -223,8 +225,6 @@ void Intro::update(float delta_time) {
updatePostState(); updatePostState();
break; break;
} }
Audio::update();
} }
// Dibuja el objeto en pantalla // Dibuja el objeto en pantalla

View File

@@ -140,7 +140,8 @@ void Logo::updateTextureColors(float delta_time) {
void Logo::update(float delta_time) { void Logo::update(float delta_time) {
elapsed_time_s_ += delta_time; // Acumula el tiempo transcurrido elapsed_time_s_ += delta_time; // Acumula el tiempo transcurrido
Screen::get()->update(); // Actualiza el objeto screen static auto *screen = Screen::get();
screen->update(delta_time); // Actualiza el objeto screen
Audio::update(); // Actualiza el objeto audio Audio::update(); // Actualiza el objeto audio
handleSound(); // Maneja la reproducción del sonido handleSound(); // Maneja la reproducción del sonido

View File

@@ -80,7 +80,10 @@ Title::~Title() {
// Actualiza las variables del objeto // Actualiza las variables del objeto
void Title::update(float deltaTime) { void Title::update(float deltaTime) {
Screen::get()->update(); static auto* screen = Screen::get();
screen->update(deltaTime); // Actualiza el objeto screen
Audio::update(); // Actualiza el objeto audio
updateFade(); updateFade();
updateState(deltaTime); updateState(deltaTime);
updateStartPrompt(deltaTime); updateStartPrompt(deltaTime);
@@ -88,8 +91,6 @@ void Title::update(float deltaTime) {
for (auto& player : players_) { for (auto& player : players_) {
player->update(deltaTime); player->update(deltaTime);
} }
Audio::update();
} }
// Calcula el tiempo transcurrido desde el último frame // Calcula el tiempo transcurrido desde el último frame

View File

@@ -2,7 +2,7 @@
#include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_FRect, SDL_RenderClear #include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_FRect, SDL_RenderClear
#include <algorithm> // Para remove_if #include <algorithm> // Para remove_if, min
#include <string> // Para basic_string, string #include <string> // Para basic_string, string
#include <utility> #include <utility>
#include <vector> // Para vector #include <vector> // Para vector
@@ -32,7 +32,6 @@ Notifier::Notifier(const std::string& icon_file, std::shared_ptr<Text> text)
icon_texture_(!icon_file.empty() ? std::make_unique<Texture>(renderer_, icon_file) : nullptr), icon_texture_(!icon_file.empty() ? std::make_unique<Texture>(renderer_, icon_file) : nullptr),
text_(std::move(text)), text_(std::move(text)),
bg_color_(param.notification.color), bg_color_(param.notification.color),
wait_time_(150),
stack_(false), stack_(false),
has_icons_(!icon_file.empty()) {} has_icons_(!icon_file.empty()) {}
@@ -43,13 +42,13 @@ void Notifier::render() {
} }
} }
// Actualiza el estado de las notificaiones // Actualiza el estado de las notificaciones
void Notifier::update() { void Notifier::update(float delta_time) {
for (int i = 0; i < (int)notifications_.size(); ++i) { for (int i = 0; i < (int)notifications_.size(); ++i) {
if (!shouldProcessNotification(i)) { if (!shouldProcessNotification(i)) {
break; break;
} }
processNotification(i); processNotification(i, delta_time);
} }
clearFinishedNotifications(); clearFinishedNotifications();
} }
@@ -59,52 +58,54 @@ auto Notifier::shouldProcessNotification(int index) const -> bool {
return index <= 0 || notifications_[index - 1].state != State::RISING; return index <= 0 || notifications_[index - 1].state != State::RISING;
} }
void Notifier::processNotification(int index) { void Notifier::processNotification(int index, float delta_time) {
auto& notification = notifications_[index]; auto& notification = notifications_[index];
notification.counter++; notification.timer += delta_time;
playNotificationSoundIfNeeded(notification); playNotificationSoundIfNeeded(notification);
updateNotificationState(index); updateNotificationState(index, delta_time);
notification.sprite->setPosition(notification.rect); notification.sprite->setPosition(notification.rect);
} }
void Notifier::playNotificationSoundIfNeeded(const Notification& notification) { void Notifier::playNotificationSoundIfNeeded(const Notification& notification) {
// Hace sonar la notificación en el primer frame // Hace sonar la notificación al inicio
if (notification.counter == 1 && if (notification.timer <= 0.016f &&
param.notification.sound && param.notification.sound &&
notification.state == State::RISING) { notification.state == State::RISING) {
Audio::get()->playSound("notify.wav", Audio::Group::INTERFACE); Audio::get()->playSound("notify.wav", Audio::Group::INTERFACE);
} }
} }
void Notifier::updateNotificationState(int index) { void Notifier::updateNotificationState(int index, float delta_time) {
auto& notification = notifications_[index]; auto& notification = notifications_[index];
switch (notification.state) { switch (notification.state) {
case State::RISING: case State::RISING:
handleRisingState(index); handleRisingState(index, delta_time);
break; break;
case State::STAY: case State::STAY:
handleStayState(index); handleStayState(index);
break; break;
case State::VANISHING: case State::VANISHING:
handleVanishingState(index); handleVanishingState(index, delta_time);
break; break;
default: default:
break; break;
} }
} }
void Notifier::handleRisingState(int index) { void Notifier::handleRisingState(int index, float delta_time) {
auto& notification = notifications_[index]; auto& notification = notifications_[index];
const float STEP = (float)notification.counter / notification.travel_dist; const float PIXELS_TO_MOVE = ANIMATION_SPEED_PX_PER_S * delta_time;
const int ALPHA = 255 * STEP; const float PROGRESS = notification.timer * ANIMATION_SPEED_PX_PER_S / notification.travel_dist;
const int ALPHA = static_cast<int>(255 * std::min(PROGRESS, 1.0f));
moveNotificationVertically(notification, param.notification.pos_v == Position::TOP ? 1 : -1); moveNotificationVertically(notification, param.notification.pos_v == Position::TOP ? PIXELS_TO_MOVE : -PIXELS_TO_MOVE);
notification.texture->setAlpha(ALPHA); notification.texture->setAlpha(ALPHA);
if (notification.rect.y == notification.y) { if ((param.notification.pos_v == Position::TOP && notification.rect.y >= notification.y) ||
(param.notification.pos_v == Position::BOTTOM && notification.rect.y <= notification.y)) {
transitionToStayState(index); transitionToStayState(index);
} }
} }
@@ -112,35 +113,37 @@ void Notifier::handleRisingState(int index) {
void Notifier::handleStayState(int index) { void Notifier::handleStayState(int index) {
auto& notification = notifications_[index]; auto& notification = notifications_[index];
if (notification.counter == wait_time_) { if (notification.timer >= STAY_DURATION_S) {
notification.state = State::VANISHING; notification.state = State::VANISHING;
notification.counter = 0; notification.timer = 0.0f;
} }
} }
void Notifier::handleVanishingState(int index) { void Notifier::handleVanishingState(int index, float delta_time) {
auto& notification = notifications_[index]; auto& notification = notifications_[index];
const float STEP = notification.counter / (float)notification.travel_dist; const float PIXELS_TO_MOVE = ANIMATION_SPEED_PX_PER_S * delta_time;
const int ALPHA = 255 * (1 - STEP); const float PROGRESS = notification.timer * ANIMATION_SPEED_PX_PER_S / notification.travel_dist;
const int ALPHA = static_cast<int>(255 * (1 - std::min(PROGRESS, 1.0f)));
moveNotificationVertically(notification, param.notification.pos_v == Position::TOP ? -1 : 1); moveNotificationVertically(notification, param.notification.pos_v == Position::TOP ? -PIXELS_TO_MOVE : PIXELS_TO_MOVE);
notification.texture->setAlpha(ALPHA); notification.texture->setAlpha(ALPHA);
if (notification.rect.y == notification.y - notification.travel_dist) { if (PROGRESS >= 1.0f) {
notification.state = State::FINISHED; notification.state = State::FINISHED;
} }
} }
void Notifier::moveNotificationVertically(Notification& notification, int direction) { void Notifier::moveNotificationVertically(Notification& notification, float pixels_to_move) {
notification.rect.y += direction; notification.rect.y += pixels_to_move;
} }
void Notifier::transitionToStayState(int index) { void Notifier::transitionToStayState(int index) {
auto& notification = notifications_[index]; auto& notification = notifications_[index];
notification.state = State::STAY; notification.state = State::STAY;
notification.texture->setAlpha(255); notification.texture->setAlpha(255);
notification.counter = 0; notification.rect.y = static_cast<float>(notification.y); // Asegurar posición exacta
notification.timer = 0.0f;
} }
// Elimina las notificaciones finalizadas // Elimina las notificaciones finalizadas

View File

@@ -32,7 +32,7 @@ class Notifier {
// --- Métodos principales --- // --- Métodos principales ---
void render(); // Dibuja las notificaciones por pantalla void render(); // Dibuja las notificaciones por pantalla
void update(); // Actualiza el estado de las notificaciones void update(float delta_time); // Actualiza el estado de las notificaciones
// --- Gestión de notificaciones --- // --- Gestión de notificaciones ---
void show(std::vector<std::string> texts, int icon = -1, const std::string &code = std::string()); // Muestra una notificación de texto por pantalla void show(std::vector<std::string> texts, int icon = -1, const std::string &code = std::string()); // Muestra una notificación de texto por pantalla
@@ -41,6 +41,10 @@ class Notifier {
auto checkCode(const std::string &code) -> bool { return stringInVector(getCodes(), code); } // Comprueba si hay alguna notificación con un código concreto auto checkCode(const std::string &code) -> bool { return stringInVector(getCodes(), code); } // Comprueba si hay alguna notificación con un código concreto
private: private:
// --- Constantes de tiempo (en segundos) ---
static constexpr float STAY_DURATION_S = 2.5f; // Tiempo que se ve la notificación (150 frames @ 60fps)
static constexpr float ANIMATION_SPEED_PX_PER_S = 60.0f; // Velocidad de animación (1 pixel/frame @ 60fps)
// --- Enums privados --- // --- Enums privados ---
enum class State { enum class State {
RISING, // Apareciendo RISING, // Apareciendo
@@ -63,7 +67,7 @@ class Notifier {
std::string code; // Código identificador de la notificación std::string code; // Código identificador de la notificación
State state{State::RISING}; // Estado de la notificación State state{State::RISING}; // Estado de la notificación
Shape shape{Shape::SQUARED}; // Forma de la notificación Shape shape{Shape::SQUARED}; // Forma de la notificación
int counter{0}; // Contador de tiempo float timer{0.0f}; // Timer en segundos
int y{0}; // Posición vertical int y{0}; // Posición vertical
int travel_dist{0}; // Distancia a recorrer int travel_dist{0}; // Distancia a recorrer
@@ -82,7 +86,7 @@ class Notifier {
// --- Variables de estado --- // --- Variables de estado ---
std::vector<Notification> notifications_; // Lista de notificaciones activas std::vector<Notification> notifications_; // Lista de notificaciones activas
Color bg_color_; // Color de fondo de las notificaciones Color bg_color_; // Color de fondo de las notificaciones
int wait_time_; // Tiempo que se ve la notificación // Nota: wait_time_ eliminado, ahora se usa STAY_DURATION_S
bool stack_; // Indica si las notificaciones se apilan bool stack_; // Indica si las notificaciones se apilan
bool has_icons_; // Indica si el notificador tiene textura para iconos bool has_icons_; // Indica si el notificador tiene textura para iconos
@@ -90,13 +94,13 @@ class Notifier {
void clearFinishedNotifications(); // Elimina las notificaciones cuyo estado es FINISHED void clearFinishedNotifications(); // Elimina las notificaciones cuyo estado es FINISHED
void clearAllNotifications(); // Elimina todas las notificaciones activas, sin importar el estado void clearAllNotifications(); // Elimina todas las notificaciones activas, sin importar el estado
[[nodiscard]] auto shouldProcessNotification(int index) const -> bool; // Determina si una notificación debe ser procesada (según su estado y posición) [[nodiscard]] auto shouldProcessNotification(int index) const -> bool; // Determina si una notificación debe ser procesada (según su estado y posición)
void processNotification(int index); // Procesa una notificación en la posición dada: actualiza su estado y comportamiento visual void processNotification(int index, float delta_time); // Procesa una notificación en la posición dada: actualiza su estado y comportamiento visual
static void playNotificationSoundIfNeeded(const Notification &notification); // Reproduce sonido asociado si es necesario (dependiendo del estado o contenido) static void playNotificationSoundIfNeeded(const Notification &notification); // Reproduce sonido asociado si es necesario (dependiendo del estado o contenido)
void updateNotificationState(int index); // Actualiza el estado interno de una notificación (ej. de RISING a STAY) void updateNotificationState(int index, float delta_time); // Actualiza el estado interno de una notificación (ej. de RISING a STAY)
void handleRisingState(int index); // Lógica de animación para el estado RISING (apareciendo) void handleRisingState(int index, float delta_time); // Lógica de animación para el estado RISING (apareciendo)
void handleStayState(int index); // Lógica para mantener una notificación visible en el estado STAY void handleStayState(int index); // Lógica para mantener una notificación visible en el estado STAY
void handleVanishingState(int index); // Lógica de animación para el estado VANISHING (desapareciendo) void handleVanishingState(int index, float delta_time); // Lógica de animación para el estado VANISHING (desapareciendo)
static void moveNotificationVertically(Notification &notification, int direction); // Mueve verticalmente una notificación en una dirección dada (útil para animación en apilamiento) static void moveNotificationVertically(Notification &notification, float pixels_to_move); // Mueve verticalmente una notificación con la cantidad de pixels especificada
void transitionToStayState(int index); // Cambia el estado de una notificación de RISING a STAY cuando ha alcanzado su posición final void transitionToStayState(int index); // Cambia el estado de una notificación de RISING a STAY cuando ha alcanzado su posición final
// --- Constructores y destructor privados (singleton) --- // --- Constructores y destructor privados (singleton) ---