Sprites y animación 2D

By Carlos Montiel | Especialista en IA Empresarial
Publicado: 2026-07-28 | Por: Carlos Montiel | Lectura: ~4 minutos

Los rectángulos de colores sirven para probar lógica, pero ningún jugador quiere ver eso. Hoy convertimos esos rectángulos en personajes animados de verdad.

De rectángulos a texturas

En la lección anterior construimos Pong usando rectángulos sólidos dibujados con `SDL_RenderFillRect`. Eso funciona para prototipar, pero un juego real usa sprites: imágenes que se cargan como texturas y se dibujan en pantalla. Vamos a usar `SDL2_image` para cargar PNGs, incluyendo transparencia (canal alfa).

#include <SDL2/SDL.h> #include <SDL2/SDL_image.h> SDL_Texture* cargar_textura(SDL_Renderer* renderer, const char* ruta) { SDL_Surface* superficie = IMG_Load(ruta); if (!superficie) { SDL_Log("Error al cargar imagen %s: %s", ruta, IMG_GetError()); return nullptr; } SDL_Texture* textura = SDL_CreateTextureFromSurface(renderer, superficie); SDL_FreeSurface(superficie); // la superficie ya no se necesita, la textura la reemplaza return textura; }

No olvides inicializar `SDL2_image` antes de usarlo, y liberar la textura cuando termine el programa:

IMG_Init(IMG_INIT_PNG); // ... uso normal del juego ... SDL_DestroyTexture(mi_textura); IMG_Quit();

Qué es un spritesheet

En vez de tener un archivo de imagen por cada fotograma de animación (caminar, saltar, atacar), lo habitual es empaquetar todos los fotogramas en una sola imagen llamada spritesheet, organizada en una cuadrícula. Para dibujar un fotograma específico, usamos un `SDL_Rect` como "recorte" (source rect) sobre esa imagen.

struct Spritesheet { SDL_Texture* textura; int ancho_fotograma; int alto_fotograma; int columnas; }; SDL_Rect obtener_recorte(const Spritesheet& hoja, int indice_fotograma) { int col = indice_fotograma % hoja.columnas; int fila = indice_fotograma / hoja.columnas; return SDL_Rect { col * hoja.ancho_fotograma, fila * hoja.alto_fotograma, hoja.ancho_fotograma, hoja.alto_fotograma }; }

Dibujando un fotograma específico

`SDL_RenderCopy` recibe un rectángulo de origen (de dónde recortar en la textura) y un rectángulo de destino (dónde dibujarlo en pantalla).

void dibujar_sprite(SDL_Renderer* renderer, const Spritesheet& hoja, int indice_fotograma, int x, int y, int escala = 1) { SDL_Rect origen = obtener_recorte(hoja, indice_fotograma); SDL_Rect destino { x, y, hoja.ancho_fotograma * escala, hoja.alto_fotograma * escala }; SDL_RenderCopy(renderer, hoja.textura, &origen, &destino); }

Construyendo un componente de animación

Para animar, necesitamos saber cuánto tiempo lleva mostrándose el fotograma actual y cuándo avanzar al siguiente. Esto lo encapsulamos en una estructura reutilizable.

struct Animacion { int fotograma_actual = 0; int total_fotogramas; float tiempo_por_fotograma; // en segundos float tiempo_acumulado = 0.0f; void actualizar(float delta) { tiempo_acumulado += delta; if (tiempo_acumulado >= tiempo_por_fotograma) { tiempo_acumulado -= tiempo_por_fotograma; fotograma_actual = (fotograma_actual + 1) % total_fotogramas; } } };

Uso típico dentro del bucle principal:

Animacion anim_caminar { 0, 6, 0.1f }; // 6 fotogramas, cambia cada 0.1 segundos // dentro del game loop, en la fase de "actualizar": anim_caminar.actualizar(delta); // en la fase de "dibujar": dibujar_sprite(renderer, hoja_personaje, anim_caminar.fotograma_actual, jugador_x, jugador_y, 2);

Volteando el sprite según la dirección

Un problema común: el personaje camina hacia la izquierda pero el sprite sigue mirando a la derecha. SDL2 resuelve esto con `SDL_RenderCopyEx`, que permite voltear horizontalmente sin necesitar una segunda spritesheet.

void dibujar_sprite_direccional(SDL_Renderer* renderer, const Spritesheet& hoja, int indice_fotograma, int x, int y, bool mirando_izquierda) { SDL_Rect origen = obtener_recorte(hoja, indice_fotograma); SDL_Rect destino { x, y, hoja.ancho_fotograma * 2, hoja.alto_fotograma * 2 }; SDL_RendererFlip flip = mirando_izquierda ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; SDL_RenderCopyEx(renderer, hoja.textura, &origen, &destino, 0.0, nullptr, flip); }

Cambiando de animación según el estado

En un juego real, un personaje tiene varias animaciones: reposo, caminar, saltar. Un enfoque simple (que luego formalizaremos con una máquina de estados en la lección 7) es tener un `enum` con el estado actual y elegir qué `Animacion` usar.

enum class EstadoPersonaje { REPOSO, CAMINANDO, SALTANDO }; EstadoPersonaje estado_actual = EstadoPersonaje::REPOSO; Animacion anim_reposo { 0, 2, 0.3f }; Animacion anim_caminando { 0, 6, 0.1f }; Animacion anim_saltando { 0, 3, 0.15f }; Animacion& obtener_animacion_activa() { switch (estado_actual) { case EstadoPersonaje::CAMINANDO: return anim_caminando; case EstadoPersonaje::SALTANDO: return anim_saltando; default: return anim_reposo; } }

Un consejo para trabajar con IA en esta lección

Los bugs de animación suelen ser sutiles: el sprite se "traba" en un fotograma, o parpadea, o se recorta mal. Cuando esto pase, en vez de pedirle a tu asistente de IA "arregla mi animación", dale contexto específico: pégale la estructura `Animacion`, dile exactamente qué fotograma esperabas ver y cuál viste, y el valor de tu `escala` y `ancho_fotograma`. Un asistente de IA con datos concretos detecta en segundos errores como una división mal hecha en `columnas` o un `tiempo_por_fotograma` que quedó en 0, algo que a simple vista es fácil pasar por alto.

Con sprites y animación resueltos, en la próxima lección vamos a formalizar algo que en Pong resolvimos de forma improvisada: la detección de colisiones, con matemática real (AABB y círculos) que vas a reutilizar en cada juego que hagas de aquí en adelante.

Carlos Montiel
Arquitecto de Soluciones IA Empresarial
Especialista en LLMs, Agentes y Orquestación
guatemalia.com/#contacto · info@guatemalia.com

¿Necesitas implementar IA en tu empresa?

Carlos Montiel es arquitecto de soluciones IA empresarial. Implementa LLMs, Agentes, RAG y orquestadores en empresas de Guatemala y Latinoamérica. Contáctalo para una consultoría.

Contactar a Carlos Montiel

info@guatemalia.com