Tank Force
Realizado durante el año 2022
Si el juego Gran Cerdo fue sobre lógica pura, este fue sobre arquitectura y gestión de recursos en tiempo real. Abandonamos el paradigma procedural para entrar de lleno en la Programación Orientada a Objetos (POO).
Como esta vez tenía libertad creativa total, decidí aprovechar la simplicidad gráfica para clonar un clásico: Battle City, pero construido desde cero sobre el motor SFML.
1. El Motor y el Boilerplate
El primer obstáculo no fue el código, sino el entorno. Configurar SFML en C++ puede ser frustrante por la gestión de librerías estáticas y dinámicas. Después de pelear con los errores de linker, decidí crear mi propio boilerplate de SFML.
Esto no solo solucionó el problema actual, sino que optimizó mi flujo de trabajo futuro: ahora puedo iniciar prototipos rápidos sin perder tiempo en configuración.
2. Matemáticas Vectoriales y Sprites Dinámicos
Mover un cuadrado por la pantalla es fácil; hacer que un tanque se sienta real no lo es.
El desafío interesante fue la torreta independiente. El cuerpo del tanque se mueve con las teclas (WASD), pero la torreta debe seguir al mouse. Tuve que calcular el ángulo de rotación exacto entre la posición del tanque y el puntero del mouse.
Además, tuve que resolver la sincronización: la torreta es una entidad separada pero debe heredar la posición del chasis en cada frame para mantenerse “pegada” visualmente, independientemente de cómo rote o se traslade la base.
3. Árbol de Comportamiento: Prioridades y “Falla Humana”
No quería enemigos estáticos. Diseñé un árbol de comportamiento con un sistema de prioridades simple pero efectivo:
- Objetivo Primario: Destruir el puente (condición de victoria enemiga).
- Objetivo de Oportunidad: Si el jugador entra en un radio de amenaza, la prioridad cambia y atacan al tanque.
Pero surgió un problema inesperado: la computadora no falla. Si el enemigo apuntaba a mis coordenadas exactas (sprite.getPosition()), sus disparos eran perfectos e imposibles de esquivar.
Para hacerlo jugable, implementé un “buffer de error”: los enemigos apunta a mi posición con un ligero desfase o retraso, simulando el tiempo de reacción humano y permitiendo al jugador esquivar proyectiles.
4. Gestión de Memoria y Rendimiento
Implementé listas de punteros para gestionar los proyectiles. Cada bala es una entidad viva que se chequea en cada frame:
- Si colisiona: Se destruye y libera memoria.
- Si sale de la pantalla: Se elimina de la lista inmediatamente.
Esta limpieza constante evita fugas de memoria (memory leaks) y mantiene el juego fluido incluso cuando hay decenas de balas en pantalla.
5. Diseño de Niveles basado en Arrays
Para no depender de herramientas externas, creé un sistema de renderizado de mapas basado en texto. El nivel es una matriz (Array) donde cada carácter representa una entidad (Árbol, Casa, Calle). Esto me permitió diseñar y balancear el nivel simplemente editando un archivo de código, iterando rápidamente sobre la jugabilidad.
Detalles que suman:
- Herencia y Polimorfismo: Clases base para
Entidad,EnemigoyProyectil. - Persistencia visual: Los enemigos destruidos quedan en el mapa y sus restos tienen colisión física, permitiendo usarlos como barricadas improvisadas.
Stack Tecnológico: C++, SFML, POO.