Parte: 20 — IA generativa y desarrollo de juegos asistido por IA · Fuente: Documentación oficial de las tecnologías de IA generativa · Prácticas de especificación de software ⏱️ Duración estimada: 110 min · Nivel: Avanzado
Aprender a convertir una idea vaga en una especificación lo bastante precisa como para que otra persona —o un modelo— pueda implementarla y para que tú puedas verificar el resultado. Esa es la habilidad real detrás de la etiqueta "prompt engineering": no son trucos de redacción, es la vieja disciplina de escribir requisitos, aplicada a un colaborador nuevo que es rápido, literal y no pregunta cuando duda.
Vas a recorrer la cadena completa idea → requisitos → GDD → arquitectura → tareas → implementación → tests, viendo en cada paso qué aporta la asistencia y qué debe decidir una persona. Y vas a construir tu propia biblioteca de prompts para las tareas recurrentes de gamedev: gameplay, shaders, IA, UI, depuración, diseño de niveles y testing. El criterio que guía todo: un prompt es bueno si su resultado se puede verificar.
Al finalizar, el alumno podrá:
| # | Tema | Por qué importa |
|---|---|---|
| 1 | Especificación vs prompt | El prompt es el vehículo; la especificación es el contenido. |
| 2 | Contexto explícito | El modelo no conoce tu proyecto; lo que no digas, se lo inventa. |
| 3 | Restricciones | Sin ellas, la respuesta será genérica y no encajará. |
| 4 | Criterios de aceptación | Sin ellos no se puede verificar, y sin verificar no sirve. |
| 5 | Formato de salida | Estructurado se valida; texto libre, no. |
| 6 | Descomposición | Tareas grandes producen resultados vagos. |
| 7 | Ejemplos (few-shot) | Un ejemplo vale más que tres párrafos de explicación. |
| 8 | Iteración dirigida | Corregir con precisión es más rápido que reescribir. |
| 9 | Biblioteca de prompts | Lo que funciona se guarda, se versiona y se comparte. |
| 10 | Cuándo el prompt no es el problema | Reconocerlo ahorra horas. |
Cualquier asistente de IA al que tengas acceso, o ninguno: el ejercicio de escribir la especificación es válido por sí mismo y es lo que se evalúa. Trabajaremos en docs/prompts/ del repositorio, versionado en git como cualquier otro recurso del equipo. Nada de esta clase depende de un proveedor concreto.
| Paso | Entrada | Salida | ¿Puede asistir? | Quién decide |
|---|---|---|---|---|
| Idea | Una frase | Concepto | Explorar variantes | Persona |
| Requisitos | Concepto | Qué debe hacer y qué no | Estructurar y detectar huecos | Persona |
| GDD | Requisitos | Documento de diseño | Redactar secciones | Persona |
| Arquitectura | Requisitos | Sistemas y sus límites | Proponer opciones y contrastarlas | Persona |
| Tareas | Arquitectura | Lista verificable | Descomponer | Persona revisa |
| Implementación | Tarea + contexto | Código | Sí, con verificación | Persona revisa |
| Tests | Criterios | Casos y aserciones | Sí, y es de lo más rentable | Persona revisa |
La fila de arquitectura está en negrita a propósito: es donde más tentador resulta delegar y donde peor sale. Un modelo propondrá una arquitectura razonable en abstracto; encajarla con lo que ya tienes, con tu equipo y con tu plazo es una decisión de contexto que no tiene.
## Contexto
Godot 4.3, GDScript. Proyecto con la arquitectura por capas de la clase 293:
`dominio/` sin nodos, `gameplay/` con nodos, comunicación por señales.
El inventario ya existe en `dominio/inventario/inventario.gd` con la API:
`agregar(id, n) -> int`, `quitar(id, n) -> bool`, `cabe(id, n) -> int`.
## Objetivo
Implementar `ordenar()` en `Inventario`: agrupa por tipo y rareza, compacta
stacks parciales y deja las ranuras vacías al final.
## Restricciones
- Sin dependencias externas ni nodos: la clase extiende `RefCounted`.
- No cambiar la firma de ningún método existente.
- Emitir `ranura_cambiada` solo para las ranuras que realmente cambien.
- Comentarios en español, solo donde expliquen un porqué no obvio.
## Criterios de aceptación
1. El total de unidades por item es idéntico antes y después.
2. Tras ordenar, no quedan dos stacks parciales del mismo item.
3. Las ranuras vacías están todas al final.
4. Ordenar dos veces seguidas no produce cambios en la segunda (idempotente).
5. Con el inventario vacío no falla y no emite señales.
## Formato de salida
Solo el código del método y los auxiliares privados que necesite, en un bloque
GDScript. Sin explicación previa.
Compárala con lo que la mayoría escribe: "hazme una función para ordenar el inventario". La diferencia en el resultado no es de estilo: es que la primera se puede verificar y la segunda no.
❌ "Implementa el sistema de combate del juego"
→ Demasiado grande. La respuesta será genérica y no encajará con nada.
✅ Descompuesto:
1. Objeto `Golpe` con sus campos y su traza (criterios: 3)
2. Reducción por armadura asintótica (criterios: 2, incluye extremos)
3. Cálculo de crítico con RNG inyectado (criterios: 2, incluye determinismo)
4. Ventana de invulnerabilidad e id de ataque (criterios: 3)
5. Integración de los cuatro en `PipelineDano` (criterios: 4)
6. Pruebas headless del pipeline completo (criterios: 1 por caso)
Regla práctica: si no puedes escribir los criterios de aceptación de una tarea en cinco líneas, la tarea es demasiado grande.
| Patrón | Cuándo | Cuándo NO |
|---|---|---|
| Ejemplos (few-shot) | Convenciones propias, formatos, estilo de comentario | Tareas estándar bien conocidas |
| Rol | Ajustar registro (documentación, nota de parche) | Como sustituto del contexto real |
| Pasos explícitos | Tareas con estructura (migración, refactor guiado) | Tareas de una línea |
| Autocrítica | Antes de aceptar una salida larga | Cuando ya tienes tests que lo comprueban |
| Salida estructurada | Siempre que vayas a procesar la salida | Cuando quieres explicación para leer |
| Iteración dirigida | Corregir un punto concreto | Cuando el planteamiento es el que falla |
Ejemplo de few-shot para una convención propia:
Genera la sección "Errores comunes" de una clase del curso siguiendo EXACTAMENTE
este formato y densidad:
| Síntoma / mensaje | Causa y cómo arreglar |
|-------------------|-----------------------|
| El progreso no persiste tras cerrar | Guardaste en `res://` (solo lectura al exportar). Usa siempre `user://`. |
| "Cannot call method 'store_string' on a null value" | `FileAccess.open` devolvió `null`. Comprueba el resultado antes de usarlo. |
Ahora genera 6 filas para el tema: sistema de inventario por ranuras.
Los síntomas deben ser lo que una persona VE o el mensaje EXACTO que aparece,
no una descripción abstracta del bug.
Devuelve SOLO un objeto JSON con este esquema, sin texto alrededor:
```json
{
"quest": {
"id": "string, snake_case, único",
"titulo": "string, máximo 60 caracteres",
"objetivos": [
{ "id": "string", "tipo": "matar|recoger|alcanzar|hablar",
"objetivo": "string", "cantidad": "int, 1-50" }
],
"recompensas": { "xp": "int, 50-2000", "oro": "int, 0-1000" }
}
}
```
Restricciones: entre 2 y 4 objetivos; los `objetivo` deben salir de esta lista
cerrada: ["lobo", "bandido", "piel_lobo", "zona_guarida", "guardia_puerta"].
Y del otro lado, la validación — porque pedir un formato no garantiza recibirlo:
func validar_quest_generada(texto: String) -> Dictionary:
var d = JSON.parse_string(texto)
if typeof(d) != TYPE_DICTIONARY or not d.has("quest"):
return {"ok": false, "error": "no es un objeto con clave 'quest'"}
var q: Dictionary = d["quest"]
var errores := []
if not str(q.get("id", "")).is_valid_identifier():
errores.append("id no es snake_case")
if str(q.get("titulo", "")).length() > 60:
errores.append("título demasiado largo")
var objetivos: Array = q.get("objetivos", [])
if objetivos.size() < 2 or objetivos.size() > 4:
errores.append("número de objetivos fuera de rango")
for o in objetivos:
# La lista cerrada NO es una sugerencia: se comprueba. Un objetivo
# inventado produce una quest imposible de completar.
if not str(o.get("objetivo", "")) in OBJETIVOS_VALIDOS:
errores.append("objetivo inexistente: %s" % o.get("objetivo", ""))
return {"ok": errores.is_empty(), "errores": errores, "quest": q}
<!-- docs/prompts/gameplay/sistema-nuevo.md — v3 -->
# Prompt: implementar un sistema de dominio
**Cuándo usarlo**: al añadir un sistema nuevo en `dominio/`.
**Verificación obligatoria**: compila, pasa `pruebas/<sistema>_test.gd`, revisión humana.
## Plantilla
## Contexto
Godot {VERSION}, GDScript. Arquitectura por capas: `dominio/` no extiende Node
ni usa `get_node`; comunicación hacia fuera por señales.
Sistemas existentes relacionados: {SISTEMAS}
API que debe respetar: {API}
## Objetivo
{OBJETIVO}
## Restricciones
- `extends RefCounted`; nada de nodos, escenas ni `get_tree()`.
- Toda aleatoriedad por un `RandomNumberGenerator` inyectado.
- Avance temporal por `tick(delta)` explícito, no `_process`.
- Comentarios solo donde expliquen un porqué no obvio.
## Criterios de aceptación
{CRITERIOS}
## Formato
Solo código GDScript. Sin explicación.
## Historial
- v3: añadida la restricción de RNG inyectado — sin ella generaba `randf()`
global y rompía el determinismo de los replays.
- v2: añadido `tick(delta)` explícito.
- v1: versión inicial.
Ese apartado "Historial" es lo que convierte una carpeta de prompts en conocimiento de equipo: cada línea es un error que ya no se repetirá.
| Síntoma del resultado | Causa probable | Arreglo |
|---|---|---|
| Genérico, "de tutorial" | Falta contexto del proyecto | Añade convenciones, API y ejemplos reales |
| Usa APIs que no existen | Contexto insuficiente o versión no indicada | Indica versión exacta y pega la API real |
| Correcto pero no encaja | Faltan restricciones | Enumera lo que no debe hacer |
| Cambia cosas que no tocaba | Alcance no acotado | Di explícitamente qué archivos y qué firmas no se tocan |
| Formato inconsistente | Sin esquema ni ejemplos | Da el esquema y un ejemplo completo |
| Se queda a medias | Tarea demasiado grande | Descompón |
| Plausible y falso | La tarea requiere hechos que no tiene | Aporta los hechos, o no es tarea para esto |
docs/prompts/ con tres prompts que uses de verdad, con su apartado de verificación.Entrega una biblioteca de prompts en docs/prompts/ con al menos cinco prompts para categorías distintas (gameplay, shaders, IA, UI, testing o depuración), cada uno con plantilla, cuándo usarlo, verificación obligatoria e historial; más una especificación completa de un sistema real y su validador de salida estructurada.
Criterio de aceptación: (a) cada prompt tiene los cinco bloques (contexto, objetivo, restricciones, criterios, formato) y declara explícitamente su verificación; (b) al menos dos prompts incluyen ejemplos few-shot con contenido real del proyecto; (c) la especificación entregada tiene al menos cinco criterios de aceptación comprobables por una máquina; (d) el validador de salida estructurada rechaza: JSON malformado, campo faltante, tipo incorrecto, valor fuera de rango y referencia a un id inexistente — con una prueba headless que lo demuestre; (e) la biblioteca está versionada en git y cada prompt tiene al menos una entrada de historial que explique un cambio y su motivo.
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| Cada resultado hay que reescribirlo entero | Falta contexto y restricciones. Usa la plantilla de cinco bloques. |
| El código usa métodos inexistentes de Godot | No se indicó la versión ni se pegó la API real. Ambas cosas. |
| El JSON generado no parsea | Se pidió "JSON" sin esquema ni "solo el objeto". Da esquema y exige salida limpia. |
| Se acepta contenido generado sin validar | Falta el validador. Pedir formato no es recibirlo. |
| Los prompts viven en el historial de chat de cada uno | No hay biblioteca. Llévalos al repositorio. |
| Se itera diez veces sobre lo mismo | La tarea es demasiado grande o inadecuada. Descompón o hazla tú. |
| El resultado es correcto pero rompe una convención | La convención no estaba escrita. Añádela a las restricciones y al prompt base. |
| El prompt tiene 3.000 palabras | Se está compensando una tarea mal planteada. Reduce el alcance. |
❓ ¿"Prompt engineering" es una habilidad real? La parte útil sí, y no es nueva: es escribir requisitos claros, descomponer problemas y definir criterios de aceptación. Eso vale igual para un colaborador humano, y de hecho el mejor indicador de que una especificación es buena es que una persona que no conoce el proyecto podría implementarla.
❓ ¿Merece la pena la biblioteca de prompts si trabajo solo? Sí, por la misma razón que merece la pena tener plantillas: cada vez que descubres una restricción que hay que añadir (como el RNG inyectado del ejemplo), la anotas una vez y no vuelves a perder tiempo con ese error.
❓ ¿Los ejemplos no gastan mucho contexto? Gastan, y compensan. Un ejemplo completo de tu convención real ahorra tres iteraciones. Si el contexto se queda corto, el problema es que la tarea es demasiado grande — vuelve a la descomposición.
❓ ¿Y si el modelo se inventa una API? Es lo esperado si no le das la real: rellenará el hueco con lo más plausible. La solución no es pedirle que no invente, es pegarle la API que debe usar. Y en cualquier caso, la verificación de la clase 328 lo detecta antes de que llegue al repositorio.
❓ ¿Esto sirve igual con cualquier proveedor? Sí, y por eso la clase no menciona ninguno. Contexto, restricciones, criterios y formato son propiedades de la especificación, no del modelo. Cambiar de proveedor puede exigir ajustar detalles; la estructura se mantiene.
JSON (parseo y validación en el motor): https://docs.godotengine.org/en/4.3/classes/class_json.html · uso: lectura de respaldo del objetivo; no se usa en el procedimientoClase 325 - IA generativa en desarrollo de videojuegos