Compare commits

6 Commits

Author SHA1 Message Date
19768cb72b delta-time: animated_sprite.cpp 2025-09-16 16:51:31 +02:00
26e0fd7247 delta-time: moving_sprite.cpp 2025-09-16 16:37:39 +02:00
e2fd470ad3 migració a delta time 2025-09-16 12:23:02 +02:00
a72ae0a5fc migració a delta time 2025-09-16 12:06:49 +02:00
7579594c22 migració a delta time 2025-09-16 10:48:22 +02:00
6c702e7e23 Logo: convertit per a usar delta time 2025-09-16 08:40:41 +02:00
28 changed files with 406 additions and 601 deletions

View File

@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(make:*)"
],
"deny": [],
"ask": []
}
}

View File

@@ -43,6 +43,10 @@ auto loadAnimationsFromFile(const std::string& file_path) -> AnimationsFileBuffe
std::vector<std::string> buffer; std::vector<std::string> buffer;
std::string line; std::string line;
while (std::getline(input_stream, line)) { while (std::getline(input_stream, line)) {
// Eliminar caracteres de retorno de carro (\r) al final de la línea
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
if (!line.empty()) { if (!line.empty()) {
buffer.push_back(line); buffer.push_back(line);
} }
@@ -82,7 +86,7 @@ auto AnimatedSprite::getAnimationIndex(const std::string& name) -> int {
return -1; return -1;
} }
// Calcula el frame correspondiente a la animación // Calcula el frame correspondiente a la animación (frame-based)
void AnimatedSprite::animate() { void AnimatedSprite::animate() {
if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) { if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) {
return; return;
@@ -112,6 +116,39 @@ void AnimatedSprite::animate() {
} }
} }
// Calcula el frame correspondiente a la animación (time-based)
void AnimatedSprite::animate(float deltaTime) {
if (animations_[current_animation_].speed == 0 || animations_[current_animation_].paused) {
return;
}
// Convertir speed (frames) a tiempo: speed frames = speed/60 segundos a 60fps
float frameTime = static_cast<float>(animations_[current_animation_].speed) / 60.0f;
// Acumular tiempo transcurrido
animations_[current_animation_].time_accumulator += deltaTime;
// Verificar si es momento de cambiar frame
if (animations_[current_animation_].time_accumulator >= frameTime) {
animations_[current_animation_].time_accumulator -= frameTime;
animations_[current_animation_].current_frame++;
// Si alcanza el final de la animación
if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size()) {
if (animations_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame
animations_[current_animation_].current_frame = animations_[current_animation_].frames.size() - 1;
animations_[current_animation_].completed = true;
} else { // Si hay loop, vuelve al frame indicado
animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].current_frame = animations_[current_animation_].loop;
}
}
// Actualizar el sprite clip
updateSpriteClip();
}
}
// Comprueba si ha terminado la animación // Comprueba si ha terminado la animación
auto AnimatedSprite::animationIsCompleted() -> bool { auto AnimatedSprite::animationIsCompleted() -> bool {
return animations_[current_animation_].completed; return animations_[current_animation_].completed;
@@ -126,10 +163,12 @@ void AnimatedSprite::setCurrentAnimation(const std::string& name, bool reset) {
if (reset) { if (reset) {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0; animations_[current_animation_].counter = 0;
animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
} else { } else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size() - 1); animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size() - 1);
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
} }
updateSpriteClip(); updateSpriteClip();
@@ -145,26 +184,35 @@ void AnimatedSprite::setCurrentAnimation(int index, bool reset) {
if (reset) { if (reset) {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0; animations_[current_animation_].counter = 0;
animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
} else { } else {
animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size()); animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size());
animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter;
animations_[current_animation_].time_accumulator = animations_[OLD_ANIMATION].time_accumulator;
animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed;
} }
updateSpriteClip(); updateSpriteClip();
} }
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto (frame-based)
void AnimatedSprite::update() { void AnimatedSprite::update() {
animate(); animate();
MovingSprite::update(); MovingSprite::update();
} }
// Actualiza las variables del objeto (time-based)
void AnimatedSprite::update(float deltaTime) {
animate(deltaTime);
MovingSprite::update(deltaTime);
}
// Reinicia la animación // Reinicia la animación
void AnimatedSprite::resetAnimation() { void AnimatedSprite::resetAnimation() {
animations_[current_animation_].current_frame = 0; animations_[current_animation_].current_frame = 0;
animations_[current_animation_].counter = 0; animations_[current_animation_].counter = 0;
animations_[current_animation_].time_accumulator = 0.0f;
animations_[current_animation_].completed = false; animations_[current_animation_].completed = false;
animations_[current_animation_].paused = false; animations_[current_animation_].paused = false;
updateSpriteClip(); updateSpriteClip();

View File

@@ -21,11 +21,12 @@ struct Animation {
std::string name; // Nombre de la animación std::string name; // Nombre de la animación
std::vector<SDL_FRect> frames; // Frames que componen la animación std::vector<SDL_FRect> frames; // Frames que componen la animación
int speed{DEFAULT_SPEED}; // Velocidad de reproducción int speed{DEFAULT_SPEED}; // Velocidad de reproducción (frame-based)
int loop{0}; // Frame de vuelta al terminar (-1 para no repetir) int loop{0}; // Frame de vuelta al terminar (-1 para no repetir)
bool completed{false}; // Indica si la animación ha finalizado bool completed{false}; // Indica si la animación ha finalizado
size_t current_frame{0}; // Frame actual en reproducción size_t current_frame{0}; // Frame actual en reproducción
int counter{0}; // Contador para la animación int counter{0}; // Contador para la animación (frame-based)
float time_accumulator{0.0f}; // Acumulador de tiempo para animaciones time-based
bool paused{false}; // La animación no avanza bool paused{false}; // La animación no avanza
Animation() = default; Animation() = default;
@@ -55,7 +56,8 @@ class AnimatedSprite : public MovingSprite {
~AnimatedSprite() override = default; ~AnimatedSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
void update() override; // Actualiza la animación void update() override; // Actualiza la animación (frame-based)
void update(float deltaTime); // Actualiza la animación (time-based)
// --- Control de animaciones --- // --- Control de animaciones ---
void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre void setCurrentAnimation(const std::string& name = "default", bool reset = true); // Establece la animación por nombre
@@ -78,7 +80,8 @@ class AnimatedSprite : public MovingSprite {
int current_animation_ = 0; // Índice de la animación activa int current_animation_ = 0; // Índice de la animación activa
// --- Métodos internos --- // --- Métodos internos ---
void animate(); // Calcula el frame correspondiente a la animación void animate(); // Calcula el frame correspondiente a la animación (frame-based)
void animate(float deltaTime); // Calcula el frame correspondiente a la animación (time-based)
void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source); // Carga la animación desde un vector de cadenas void loadFromAnimationsFileBuffer(const AnimationsFileBuffer& source); // Carga la animación desde un vector de cadenas
void processConfigLine(const std::string& line, AnimationConfig& config); // Procesa una línea de configuración void processConfigLine(const std::string& line, AnimationConfig& config); // Procesa una línea de configuración
void updateFrameCalculations(AnimationConfig& config); // Actualiza los cálculos basados en las dimensiones del frame void updateFrameCalculations(AnimationConfig& config); // Actualiza los cálculos basados en las dimensiones del frame

View File

@@ -126,7 +126,14 @@ void Background::initializeTextures() {
} }
// Actualiza la lógica del objeto // Actualiza la lógica del objeto
// Actualiza la lógica del objeto (compatibilidad)
void Background::update() { void Background::update() {
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f; // 16.67ms por frame a 60 FPS
update(FRAME_TIME_MS);
}
// Actualiza la lógica del objeto
void Background::update(float delta_time) {
// Actualiza la progresión y calcula transiciones // Actualiza la progresión y calcula transiciones
if (!manual_mode_) { if (!manual_mode_) {
updateProgression(); updateProgression();

View File

@@ -31,7 +31,8 @@ class Background {
~Background(); // Destructor ~Background(); // Destructor
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la lógica del objeto void update(); // Actualiza la lógica del objeto (compatibilidad)
void update(float delta_time); // Actualiza la lógica del objeto
void render(); // Dibuja el objeto void render(); // Dibuja el objeto
void reset(); // Reinicia la progresión void reset(); // Reinicia la progresión

View File

@@ -219,7 +219,7 @@ constexpr SDL_ScaleMode VIDEO_SCALE_MODE = SDL_ScaleMode::SDL_SCALEMODE_NEAREST;
constexpr bool VIDEO_FULLSCREEN = false; constexpr bool VIDEO_FULLSCREEN = false;
constexpr bool VIDEO_VSYNC = true; constexpr bool VIDEO_VSYNC = true;
constexpr bool VIDEO_INTEGER_SCALE = true; constexpr bool VIDEO_INTEGER_SCALE = true;
constexpr bool VIDEO_SHADERS = true; constexpr bool VIDEO_SHADERS = false;
// Music // Music
constexpr bool MUSIC_ENABLED = true; constexpr bool MUSIC_ENABLED = true;

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::HI_SCORE_TABLE; Section::name = Section::Name::INSTRUCTIONS;
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

@@ -4,20 +4,6 @@
#include <string> // Para basic_string, string #include <string> // Para basic_string, string
namespace shader { namespace shader {
bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &shaderSource, const std::string &fragmentShader = ""); bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader = "");
void render(); void render();
void cleanup();
bool isUsingOpenGL();
#ifdef __APPLE__
namespace metal {
bool initMetal(SDL_Window* window, SDL_Texture* backBuffer, const std::string& shaderFilename);
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height);
void updateMetalTexture(SDL_Texture* backBuffer);
void renderMetal();
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture);
void cleanupMetal();
}
#endif
} // namespace shader } // namespace shader

View File

@@ -1,387 +0,0 @@
#include "jail_shader.h"
#ifdef __APPLE__
#include <SDL3/SDL.h>
#include <SDL3/SDL_metal.h>
#include <Metal/Metal.h>
#include <QuartzCore/CAMetalLayer.h>
#include <CoreFoundation/CoreFoundation.h>
#include <stdexcept>
#include <vector>
#include "../asset.h"
namespace shader {
namespace metal {
// Metal objects
id<MTLDevice> device = nullptr;
id<MTLRenderPipelineState> pipelineState = nullptr;
id<MTLBuffer> vertexBuffer = nullptr;
id<MTLTexture> backBufferTexture = nullptr;
id<MTLTexture> gameCanvasTexture = nullptr; // Our custom render target texture
id<MTLSamplerState> sampler = nullptr;
// SDL objects (references from main shader module)
SDL_Window* win = nullptr;
SDL_Renderer* renderer = nullptr;
SDL_Texture* backBuffer = nullptr;
// Vertex data for fullscreen quad
struct Vertex {
float position[4]; // x, y, z, w
float texcoord[2]; // u, v
};
const Vertex quadVertices[] = {
// Position (x, y, z, w) // TexCoord (u, v) - Standard OpenGL-style coordinates
{{-1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}}, // Bottom-left
{{ 1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}, // Bottom-right
{{-1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}}, // Top-left
{{ 1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 0.0f}}, // Top-right
};
std::string loadMetalShader(const std::string& filename) {
// Try to load the .metal file from the same location as GLSL files
auto data = Asset::get()->loadData(filename);
if (!data.empty()) {
return std::string(data.begin(), data.end());
}
return "";
}
SDL_Texture* createMetalRenderTarget(SDL_Renderer* renderer, int width, int height) {
if (!renderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "createMetalRenderTarget: No renderer provided");
return nullptr;
}
// Crear textura Metal como render target
SDL_Texture* metalTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
if (!metalTexture) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal render target texture: %s", SDL_GetError());
return nullptr;
}
// Configurar filtrado nearest neighbor para look pixelado
SDL_SetTextureScaleMode(metalTexture, SDL_SCALEMODE_NEAREST);
// Try to extract and store the Metal texture directly
SDL_PropertiesID props = SDL_GetTextureProperties(metalTexture);
if (props != 0) {
const char* propertyNames[] = {
"SDL.texture.metal.texture",
"SDL.renderer.metal.texture",
"metal.texture",
"texture.metal",
"MTLTexture",
"texture"
};
for (const char* propName : propertyNames) {
gameCanvasTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
if (gameCanvasTexture) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Successfully extracted Metal texture via property '%s' (size: %lux%lu)",
propName, [gameCanvasTexture width], [gameCanvasTexture height]);
break;
}
}
}
if (!gameCanvasTexture) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not extract Metal texture from SDL texture - shaders may not work");
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Created Metal render target texture (%dx%d)", width, height);
return metalTexture;
}
bool initMetal(SDL_Window* window, SDL_Texture* backBufferTexture, const std::string& shaderFilename) {
// Store references
win = window;
backBuffer = backBufferTexture;
renderer = SDL_GetRenderer(window);
if (!renderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL renderer");
return false;
}
// Get Metal layer from SDL renderer and extract device from it
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*)SDL_GetRenderMetalLayer(renderer);
if (!metalLayer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal layer from SDL renderer");
return false;
}
device = metalLayer.device;
if (!device) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal device from layer");
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal device from SDL layer: %s", [[device name] UTF8String]);
// Note: We no longer need our own texture - we'll use the backBuffer directly
// Load and compile shaders
std::string metalShaderSource = loadMetalShader(shaderFilename);
if (metalShaderSource.empty()) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load Metal shader: %s", shaderFilename.c_str());
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded Metal shader %s (length: %zu)",
shaderFilename.c_str(), metalShaderSource.length());
NSString* shaderNSString = [NSString stringWithUTF8String:metalShaderSource.c_str()];
NSError* error = nil;
id<MTLLibrary> library = [device newLibraryWithSource:shaderNSString options:nil error:&error];
if (!library || error) {
if (error) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile Metal shader: %s",
[[error localizedDescription] UTF8String]);
}
return false;
}
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"vertex_main"];
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"fragment_main"];
if (!vertexFunction || !fragmentFunction) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load Metal shader functions");
return false;
}
// Create render pipeline
MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineDescriptor.vertexFunction = vertexFunction;
pipelineDescriptor.fragmentFunction = fragmentFunction;
pipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
// Set up vertex descriptor
MTLVertexDescriptor* vertexDescriptor = [[MTLVertexDescriptor alloc] init];
vertexDescriptor.attributes[0].format = MTLVertexFormatFloat4;
vertexDescriptor.attributes[0].offset = 0;
vertexDescriptor.attributes[0].bufferIndex = 0;
vertexDescriptor.attributes[1].format = MTLVertexFormatFloat2;
vertexDescriptor.attributes[1].offset = 4 * sizeof(float);
vertexDescriptor.attributes[1].bufferIndex = 0;
vertexDescriptor.layouts[0].stride = sizeof(Vertex);
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
pipelineDescriptor.vertexDescriptor = vertexDescriptor;
pipelineState = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
if (!pipelineState || error) {
if (error) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create Metal render pipeline: %s",
[[error localizedDescription] UTF8String]);
}
return false;
}
// Create vertex buffer
vertexBuffer = [device newBufferWithBytes:quadVertices
length:sizeof(quadVertices)
options:MTLResourceOptionCPUCacheModeDefault];
// Create sampler state for nearest neighbor filtering (pixelated look)
MTLSamplerDescriptor* samplerDescriptor = [[MTLSamplerDescriptor alloc] init];
samplerDescriptor.minFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.magFilter = MTLSamplerMinMagFilterNearest;
samplerDescriptor.sAddressMode = MTLSamplerAddressModeClampToEdge;
samplerDescriptor.tAddressMode = MTLSamplerAddressModeClampToEdge;
sampler = [device newSamplerStateWithDescriptor:samplerDescriptor];
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "** Metal shader system initialized successfully");
return true;
}
void updateMetalTexture(SDL_Texture* backBuffer) {
if (!device || !backBuffer) return;
// Only log this occasionally to avoid spam
static int attemptCount = 0;
static bool hasLogged = false;
// Try multiple property names that SDL3 might use
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
if (props != 0) {
const char* propertyNames[] = {
"SDL.texture.metal.texture",
"SDL.renderer.metal.texture",
"metal.texture",
"texture.metal",
"MTLTexture",
"texture"
};
for (const char* propName : propertyNames) {
id<MTLTexture> sdlMetalTexture = (__bridge id<MTLTexture>)SDL_GetPointerProperty(props, propName, nullptr);
if (sdlMetalTexture) {
backBufferTexture = sdlMetalTexture;
if (!hasLogged) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Got Metal texture via property '%s' (size: %lux%lu)",
propName, [backBufferTexture width], [backBufferTexture height]);
hasLogged = true;
}
return;
}
}
}
// If we can't get the texture after several attempts, log once and continue
if (!hasLogged && attemptCount++ > 10) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Could not access SDL Metal texture after %d attempts - shader will be skipped", attemptCount);
hasLogged = true;
}
}
void renderMetal() {
if (!renderer || !device || !pipelineState) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal render failed: missing components");
return;
}
// Correct pipeline: backBuffer → Metal shaders → screen (no double rendering)
// Try to get the Metal texture directly from the SDL backBuffer texture
updateMetalTexture(backBuffer);
if (!backBufferTexture) {
static int fallbackLogCount = 0;
if (fallbackLogCount++ < 3) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Metal texture not available - falling back to normal SDL rendering (attempt %d)", fallbackLogCount);
}
// Fallback: render without shaders using normal SDL path
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
SDL_RenderPresent(renderer);
return;
}
// Apply Metal CRT shader directly: backBuffer texture → screen
SDL_SetRenderTarget(renderer, nullptr);
// Get Metal command encoder to render directly to screen
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
if (encoder_ptr) {
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
static int debugCount = 0;
if (debugCount++ < 5) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal render attempt %d: encoder=%p, pipeline=%p, texture=%p",
debugCount, encoder, pipelineState, backBufferTexture);
}
// Apply CRT shader effect directly to backBuffer texture
[encoder setRenderPipelineState:pipelineState];
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
[encoder setFragmentTexture:backBufferTexture atIndex:0];
[encoder setFragmentSamplerState:sampler atIndex:0];
// Draw fullscreen quad with CRT effect directly to screen
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
static int successCount = 0;
if (successCount++ < 5) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT shader to backBuffer (attempt %d) - texture size: %lux%lu",
successCount, [backBufferTexture width], [backBufferTexture height]);
}
} else {
// Fallback: render normally without shaders
SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr);
static int fallbackCount = 0;
if (fallbackCount++ < 3) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder - fallback rendering");
}
}
SDL_RenderPresent(renderer);
}
void renderWithPostProcessing(SDL_Renderer* renderer, SDL_Texture* sourceTexture) {
if (!renderer || !sourceTexture || !device || !pipelineState) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing failed: missing components");
// Fallback: render normally without shaders
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
SDL_RenderPresent(renderer);
return;
}
// Use our stored Metal texture if available
id<MTLTexture> metalTexture = gameCanvasTexture;
if (!metalTexture) {
static int fallbackLogCount = 0;
if (fallbackLogCount++ < 3) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "gameCanvasTexture not available - falling back to normal rendering (attempt %d)", fallbackLogCount);
}
// Fallback: render normally without shaders
SDL_SetRenderTarget(renderer, nullptr);
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
SDL_RenderPresent(renderer);
return;
}
// Apply Metal CRT shader post-processing: sourceTexture → screen
SDL_SetRenderTarget(renderer, nullptr);
// Get Metal command encoder to render directly to screen
void* encoder_ptr = SDL_GetRenderMetalCommandEncoder(renderer);
if (encoder_ptr) {
id<MTLRenderCommandEncoder> encoder = (__bridge id<MTLRenderCommandEncoder>)encoder_ptr;
static int debugCount = 0;
if (debugCount++ < 3) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal post-processing attempt %d: encoder=%p, pipeline=%p, texture=%p",
debugCount, encoder, pipelineState, metalTexture);
}
// Apply CRT shader effect to sourceTexture
[encoder setRenderPipelineState:pipelineState];
[encoder setVertexBuffer:vertexBuffer offset:0 atIndex:0];
[encoder setFragmentTexture:metalTexture atIndex:0];
[encoder setFragmentSamplerState:sampler atIndex:0];
// Draw fullscreen quad with CRT effect directly to screen
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
static int successCount = 0;
if (successCount++ < 3) {
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Applied CRT post-processing (attempt %d) - texture size: %lux%lu",
successCount, [metalTexture width], [metalTexture height]);
}
} else {
// Fallback: render normally without shaders
SDL_RenderTexture(renderer, sourceTexture, nullptr, nullptr);
static int fallbackCount = 0;
if (fallbackCount++ < 3) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Failed to get Metal command encoder for post-processing - fallback rendering");
}
}
SDL_RenderPresent(renderer);
}
void cleanupMetal() {
// Release Metal objects (ARC handles most of this automatically)
pipelineState = nullptr;
backBufferTexture = nullptr;
gameCanvasTexture = nullptr;
vertexBuffer = nullptr;
sampler = nullptr;
device = nullptr;
renderer = nullptr;
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Metal shader system cleaned up");
}
} // namespace metal
} // namespace shader
#endif // __APPLE__

View File

@@ -82,6 +82,11 @@ void Fade::update() {
} }
} }
// Compatibilidad delta-time (ignora el parámetro ya que usa SDL_GetTicks)
void Fade::update(float delta_time) {
update(); // Llama al método original
}
void Fade::updatePreState() { void Fade::updatePreState() {
// Sistema basado en tiempo únicamente // Sistema basado en tiempo únicamente
Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_; Uint32 elapsed_time = SDL_GetTicks() - pre_start_time_;

View File

@@ -37,10 +37,11 @@ class Fade {
~Fade(); ~Fade();
// --- Métodos principales --- // --- Métodos principales ---
void reset(); // Resetea variables para reutilizar el fade void reset(); // Resetea variables para reutilizar el fade
void render(); // Dibuja la transición en pantalla void render(); // Dibuja la transición en pantalla
void update(); // Actualiza el estado interno void update(); // Actualiza el estado interno (ya usa tiempo real)
void activate(); // Activa el fade void update(float delta_time); // Compatibilidad delta-time (ignora el parámetro)
void activate(); // Activa el fade
// --- Configuración --- // --- Configuración ---
void setColor(Uint8 r, Uint8 g, Uint8 b); // Establece el color RGB del fade void setColor(Uint8 r, Uint8 g, Uint8 b); // Establece el color RGB del fade

View File

@@ -53,7 +53,7 @@ void MovingSprite::stop() {
flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite
} }
// Mueve el sprite // Mueve el sprite (frame-based)
void MovingSprite::move() { void MovingSprite::move() {
x_ += vx_; x_ += vx_;
y_ += vy_; y_ += vy_;
@@ -65,16 +65,34 @@ void MovingSprite::move() {
pos_.y = static_cast<int>(y_); pos_.y = static_cast<int>(y_);
} }
// Actualiza las variables internas del objeto // Mueve el sprite (time-based)
void MovingSprite::move(float deltaTime) {
x_ += vx_ * deltaTime;
y_ += vy_ * deltaTime;
vx_ += ax_ * deltaTime;
vy_ += ay_ * deltaTime;
pos_.x = static_cast<int>(x_);
pos_.y = static_cast<int>(y_);
}
// Actualiza las variables internas del objeto (frame-based)
void MovingSprite::update() { void MovingSprite::update() {
move(); move();
rotate(); rotate();
} }
// Actualiza las variables internas del objeto (time-based)
void MovingSprite::update(float deltaTime) {
move(deltaTime);
rotate(deltaTime);
}
// Muestra el sprite por pantalla // Muestra el sprite por pantalla
void MovingSprite::render() { getTexture()->render(pos_.x, pos_.y, &sprite_clip_, horizontal_zoom_, vertical_zoom_, rotate_.angle, &rotate_.center, flip_); } void MovingSprite::render() { getTexture()->render(pos_.x, pos_.y, &sprite_clip_, horizontal_zoom_, vertical_zoom_, rotate_.angle, &rotate_.center, flip_); }
// Establece la rotacion // Establece la rotacion (frame-based)
void MovingSprite::rotate() { void MovingSprite::rotate() {
if (rotate_.enabled) { if (rotate_.enabled) {
++rotate_.counter; ++rotate_.counter;
@@ -85,6 +103,15 @@ void MovingSprite::rotate() {
} }
} }
// Establece la rotacion (time-based)
void MovingSprite::rotate(float deltaTime) {
if (rotate_.enabled) {
// Convertir speed (frames) a tiempo: speed frames = speed/60 segundos a 60fps
float rotationSpeed = static_cast<float>(rotate_.speed) / 60.0f;
rotate_.angle += rotate_.amount * (deltaTime / rotationSpeed);
}
}
// Activa o desactiva el efecto de rotación // Activa o desactiva el efecto de rotación
void MovingSprite::setRotate(bool enable) { void MovingSprite::setRotate(bool enable) {
rotate_.enabled = enable; rotate_.enabled = enable;

View File

@@ -29,10 +29,11 @@ class MovingSprite : public Sprite {
~MovingSprite() override = default; ~MovingSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
virtual void update(); // Actualiza las variables internas del objeto virtual void update(); // Actualiza las variables internas del objeto (frame-based)
void clear() override; // Reinicia todas las variables a cero virtual void update(float deltaTime); // Actualiza las variables internas del objeto (time-based)
void stop(); // Elimina el movimiento del sprite void clear() override; // Reinicia todas las variables a cero
void render() override; // Muestra el sprite por pantalla void stop(); // Elimina el movimiento del sprite
void render() override; // Muestra el sprite por pantalla
// --- Configuración --- // --- Configuración ---
void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto
@@ -79,6 +80,8 @@ class MovingSprite : public Sprite {
// --- Métodos internos --- // --- Métodos internos ---
void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo
void move(); // Mueve el sprite según velocidad y aceleración void move(); // Mueve el sprite según velocidad y aceleración (frame-based)
void rotate(); // Rota el sprite según los parámetros de rotación void move(float deltaTime); // Mueve el sprite según velocidad y aceleración (time-based)
void rotate(); // Rota el sprite según los parámetros de rotación (frame-based)
void rotate(float deltaTime); // Rota el sprite según los parámetros de rotación (time-based)
}; };

View File

@@ -4,6 +4,13 @@
#include <functional> // Para function #include <functional> // Para function
#include <utility> // Para move #include <utility> // Para move
// Constructor para paths por puntos (compatibilidad)
Path::Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init)
: spots(spots_init), is_point_path(true) {
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f;
waiting_time_ms = static_cast<float>(waiting_counter_init) * FRAME_TIME_MS;
}
// Devuelve un vector con los puntos que conforman la ruta // Devuelve un vector con los puntos que conforman la ruta
auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easing_function) -> std::vector<SDL_FPoint> { auto createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easing_function) -> std::vector<SDL_FPoint> {
std::vector<SDL_FPoint> v; std::vector<SDL_FPoint> v;
@@ -32,10 +39,16 @@ auto createPath(float start, float end, PathType type, float fixed_pos, int step
return v; return v;
} }
// Actualiza la posición y comprueba si ha llegado a su destino // Actualiza la posición y comprueba si ha llegado a su destino (compatibilidad)
void PathSprite::update() { void PathSprite::update() {
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f; // 16.67ms por frame a 60 FPS
update(FRAME_TIME_MS);
}
// Actualiza la posición y comprueba si ha llegado a su destino
void PathSprite::update(float delta_time) {
if (enabled_ && !has_finished_) { if (enabled_ && !has_finished_) {
moveThroughCurrentPath(); moveThroughCurrentPath(delta_time);
goToNextPathOrDie(); goToNextPathOrDie();
} }
} }
@@ -79,7 +92,13 @@ void PathSprite::addPath(Path path, bool centered) {
// Añade un recorrido // Añade un recorrido
void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easing_function, int waiting_counter) { void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easing_function, int waiting_counter) {
paths_.emplace_back(createPath(start, end, type, fixed_pos, steps, easing_function), waiting_counter); // Convertir frames a milisegundos
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f;
float duration_ms = static_cast<float>(steps) * FRAME_TIME_MS;
float waiting_ms = static_cast<float>(waiting_counter) * FRAME_TIME_MS;
paths_.emplace_back(static_cast<float>(start), static_cast<float>(end), type, static_cast<float>(fixed_pos),
duration_ms, waiting_ms, easing_function);
} }
// Añade un recorrido // Añade un recorrido
@@ -95,35 +114,78 @@ void PathSprite::enable() {
enabled_ = true; enabled_ = true;
// Establece la posición // Establece la posición inicial
auto &path = paths_.at(current_path_); auto &path = paths_.at(current_path_);
const auto &p = path.spots.at(path.counter); if (path.is_point_path) {
setPosition(p); const auto &p = path.spots.at(path.counter);
setPosition(p);
} else {
// Para paths generados, establecer posición inicial
SDL_FPoint initial_pos;
if (path.type == PathType::HORIZONTAL) {
initial_pos = {path.start_pos, path.fixed_pos};
} else {
initial_pos = {path.fixed_pos, path.start_pos};
}
setPosition(initial_pos);
}
} }
// Coloca el sprite en los diferentes puntos del recorrido // Coloca el sprite en los diferentes puntos del recorrido
void PathSprite::moveThroughCurrentPath() { void PathSprite::moveThroughCurrentPath(float delta_time) {
auto &path = paths_.at(current_path_); auto &path = paths_.at(current_path_);
// Establece la posición if (path.is_point_path) {
const auto &p = path.spots.at(path.counter); // Lógica para paths por puntos (compatibilidad)
setPosition(p); const auto &p = path.spots.at(path.counter);
setPosition(p);
// Comprobar si ha terminado el recorrido if (!path.on_destination) {
if (!path.on_destination) { ++path.counter;
++path.counter; if (path.counter >= static_cast<int>(path.spots.size())) {
if (path.counter >= static_cast<int>(path.spots.size())) { path.on_destination = true;
path.on_destination = true; path.counter = static_cast<int>(path.spots.size()) - 1;
path.counter = static_cast<int>(path.spots.size()) - 1; }
} }
}
// Comprobar si ha terminado la espera if (path.on_destination) {
if (path.on_destination) { path.waiting_elapsed += delta_time;
if (path.waiting_counter == 0) { if (path.waiting_elapsed >= path.waiting_time_ms) {
path.finished = true; path.finished = true;
}
}
} else {
// Lógica para paths generados en tiempo real
if (!path.on_destination) {
path.elapsed_time += delta_time;
// Calcular progreso (0.0 a 1.0)
float progress = path.elapsed_time / path.duration_ms;
if (progress >= 1.0f) {
progress = 1.0f;
path.on_destination = true;
}
// Aplicar función de easing
double eased_progress = path.easing_function(progress);
// Calcular posición actual
float current_pos = path.start_pos + (path.end_pos - path.start_pos) * static_cast<float>(eased_progress);
// Establecer posición según el tipo
SDL_FPoint position;
if (path.type == PathType::HORIZONTAL) {
position = {current_pos, path.fixed_pos};
} else {
position = {path.fixed_pos, current_pos};
}
setPosition(position);
} else { } else {
--path.waiting_counter; // Esperar en destino
path.waiting_elapsed += delta_time;
if (path.waiting_elapsed >= path.waiting_time_ms) {
path.finished = true;
}
} }
} }
} }

View File

@@ -24,17 +24,31 @@ enum class PathCentered { // Centrado del recorrido
}; };
// --- Estructuras --- // --- Estructuras ---
struct Path { // Define un recorrido para el sprite struct Path { // Define un recorrido para el sprite
std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite float start_pos; // Posición inicial
int waiting_counter; // Tiempo de espera una vez en el destino float end_pos; // Posición final
bool on_destination = false; // Indica si ha llegado al destino PathType type; // Tipo de movimiento (horizontal/vertical)
bool finished = false; // Indica si ha terminado de esperarse float fixed_pos; // Posición fija en el eje contrario
int counter = 0; // Contador interno float duration_ms; // Duración de la animación en milisegundos
float waiting_time_ms; // Tiempo de espera una vez en el destino
std::function<double(double)> easing_function; // Función de easing
float elapsed_time = 0.0f; // Tiempo transcurrido
float waiting_elapsed = 0.0f; // Tiempo de espera transcurrido
bool on_destination = false; // Indica si ha llegado al destino
bool finished = false; // Indica si ha terminado de esperarse
// Constructor // Constructor para paths generados
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init) Path(float start, float end, PathType path_type, float fixed, float duration, float waiting, std::function<double(double)> easing)
: spots(spots_init), : start_pos(start), end_pos(end), type(path_type), fixed_pos(fixed),
waiting_counter(waiting_counter_init) {} duration_ms(duration), waiting_time_ms(waiting), easing_function(std::move(easing)) {}
// Constructor para paths por puntos (mantenemos compatibilidad)
Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init);
// Variables para paths por puntos
std::vector<SDL_FPoint> spots; // Solo para paths por puntos
int counter = 0; // Solo para paths por puntos
bool is_point_path = false; // Indica si es un path por puntos
}; };
// --- Funciones --- // --- Funciones ---
@@ -49,7 +63,8 @@ class PathSprite : public Sprite {
~PathSprite() override = default; ~PathSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza la posición del sprite según el recorrido void update(); // Actualiza la posición del sprite según el recorrido (compatibilidad)
void update(float delta_time); // Actualiza la posición del sprite según el recorrido
void render() override; // Muestra el sprite por pantalla void render() override; // Muestra el sprite por pantalla
// --- Gestión de recorridos --- // --- Gestión de recorridos ---
@@ -72,6 +87,6 @@ class PathSprite : public Sprite {
std::vector<Path> paths_; // Caminos a recorrer por el sprite std::vector<Path> paths_; // Caminos a recorrer por el sprite
// --- Métodos internos --- // --- Métodos internos ---
void moveThroughCurrentPath(); // Coloca el sprite en los diferentes puntos del recorrido void moveThroughCurrentPath(float delta_time); // Coloca el sprite en los diferentes puntos del recorrido
void goToNextPathOrDie(); // Cambia de recorrido o finaliza void goToNextPathOrDie(); // Cambia de recorrido o finaliza
}; };

View File

@@ -44,21 +44,8 @@ Screen::Screen()
initSDLVideo(); initSDLVideo();
// Crea la textura de destino // Crea la textura de destino
#ifdef __APPLE__
const auto render_name = SDL_GetRendererName(renderer_);
if (render_name && !strncmp(render_name, "metal", 5)) {
// Usar nuestra propia Metal texture como render target
game_canvas_ = shader::metal::createMetalRenderTarget(renderer_, param.game.width, param.game.height);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Using custom Metal render target for game_canvas_");
} else {
// Fallback para otros renderers
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
}
#else
game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); game_canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height);
SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST); SDL_SetTextureScaleMode(game_canvas_, SDL_SCALEMODE_NEAREST);
#endif
// Crea el objeto de texto // Crea el objeto de texto
createText(); createText();
@@ -112,18 +99,7 @@ void Screen::renderPresent() {
clean(); clean();
if (Options::video.shaders) { if (Options::video.shaders) {
#ifdef __APPLE__
const auto render_name = SDL_GetRendererName(renderer_);
if (render_name && !strncmp(render_name, "metal", 5)) {
// Use Metal post-processing with our custom render target
shader::metal::renderWithPostProcessing(renderer_, game_canvas_);
} else {
// Fallback to standard shader system for non-Metal renderers
shader::render();
}
#else
shader::render(); shader::render();
#endif
} else { } else {
SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr); SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr);
SDL_RenderPresent(renderer_); SDL_RenderPresent(renderer_);
@@ -320,29 +296,17 @@ auto Screen::initSDLVideo() -> bool {
// Obtener información de la pantalla // Obtener información de la pantalla
getDisplayInfo(); getDisplayInfo();
#ifdef __APPLE__
// Configurar hint para Metal
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal")) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Failed to set Metal hint!");
}
// Configurar flags para la creación de la ventana
SDL_WindowFlags window_flags = SDL_WINDOW_METAL;
#else // NOT APPLE
// Configurar hint para OpenGL // Configurar hint para OpenGL
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) { if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Warning: Failed to set OpenGL hint!"); "Warning: Failed to set OpenGL hint!");
} }
// Configurar flags para la creación de la ventana
// Crear ventana
SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL; SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL;
#endif
// Configurar flags para la creación de la ventana
if (Options::video.fullscreen) { if (Options::video.fullscreen) {
window_flags |= SDL_WINDOW_FULLSCREEN; window_flags |= SDL_WINDOW_FULLSCREEN;
} }
// Crear ventana
window_ = SDL_CreateWindow( window_ = SDL_CreateWindow(
Options::window.caption.c_str(), Options::window.caption.c_str(),
param.game.width * Options::window.zoom, param.game.width * Options::window.zoom,

View File

@@ -33,7 +33,7 @@ HiScoreTable::HiScoreTable()
backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)),
fade_(std::make_unique<Fade>()), fade_(std::make_unique<Fade>()),
background_(std::make_unique<Background>()), background_(std::make_unique<Background>()),
ticks_(0), last_time_(0),
view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}), view_area_(SDL_FRect{0, 0, param.game.width, param.game.height}),
fade_mode_(Fade::Mode::IN), fade_mode_(Fade::Mode::IN),
background_fade_color_(Color(0, 0, 0)) { background_fade_color_(Color(0, 0, 0)) {
@@ -53,17 +53,14 @@ HiScoreTable::~HiScoreTable() {
} }
// Actualiza las variables // Actualiza las variables
void HiScoreTable::update() { void HiScoreTable::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { Screen::get()->update(); // Actualiza el objeto screen
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen
updateSprites(); // Actualiza las posiciones de los sprites de texto updateSprites(delta_time); // Actualiza las posiciones de los sprites de texto
background_->update(); // Actualiza el fondo background_->update(delta_time); // Actualiza el fondo
updateFade(); // 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(); Audio::update();
} }
@@ -117,20 +114,32 @@ void HiScoreTable::checkInput() {
GlobalInputs::check(); GlobalInputs::check();
} }
// Calcula el tiempo transcurrido desde el último frame
float HiScoreTable::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para la pantalla de instrucciones // Bucle para la pantalla de instrucciones
void HiScoreTable::run() { void HiScoreTable::run() {
last_time_ = SDL_GetTicks();
Audio::get()->playMusic("title.ogg"); Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::HI_SCORE_TABLE) { while (Section::name == Section::Name::HI_SCORE_TABLE) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
} }
// Gestiona el fade // Gestiona el fade
void HiScoreTable::updateFade() { void HiScoreTable::updateFade(float delta_time) {
fade_->update(); fade_->update(delta_time);
if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) { if (fade_->hasEnded() && fade_mode_ == Fade::Mode::IN) {
fade_->reset(); fade_->reset();
@@ -249,7 +258,7 @@ void HiScoreTable::createSprites() {
} }
// Actualiza las posiciones de los sprites de texto // Actualiza las posiciones de los sprites de texto
void HiScoreTable::updateSprites() { void HiScoreTable::updateSprites(float delta_time) {
constexpr int INIT_COUNTER = 190; constexpr int INIT_COUNTER = 190;
const int COUNTER_BETWEEN_ENTRIES = 16; const int COUNTER_BETWEEN_ENTRIES = 16;
if (counter_ >= INIT_COUNTER) { if (counter_ >= INIT_COUNTER) {
@@ -262,7 +271,7 @@ void HiScoreTable::updateSprites() {
} }
} }
for (auto const &entry : entry_names_) { for (auto const &entry : entry_names_) {
entry->update(); entry->update(delta_time);
} }
glowEntryNames(); glowEntryNames();

View File

@@ -45,26 +45,27 @@ class HiScoreTable {
// --- Variables --- // --- Variables ---
Uint16 counter_ = 0; // Contador Uint16 counter_ = 0; // Contador
Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla
Fade::Mode fade_mode_; // Modo de fade a utilizar Fade::Mode fade_mode_; // Modo de fade a utilizar
Color background_fade_color_; // Color de atenuación del fondo Color background_fade_color_; // Color de atenuación del fondo
std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables void update(float delta_time); // Actualiza las variables
void render(); // Pinta en pantalla void render(); // Pinta en pantalla
static void checkEvents(); // Comprueba los eventos static void checkEvents(); // Comprueba los eventos
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
static auto format(int number) -> std::string; // Convierte un entero a un string con separadores de miles static auto format(int number) -> std::string; // Convierte un entero a un string con separadores de miles
void fillTexture(); // Dibuja los sprites en la textura void fillTexture(); // Dibuja los sprites en la textura
void updateFade(); // Gestiona el fade void updateFade(float delta_time); // Gestiona el fade
void createSprites(); // Crea los sprites con los textos void createSprites(); // Crea los sprites con los textos
void updateSprites(); // Actualiza las posiciones de los sprites de texto void updateSprites(float delta_time); // Actualiza las posiciones de los sprites de texto
void initFade(); // Inicializa el fade void initFade(); // Inicializa el fade
void initBackground(); // Inicializa el fondo void initBackground(); // Inicializa el fondo
auto getEntryColor(int counter) -> Color; // Obtiene un color del vector de colores de entradas auto getEntryColor(int counter) -> Color; // Obtiene un color del vector de colores de entradas
void iniEntryColors(); // Inicializa los colores de las entradas void iniEntryColors(); // Inicializa los colores de las entradas
void glowEntryNames(); // Hace brillar los nombres de la tabla de records void glowEntryNames(); // Hace brillar los nombres de la tabla de records
void updateCounter(); // Gestiona el contador void updateCounter(); // Gestiona el contador
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
}; };

View File

@@ -204,18 +204,15 @@ void Instructions::fillBackbuffer() {
} }
// Actualiza las variables // Actualiza las variables
void Instructions::update() { void Instructions::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { Screen::get()->update(); // Actualiza el objeto screen
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen
counter_++; // Incrementa el contador counter_++; // Incrementa el contador
updateSprites(); // Actualiza los sprites updateSprites(); // Actualiza los sprites
updateBackbuffer(); // Gestiona la textura con los graficos updateBackbuffer(); // Gestiona la textura con los graficos
tiled_bg_->update(); // Actualiza el mosaico de fondo tiled_bg_->update(delta_time); // Actualiza el mosaico de fondo
fade_->update(); // Actualiza el objeto "fade" fade_->update(delta_time); // Actualiza el objeto "fade"
fillBackbuffer(); // Rellena el backbuffer fillBackbuffer(); // Rellena el backbuffer
}
Audio::update(); Audio::update();
} }
@@ -255,12 +252,24 @@ void Instructions::checkInput() {
GlobalInputs::check(); GlobalInputs::check();
} }
// Calcula el tiempo transcurrido desde el último frame
float Instructions::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para la pantalla de instrucciones // Bucle para la pantalla de instrucciones
void Instructions::run() { void Instructions::run() {
last_time_ = SDL_GetTicks();
Audio::get()->playMusic("title.ogg"); Audio::get()->playMusic("title.ogg");
while (Section::name == Section::Name::INSTRUCTIONS) { while (Section::name == Section::Name::INSTRUCTIONS) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }

View File

@@ -63,7 +63,7 @@ class Instructions {
// --- Variables --- // --- Variables ---
int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla
SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista
float item_space_ = 2.0; // Espacio entre los items en pantalla float item_space_ = 2.0; // Espacio entre los items en pantalla
@@ -73,7 +73,7 @@ class Instructions {
bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables void update(float delta_time); // Actualiza las variables
void render(); // Pinta en pantalla void render(); // Pinta en pantalla
static void checkEvents(); // Comprueba los eventos static void checkEvents(); // Comprueba los eventos
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
@@ -82,7 +82,8 @@ class Instructions {
void iniSprites(); // Inicializa los sprites de los items void iniSprites(); // Inicializa los sprites de los items
void updateSprites(); // Actualiza los sprites void updateSprites(); // Actualiza los sprites
static auto initializeLines(int height) -> std::vector<Line>; // Inicializa las líneas animadas static auto initializeLines(int height) -> std::vector<Line>; // Inicializa las líneas animadas
static auto moveLines(std::vector<Line> &lines, int width, float duration, Uint32 start_delay) -> bool; // Mueve las líneas static auto moveLines(std::vector<Line> &lines, int width, float duration, Uint32 start_delay) -> bool; // Mueve las líneas (ya usa tiempo real)
static void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas static void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas
void updateBackbuffer(); // Gestiona la textura con los gráficos void updateBackbuffer(); // Gestiona la textura con los gráficos
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
}; };

View File

@@ -207,24 +207,21 @@ void Intro::switchText(int from_index, int to_index) {
} }
// Actualiza las variables del objeto // Actualiza las variables del objeto
void Intro::update() { void Intro::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { Screen::get()->update(); // Actualiza el objeto screen
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks
Screen::get()->update(); // Actualiza el objeto screen
tiled_bg_->update(); // Actualiza el fondo tiled_bg_->update(delta_time); // Actualiza el fondo
switch (state_) { switch (state_) {
case State::SCENES: case State::SCENES:
updateSprites(); updateSprites(delta_time);
updateTexts(); updateTexts(delta_time);
updateScenes(); updateScenes();
break; break;
case State::POST: case State::POST:
updatePostState(); updatePostState();
break; break;
}
} }
Audio::update(); Audio::update();
@@ -253,12 +250,24 @@ void Intro::render() {
SCREEN->render(); // Vuelca el contenido del renderizador en pantalla SCREEN->render(); // Vuelca el contenido del renderizador en pantalla
} }
// Calcula el tiempo transcurrido desde el último frame
float Intro::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle principal // Bucle principal
void Intro::run() { void Intro::run() {
last_time_ = SDL_GetTicks();
Audio::get()->playMusic("intro.ogg", 0); Audio::get()->playMusic("intro.ogg", 0);
while (Section::name == Section::Name::INTRO) { while (Section::name == Section::Name::INTRO) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }
@@ -444,20 +453,20 @@ void Intro::initTexts() {
} }
// Actualiza los sprites // Actualiza los sprites
void Intro::updateSprites() { void Intro::updateSprites(float delta_time) {
for (auto &sprite : card_sprites_) { for (auto &sprite : card_sprites_) {
sprite->update(); sprite->update(delta_time);
} }
for (auto &sprite : shadow_sprites_) { for (auto &sprite : shadow_sprites_) {
sprite->update(); sprite->update(delta_time);
} }
} }
// Actualiza los textos // Actualiza los textos
void Intro::updateTexts() { void Intro::updateTexts(float delta_time) {
for (auto &text : texts_) { for (auto &text : texts_) {
text->update(); text->update(delta_time);
} }
} }

View File

@@ -42,7 +42,7 @@ class Intro {
std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico
// --- Variables --- // --- Variables ---
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
int scene_ = 0; // Indica qué escena está activa int scene_ = 0; // Indica qué escena está activa
State state_ = State::SCENES; // Estado principal de la intro State state_ = State::SCENES; // Estado principal de la intro
PostState post_state_ = PostState::STOP_BG; // Estado POST PostState post_state_ = PostState::STOP_BG; // Estado POST
@@ -50,19 +50,20 @@ class Intro {
Color bg_color_ = param.intro.bg_color; // Color de fondo Color bg_color_ = param.intro.bg_color; // Color de fondo
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables del objeto void update(float delta_time); // Actualiza las variables del objeto
void render(); // Dibuja el objeto en pantalla void render(); // Dibuja el objeto en pantalla
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
static void checkEvents(); // Comprueba los eventos static void checkEvents(); // Comprueba los eventos
void updateScenes(); // Actualiza las escenas de la intro void updateScenes(); // Actualiza las escenas de la intro
void initSprites(); // Inicializa las imágenes void initSprites(); // Inicializa las imágenes
void initTexts(); // Inicializa los textos void initTexts(); // Inicializa los textos
void updateSprites(); // Actualiza los sprites void updateSprites(float delta_time); // Actualiza los sprites
void updateTexts(); // Actualiza los textos void updateTexts(float delta_time); // Actualiza los textos
void renderSprites(); // Dibuja los sprites void renderSprites(); // Dibuja los sprites
void renderTexts(); // Dibuja los textos void renderTexts(); // Dibuja los textos
static void renderTextRect(); // Dibuja el rectangulo de fondo del texto; static void renderTextRect(); // Dibuja el rectangulo de fondo del texto;
void updatePostState(); // Actualiza el estado POST void updatePostState(); // Actualiza el estado POST
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
// --- Métodos para manejar cada escena individualmente --- // --- Métodos para manejar cada escena individualmente ---
void updateScene0(); void updateScene0();

View File

@@ -79,21 +79,23 @@ void Logo::checkInput() {
} }
// Gestiona el logo de JAILGAMES // Gestiona el logo de JAILGAMES
void Logo::updateJAILGAMES() { void Logo::updateJAILGAMES(float delta_time) {
if (counter_ == 30) { if (counter_ == 30) {
Audio::get()->playSound("logo.wav"); Audio::get()->playSound("logo.wav");
} }
if (counter_ > 30) { if (counter_ > 30) {
const float pixels_to_move = SPEED * delta_time;
for (int i = 0; i < (int)jail_sprite_.size(); ++i) { for (int i = 0; i < (int)jail_sprite_.size(); ++i) {
if (jail_sprite_[i]->getX() != dest_.x) { if (jail_sprite_[i]->getX() != dest_.x) {
if (i % 2 == 0) { if (i % 2 == 0) {
jail_sprite_[i]->incX(-SPEED); jail_sprite_[i]->incX(-pixels_to_move);
if (jail_sprite_[i]->getX() < dest_.x) { if (jail_sprite_[i]->getX() < dest_.x) {
jail_sprite_[i]->setX(dest_.x); jail_sprite_[i]->setX(dest_.x);
} }
} else { } else {
jail_sprite_[i]->incX(SPEED); jail_sprite_[i]->incX(pixels_to_move);
if (jail_sprite_[i]->getX() > dest_.x) { if (jail_sprite_[i]->getX() > dest_.x) {
jail_sprite_[i]->setX(dest_.x); jail_sprite_[i]->setX(dest_.x);
} }
@@ -129,16 +131,21 @@ void Logo::updateTextureColors() {
} }
// Actualiza las variables // Actualiza las variables
void Logo::update() { void Logo::update(float delta_time) {
if (SDL_GetTicks() - ticks_ > param.game.speed) { static float logic_accumulator = 0.0f;
ticks_ = SDL_GetTicks(); // Actualiza el contador de ticks logic_accumulator += delta_time;
Screen::get()->update(); // Actualiza el objeto screen
updateJAILGAMES(); // Actualiza el logo de JAILGAMES // Ejecutar lógica a 60 FPS (cada 16.67ms) para mantener consistencia en counter_ y colores
constexpr float LOGIC_FRAME_TIME = 1000.0f / 60.0f;
if (logic_accumulator >= LOGIC_FRAME_TIME) {
Screen::get()->update(); // Actualiza el objeto screen
updateTextureColors(); // Actualiza los colores de las texturas updateTextureColors(); // Actualiza los colores de las texturas
++counter_; // Gestiona el contador ++counter_; // Gestiona el contador
logic_accumulator -= LOGIC_FRAME_TIME;
} }
updateJAILGAMES(delta_time); // Actualiza el logo de JAILGAMES con delta-time real
Audio::update(); Audio::update();
} }
@@ -154,11 +161,23 @@ void Logo::render() {
SCREEN->render(); SCREEN->render();
} }
// Calcula el tiempo transcurrido desde el último frame
float Logo::calculateDeltaTime() {
const Uint64 current_time = SDL_GetTicks();
const float delta_time = static_cast<float>(current_time - last_time_);
last_time_ = current_time;
return delta_time;
}
// Bucle para el logo del juego // Bucle para el logo del juego
void Logo::run() { void Logo::run() {
last_time_ = SDL_GetTicks();
while (Section::name == Section::Name::LOGO) { while (Section::name == Section::Name::LOGO) {
const float delta_time = calculateDeltaTime();
checkInput(); checkInput();
update(); update(delta_time);
checkEvents(); // Tiene que ir antes del render checkEvents(); // Tiene que ir antes del render
render(); render();
} }

View File

@@ -31,7 +31,7 @@ class Logo {
static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro
static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo
static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo
static constexpr int SPEED = 8; // Velocidad de desplazamiento de cada línea static constexpr float SPEED = 8.0f / 15.0f; // Velocidad de desplazamiento de cada línea (píxeles por ms)
// --- Objetos y punteros --- // --- Objetos y punteros ---
std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998" std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998"
@@ -42,15 +42,16 @@ class Logo {
// --- Variables --- // --- Variables ---
std::vector<Color> color_; // Vector con los colores para el fade std::vector<Color> color_; // Vector con los colores para el fade
int counter_ = 0; // Contador int counter_ = 0; // Contador
Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa Uint64 last_time_ = 0; // Último timestamp para calcular delta-time
SDL_FPoint dest_; // Posición donde dibujar el logo SDL_FPoint dest_; // Posición donde dibujar el logo
// --- Métodos internos --- // --- Métodos internos ---
void update(); // Actualiza las variables void update(float delta_time); // Actualiza las variables
void render(); // Dibuja en pantalla void render(); // Dibuja en pantalla
static void checkEvents(); // Comprueba el manejador de eventos static void checkEvents(); // Comprueba el manejador de eventos
static void checkInput(); // Comprueba las entradas static void checkInput(); // Comprueba las entradas
void updateJAILGAMES(); // Gestiona el logo de JAILGAMES void updateJAILGAMES(float delta_time); // Gestiona el logo de JAILGAMES
void renderJAILGAMES(); // Renderiza el logo de JAILGAMES void renderJAILGAMES(); // Renderiza el logo de JAILGAMES
void updateTextureColors(); // Gestiona el color de las texturas void updateTextureColors(); // Gestiona el color de las texturas
float calculateDeltaTime(); // Calcula el tiempo transcurrido desde el último frame
}; };

View File

@@ -81,9 +81,15 @@ void TiledBG::render() {
SDL_RenderTexture(renderer_, canvas_, &window_, &pos_); SDL_RenderTexture(renderer_, canvas_, &window_, &pos_);
} }
// Actualiza la lógica de la clase // Actualiza la lógica de la clase (compatibilidad)
void TiledBG::update() { void TiledBG::update() {
updateDesp(); constexpr float FRAME_TIME_MS = 1000.0f / 60.0f; // 16.67ms por frame a 60 FPS
update(FRAME_TIME_MS);
}
// Actualiza la lógica de la clase
void TiledBG::update(float delta_time) {
updateDesp(delta_time);
updateStop(); updateStop();
switch (mode_) { switch (mode_) {

View File

@@ -24,8 +24,9 @@ class TiledBG {
~TiledBG(); ~TiledBG();
// --- Métodos principales --- // --- Métodos principales ---
void render(); // Pinta la clase en pantalla void render(); // Pinta la clase en pantalla
void update(); // Actualiza la lógica de la clase void update(); // Actualiza la lógica de la clase (compatibilidad)
void update(float delta_time); // Actualiza la lógica de la clase
// --- Configuración --- // --- Configuración ---
void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad
@@ -54,7 +55,8 @@ class TiledBG {
bool stopping_ = false; // Indica si se está deteniendo bool stopping_ = false; // Indica si se está deteniendo
// --- Métodos internos --- // --- Métodos internos ---
void fillTexture(); // Rellena la textura con el contenido void fillTexture(); // Rellena la textura con el contenido
void updateDesp() { desp_ += speed_; } // Actualiza el desplazamiento void updateDesp() { desp_ += speed_; } // Actualiza el desplazamiento (compatibilidad)
void updateStop(); // Detiene el desplazamiento de forma ordenada void updateDesp(float delta_time) { desp_ += speed_ * delta_time / (1000.0f / 60.0f); } // Actualiza el desplazamiento
void updateStop(); // Detiene el desplazamiento de forma ordenada
}; };

View File

@@ -3,15 +3,14 @@
#include "text.h" // Para Text #include "text.h" // Para Text
// Actualiza el objeto // Actualiza el objeto
void Writer::update() { void Writer::update(float delta_time) {
if (enabled_) { if (enabled_) {
if (!completed_) { if (!completed_) {
// No completado // No completado
if (writing_counter_ > 0) { writing_timer_ += delta_time;
writing_counter_--; if (writing_timer_ >= speed_ms_) {
} else {
index_++; index_++;
writing_counter_ = speed_; writing_timer_ = 0.0f;
} }
if (index_ == length_) { if (index_ == length_) {
@@ -19,10 +18,8 @@ void Writer::update() {
} }
} else { } else {
// Completado // Completado
finished_ = enabled_counter_ <= 0; enabled_timer_ += delta_time;
if (!finished_) { finished_ = enabled_timer_ >= enabled_timer_target_;
enabled_counter_--;
}
} }
} }
} }
@@ -57,8 +54,10 @@ void Writer::setCaption(const std::string &text) {
// Establece el valor de la variable // Establece el valor de la variable
void Writer::setSpeed(int value) { void Writer::setSpeed(int value) {
speed_ = value; // Convierte frames a milisegundos (frames * 16.67ms)
writing_counter_ = value; constexpr float FRAME_TIME_MS = 1000.0f / 60.0f;
speed_ms_ = static_cast<float>(value) * FRAME_TIME_MS;
writing_timer_ = 0.0f;
} }
// Establece el valor de la variable // Establece el valor de la variable
@@ -73,7 +72,10 @@ auto Writer::isEnabled() const -> bool {
// Establece el valor de la variable // Establece el valor de la variable
void Writer::setFinishedCounter(int time) { void Writer::setFinishedCounter(int time) {
enabled_counter_ = time; // Convierte frames a milisegundos (frames * 16.67ms)
constexpr float FRAME_TIME_MS = 1000.0f / 60.0f;
enabled_timer_target_ = static_cast<float>(time) * FRAME_TIME_MS;
enabled_timer_ = 0.0f;
} }
// Centra la cadena de texto a un punto X // Centra la cadena de texto a un punto X

View File

@@ -15,7 +15,7 @@ class Writer {
~Writer() = default; ~Writer() = default;
// --- Métodos principales --- // --- Métodos principales ---
void update(); // Actualiza el objeto void update(float delta_time); // Actualiza el objeto
void render() const; // Dibuja el objeto en pantalla void render() const; // Dibuja el objeto en pantalla
// --- Setters --- // --- Setters ---
@@ -38,16 +38,17 @@ class Writer {
std::shared_ptr<Text> text_; // Objeto encargado de escribir el texto std::shared_ptr<Text> text_; // Objeto encargado de escribir el texto
// --- Variables de estado --- // --- Variables de estado ---
std::string caption_; // El texto para escribir std::string caption_; // El texto para escribir
int pos_x_ = 0; // Posición en el eje X donde empezar a escribir el texto int pos_x_ = 0; // Posición en el eje X donde empezar a escribir el texto
int pos_y_ = 0; // Posición en el eje Y donde empezar a escribir el texto int pos_y_ = 0; // Posición en el eje Y donde empezar a escribir el texto
int kerning_ = 0; // Kerning del texto, es decir, espaciado entre caracteres int kerning_ = 0; // Kerning del texto, es decir, espaciado entre caracteres
int speed_ = 0; // Velocidad de escritura float speed_ms_ = 0.0f; // Velocidad de escritura en milisegundos
int writing_counter_ = 0; // Temporizador de escritura para cada caracter float writing_timer_ = 0.0f; // Temporizador de escritura para cada caracter
int index_ = 0; // Posición del texto que se está escribiendo int index_ = 0; // Posición del texto que se está escribiendo
int length_ = 0; // Longitud de la cadena a escribir int length_ = 0; // Longitud de la cadena a escribir
int enabled_counter_ = 0; // Temporizador para deshabilitar el objeto float enabled_timer_ = 0.0f; // Temporizador para deshabilitar el objeto
bool completed_ = false; // Indica si se ha escrito todo el texto float enabled_timer_target_ = 0.0f; // Tiempo objetivo para deshabilitar el objeto
bool enabled_ = false; // Indica si el objeto está habilitado bool completed_ = false; // Indica si se ha escrito todo el texto
bool finished_ = false; // Indica si ya ha terminado bool enabled_ = false; // Indica si el objeto está habilitado
bool finished_ = false; // Indica si ya ha terminado
}; };