El proyecto que acompaña a la Parte 5 (clases 108–125) y a su capstone (clase 125).
Un enemigo que te busca. Tiene behavior tree, cono de visión, memoria y pathfinding con A*, y con eso solo ya recorre el ciclo entero:
patrulla → te ve → persigue → te pierde → busca donde te vio → vuelve a patrullar
RayCast2D (clase 116).AStarGrid2D sobre el mapa (clase 113).Controles: WASD o flechas para moverte. Escóndete tras un muro y mira qué hace.
ia-enemigo/
├── inicio/ ← empieza aquí: completa los TODO
│ ├── project.godot
│ ├── escenas/ (mundo, enemigo, jugador)
│ └── scripts/
│ ├── mundo.gd (nivel ASCII y reparto de dependencias — resuelto)
│ ├── navegador.gd (el servicio de A* — resuelto)
│ ├── jugador.gd (control y bot de pruebas — resuelto)
│ ├── enemigo.gd ← TU TRABAJO: percepción, memoria y acciones
│ └── bt/
│ ├── nodo_bt.gd (el contrato: EXITO / FALLO / EN_CURSO — resuelto)
│ ├── compuesto.gd (base de los nodos con hijos — resuelto)
│ ├── hoja.gd (Condicion y AccionBT — resuelto)
│ ├── secuencia.gd ← TU TRABAJO (TODO 1)
│ └── selector.gd ← TU TRABAJO (TODO 2)
└── solucion/ ← referencia completa
labs/ia-enemigo/inicio/project.godot → F5.TODO en orden:| TODO | Dónde | Qué consigues | Clase |
|---|---|---|---|
| 1 | bt/secuencia.gd |
El "Y" del árbol | 111 |
| 2 | bt/selector.gd |
El "O", y con él la prioridad | 111 |
| 3 | enemigo.gd |
Que te vea (distancia + cono + muros) | 116 |
| 4 | enemigo.gd |
Que te recuerde al perderte | 116 |
| 5 | enemigo.gd |
Que te persiga con A* | 113 |
| 6 | enemigo.gd |
Que patrulle entre los puntos | 125 |
Con 1 y 2 el árbol ya decide, pero el enemigo sigue ciego: verás IA: patrulla y poco más. Con 3 y 4 empieza a verte. Con 5 y 6 se mueve.
¿Atascado? Abre el archivo equivalente en
solucion/y compara. No es hacer trampa: leer código bueno es parte de aprender.
Con una FSM (clase 109), cada comportamiento nuevo te obliga a repasar todas las transiciones que ya tenías: cinco estados son veinte flechas posibles, y todas hay que pensarlas. Con un árbol, añadir un comportamiento es añadir una rama.
Todo el árbol se sostiene sobre dos ideas y nada más:
EXITO, FALLO o EN_CURSO.Y de ahí sale la idea que más cuesta ver: el orden de los hijos ES el diseño de la IA. El selector raíz de este lab es
raíz (Selector)
├── combatir (Secuencia) ¿lo veo? → persíguelo
├── buscar (Secuencia) ¿me queda memoria? → ve a donde lo vi
└── patrullar (Secuencia) recorre los puntos
Ese orden es lo único que hace que perseguirte gane a seguir la ronda. Cambia patrullar de sitio y tendrás un enemigo que te mira a la cara y se va a hacer su recorrido. Pruébalo: es un cambio de una línea y explica la Parte 5 entera.
Un detalle que se escapa siempre. Perseguir devuelve
EN_CURSO, noEXITO. Si devolvieraEXITO, la secuencia daría la rama por terminada y el selector bajaría a patrullar en el frame siguiente.EN_CURSOes lo que significa "sigo en ello, no me interrumpas".
Ordenados del más barato al más caro, y ese orden importa:
dot de dos vectores normalizados ya es el coseno del ángulo entre ellos: compararlo con cos(45°) cuesta una multiplicación, mientras que sacar el ángulo con acos() cuesta mucho más y da lo mismo.Se genera desde un mapa ASCII en scripts/mundo.gd. Edítalo y verás el cambio al instante:
'#' muro '.' suelo 'P' jugador 'E' enemigo 'o' punto de patrulla
Es el mismo truco de los otros labs: legible, editable con cualquier editor y revisable en un diff.
Por qué
AStarGrid2Dy noNavigationAgent2D. El agente de navegación es más cómodo en un juego real, pero calcula por detrás y contesta por señal, así que dos partidas iguales no dan exactamente lo mismo.AStarGrid2Dresponde en el acto y siempre igual. Para aprender el algoritmo es mejor (lo ves entero, clase 113), y además es lo que permite que la CI compruebe la IA en vez de solo mirar si arranca. Cuando montes un juego 3D con terreno irregular, usaNavigationAgent3D— la clase 114 lo enseña.
Inversor: devuelve EXITO donde su hijo da FALLO. Con él, "si NO lo veo" es una rama más._draw(): ver lo que la IA "piensa" es la mejor herramienta de depuración que vas a tener.Este lab se comprueba en CI de dos formas:
Esa prueba es posible porque el lab es determinista: AStarGrid2D es síncrono y el bot lleva un recorrido fijo, así que la misma partida da siempre la misma secuencia. Puedes repetirla en local:
godot --headless --path labs/ia-enemigo/solucion --quit-after 1500 -- --bot
# Debe imprimir, en este orden:
# IA: patrulla
# IA: persecucion
# IA: busqueda
# IA: patrulla