Tu primer juego: Pong en C++ paso a paso con IA

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

Pong es el "hola mundo" de los videojuegos. Si entiendes cómo construirlo desde cero, entiendes la estructura que comparten prácticamente todos los juegos en tiempo real.

Estructura del proyecto

En la lección anterior dejamos el entorno listo con SDL2 y CMake. Ahora vamos a usar exactamente esa base para construir Pong: dos paletas, una pelota que rebota, y un marcador simple.

pong/ ├── CMakeLists.txt ├── src/ │ └── main.cpp └── build/

Todo el juego va a vivir en un único `main.cpp` por simplicidad — en lecciones posteriores vamos a separar el código en múltiples archivos cuando el proyecto lo justifique. Por ahora, la prioridad es que entiendas el flujo completo sin saltar entre archivos.

El bucle principal del juego

Todo videojuego, sin importar su complejidad, tiene el mismo esqueleto: procesar input, actualizar el estado del mundo, y dibujar. Esto se llama el "game loop", y es probablemente el concepto más importante de todo este curso.

#include <SDL2/SDL.h> #include <iostream> const int ANCHO = 800; const int ALTO = 600; int main(int argc, char* argv[]) { SDL_Init(SDL_INIT_VIDEO); SDL_Window* ventana = SDL_CreateWindow("Pong", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, ANCHO, ALTO, SDL_WINDOW_SHOWN); SDL_Renderer* renderer = SDL_CreateRenderer(ventana, -1, SDL_RENDERER_ACCELERATED); bool corriendo = true; Uint32 tiempo_anterior = SDL_GetTicks(); while (corriendo) { // 1. Procesar input SDL_Event evento; while (SDL_PollEvent(&evento)) { if (evento.type == SDL_QUIT) corriendo = false; } // 2. Calcular delta time (tiempo entre fotogramas) Uint32 tiempo_actual = SDL_GetTicks(); float delta = (tiempo_actual - tiempo_anterior) / 1000.0f; tiempo_anterior = tiempo_actual; // 3. Actualizar (aquí irá la lógica del juego) // 4. Dibujar SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); SDL_RenderPresent(renderer); SDL_Delay(16); // aproximadamente 60 fotogramas por segundo } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(ventana); SDL_Quit(); return 0; }

El `delta` (tiempo transcurrido entre fotograma y fotograma) es clave: en vez de mover objetos una cantidad fija de píxeles por fotograma, los movemos una cantidad proporcional al tiempo real transcurrido. Esto hace que el juego se comporte igual en una computadora rápida que en una lenta.

Definiendo las paletas y la pelota

Vamos a representar cada elemento del juego con un `SDL_Rect` (para las paletas, que son rectángulos) y variables de posición y velocidad para la pelota.

struct Paleta { SDL_Rect rect; float velocidad_y = 0.0f; const float VELOCIDAD = 400.0f; // píxeles por segundo }; struct Pelota { SDL_Rect rect; float vel_x = 300.0f; float vel_y = 200.0f; }; Paleta jugador1 { { 30, ALTO / 2 - 50, 15, 100 } }; Paleta jugador2 { { ANCHO - 45, ALTO / 2 - 50, 15, 100 } }; Pelota pelota { { ANCHO / 2 - 8, ALTO / 2 - 8, 16, 16 } };

Detectando el input del jugador

Para un control más fluido que reaccionar a eventos discretos, usamos `SDL_GetKeyboardState`, que nos da el estado de todas las teclas en cada fotograma.

const Uint8* teclado = SDL_GetKeyboardState(nullptr); // Jugador 1: W / S jugador1.velocidad_y = 0.0f; if (teclado[SDL_SCANCODE_W]) jugador1.velocidad_y = -jugador1.VELOCIDAD; if (teclado[SDL_SCANCODE_S]) jugador1.velocidad_y = jugador1.VELOCIDAD; // Jugador 2: flechas arriba / abajo jugador2.velocidad_y = 0.0f; if (teclado[SDL_SCANCODE_UP]) jugador2.velocidad_y = -jugador2.VELOCIDAD; if (teclado[SDL_SCANCODE_DOWN]) jugador2.velocidad_y = jugador2.VELOCIDAD; jugador1.rect.y += static_cast<int>(jugador1.velocidad_y * delta); jugador2.rect.y += static_cast<int>(jugador2.velocidad_y * delta); // Evitar que las paletas salgan de la pantalla if (jugador1.rect.y < 0) jugador1.rect.y = 0; if (jugador1.rect.y + jugador1.rect.h > ALTO) jugador1.rect.y = ALTO - jugador1.rect.h; if (jugador2.rect.y < 0) jugador2.rect.y = 0; if (jugador2.rect.y + jugador2.rect.h > ALTO) jugador2.rect.y = ALTO - jugador2.rect.h;

Moviendo la pelota y rebotando en los bordes

pelota.rect.x += static_cast<int>(pelota.vel_x * delta); pelota.rect.y += static_cast<int>(pelota.vel_y * delta); // Rebote arriba/abajo if (pelota.rect.y <= 0 || pelota.rect.y + pelota.rect.h >= ALTO) { pelota.vel_y = -pelota.vel_y; } // Punto para un jugador (la pelota sale por izquierda o derecha) if (pelota.rect.x < 0) { std::cout << "Punto para el jugador 2" << std::endl; pelota.rect.x = ANCHO / 2 - 8; pelota.rect.y = ALTO / 2 - 8; pelota.vel_x = 300.0f; } if (pelota.rect.x > ANCHO) { std::cout << "Punto para el jugador 1" << std::endl; pelota.rect.x = ANCHO / 2 - 8; pelota.rect.y = ALTO / 2 - 8; pelota.vel_x = -300.0f; }

Nota que todavía no detectamos colisión entre la pelota y las paletas — eso lo vamos a formalizar con matemática de colisiones en la lección 5. Por ahora, para tener Pong jugable hoy mismo, podemos hacer una comprobación simple con `SDL_HasIntersection`:

if (SDL_HasIntersection(&pelota.rect, &jugador1.rect) || SDL_HasIntersection(&pelota.rect, &jugador2.rect)) { pelota.vel_x = -pelota.vel_x; }

Dibujando todo en pantalla

SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderFillRect(renderer, &jugador1.rect); SDL_RenderFillRect(renderer, &jugador2.rect); SDL_RenderFillRect(renderer, &pelota.rect); SDL_RenderPresent(renderer);

Compilando y probando

mkdir build && cd build cmake .. cmake --build . ./juego

Deberías ver dos paletas blancas y una pelota moviéndose, con W/S y las flechas controlando cada lado. Si algo no compila, este es un excelente momento para practicar el flujo de trabajo con tu asistente de IA: pégale el error completo del compilador (no solo la última línea) y pídele que te explique la causa antes de aplicar la corrección — así aprendes qué significaba el error, no solo cómo desapareció.

En la próxima lección vamos a reemplazar estos rectángulos blancos por sprites reales, cargando imágenes con SDL2_image y animándolas por fotogramas.

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