← Volver al blog

Godot 4: Pon reglas de señales en tu prompt para que MCP conecte la barra de salud

Antes de pedir a Godot MCP en Cursor, define un contrato de señal health_changed para que el HUD se conecte sin rutas get_node profundas: plantillas de prompt, ejemplos GDScript, anti-patrones y aceptación en Play.

Publicado
  • Godot MCP
  • Godot 4
  • signals
  • HUD
  • GDScript
  • Cursor
  • AI game development
  • tutorial

El problema: rutas de nodo frágiles

El fallo más común de “la IA rompió mi escena” en Godot no es un error de sintaxis—es un script de barra de salud que codifica rutas fijas:

$"/root/Game/Player/HitPoints".hp  # o get_node("../../Player/...")

Reorganiza el árbol o instancia Player como escena hija, y esa ruta muere. Godot MCP sigue tu prompt fielmente—si no prohíbes rutas fijas, el modelo seguirá inventándolas.

Este post hace un solo trabajo: en Cursor (o Claude Code), establece primero un contrato de señal, luego deja que MCP conecte la salud del jugador a la barra del HUD. Sin guía de instalación, sin construcción completa de plataformas.

PasoQué haces
1Nombra la señal, argumentos, emisor / receptor
2Prompt a MCP con restricciones para editar scripts y conexiones
3Acepta en Play; corrige el prompt usando anti-patrones

Relacionado:

Contexto: Organización de escenas — las escenas hijas deben exponer señales / exports, no rutas profundas.

Por qué las reglas de señales van en el prompt

EnfoqueCorto plazoDespués de reorganizar
get_node("../Player/Health")RápidoA menudo se rompe
@onready var bar = %HealthBar (nombre único)Bien en una escenaAún frágil entre escenas
signal health_changed(current, max) + connectDos líneas extraLos árboles de HUD y Player pueden cambiar independientemente

MCP no tiene “memoria de arquitectura” duradera a menos que repitas restricciones. Un bloque de contrato pegable es más barato que limpiar rutas fijas de cada diff.

Esqueleto mínimo de nodos

No necesitas una UI completa. v1 solo necesita:

Game (Node2D / Node)
├── Player (CharacterBody2D)  ← player_health.gd
│   └── ...
└── HUD (CanvasLayer)
    └── HealthBar (ProgressBar o TextureProgressBar)  ← hud_health.gd

Nombres:

NodoNombre sugeridoNotas
Raíz de PlayerPlayerPosee el script de salud
Capa HUDHUDCanvasLayer
BarraHealthBarProgressBar; HP absoluto o max_value = 1 ratio

Si el HUD proviene de AI Studio, renombra a IDs estables en inglés antes de cablear.

Contrato de señal (pegar al inicio de cada prompt)

Trátalo como ley del proyecto:

[Contrato de señal — salud]
1. Solo el script de salud en Player emite:
   signal health_changed(current: float, max_health: float)
   Nombrado en pasado / completado; argumentos siempre (current, max_health).
2. El script de la barra del HUD solo conecta esa señal y actualiza ProgressBar; sin get_node a Player desde HUD.
3. Prohibido: NodePaths profundos entre escenas, Autoload como bus de salud (no necesario aquí), sondeo de Player en _process.
4. Prefiere conexiones de señal del editor, o @export Node / NodePath en el HUD y luego .connect — nunca codificar "/root/...".
5. Daño y curación solo cambian valores en Player y emiten; la UI nunca muta HP.

Ese bloque es el punto de este artículo: contrato antes de implementación.

Paso 1: Prueba de humo de solo lectura

Lista los nombres de los hijos bajo la raíz de la escena actualmente editada. No modifiques archivos ni nodos.

Solo escribe una vez que veas Player y HUD (o tus equivalentes).

Paso 2: Player solo emite

Añade después del contrato:

[Contrato de señal — salud] (pega el bloque completo)

En res://scripts/player_health.gd (crear si falta) y adjuntar a Player:
- @export var max_health: float = 100.0
- var current: float
- signal health_changed(current: float, max_health: float)
- En _ready: current = max_health; emitir health_changed una vez
- take_damage(amount), heal(amount); clamp 0..max_health; emitir después de cada cambio
- No referenciar HUD; no get_node a UI
Guardar y reportar tipos de argumentos de la señal.

Implementación de referencia:

extends Node
class_name PlayerHealth

signal health_changed(current: float, max_health: float)

@export var max_health: float = 100.0
var current: float

func _ready() -> void:
	current = max_health
	health_changed.emit(current, max_health)

func take_damage(amount: float) -> void:
	if amount <= 0.0 or current <= 0.0:
		return
	current = maxf(0.0, current - amount)
	health_changed.emit(current, max_health)

func heal(amount: float) -> void:
	if amount <= 0.0 or current <= 0.0:
		return
	current = minf(max_health, current + amount)
	health_changed.emit(current, max_health)

Tecla de daño temporal (eliminar después):

Añade debug_damage.gd temporal en Player: presionar H llama a PlayerHealth.take_damage(10) en el mismo nodo (o padre). Solo para aceptación de cableado. No tocar HUD.

Si el script está en la raíz de Player, cambia extends Node para que coincida (ej. CharacterBody2D), o haz PlayerHealth un hijo Health—elige uno en el prompt para que MCP no adivine el punto de montaje.

Paso 3: HUD se suscribe mediante referencia exportada

[Contrato de señal — salud] (pega el bloque completo)

Crear res://scripts/hud_health.gd en HUD o HealthBar:
- @export var player_health: PlayerHealth
- @onready var bar: ProgressBar = $HealthBar  # solo si el script está en HUD; la ruta permanece dentro del subárbol HUD
- _ready: assert player_health != null; player_health.health_changed.connect(_on_health_changed)
- _on_health_changed(current, max_health): bar.max_value = max_health; bar.value = current
  (o ratio: max_value = 1; value = current / max_health)
- Prohibido: get_node a Player; prohibido: rutas absolutas /root
Luego asigna el player_health exportado al PlayerHealth de Player (o hijo Health) en el editor. Guarda la escena.

Referencia:

extends CanvasLayer

@export var player_health: PlayerHealth
@onready var bar: ProgressBar = $HealthBar

func _ready() -> void:
	assert(player_health != null, "Asigna PlayerHealth en el inspector")
	player_health.health_changed.connect(_on_health_changed)

func _on_health_changed(current: float, max_health: float) -> void:
	bar.max_value = max_health
	bar.value = current

$HealthBar dentro del HUD está bien—mismo subárbol. Prohibido es una ruta profunda hacia Player.

La conexión del editor “Nodo → Conectar una señal” vía MCP también está bien:

Conecta vía editor: Player (PlayerHealth) health_changed → script HUD _on_health_changed. No escribas cadenas NodePath a mano.

Paso 4: Lista de aceptación en Play

  1. Al entrar: barra llena (o ratio correcto)
  2. Presiona H: bar.value baja
  3. A 0: barra vacía; la UI no debe escribir current
  4. Después de heal: barra sube
  5. Reorganiza Player bajo ej. Entities y juega de nuevo: la barra aún se actualiza si la ref exportada permanece—esa es la victoria vs rutas fijas

Si se rompe después de reorganizar, arregla el export vacío—no pidas a MCP “otro get_node”.

Prompt de aceptación:

Diagnostica desde errores de Play: si la señal nunca se dispara, revisa connect; si player_health es null, solo arregla el export. No "arregles" con rutas get_node absolutas. Diagnostica primero; espera mi OK antes de editar.

Anti-patrones: cambia el prompt, no fusiones

Anti-patrónPor qué dueleAñade al prompt
get_node("/root/Game/Player")Cambio de ruta raíz”No /root o NodePath absoluto”
get_parent().get_node("Player")Acoplamiento de hermanos”HUD no debe asumir disposición de hermanos de Player”
Autoload GlobalHealth.hpCajón global de basura”No nuevo Autoload para esta tarea”
HUD _process buscando PlayerLento y frágil”No sondeo; solo señales”
Señal llamada update / set_hpVaga, propensa a colisiones”La señal debe ser health_changed”

Escribir a mano aún gana para movimiento de ruta crítica y matemática de combate compleja; el cableado de barra de salud es boilerplate donde un prompt MCP contratado suele ser más barato. Ver Godot MCP vs scripting manual.

Esqueleto reutilizable “contrato + tarea”

Intercambia nombres para maná / barras de jefe:

[Contrato de señal]
- Emisor: <nodo/script>
- signal <nombre>(...)
- Receptor: <método HUD>
- Prohibido: NodePath absoluto, bus Autoload, sondeo _process

[Tarea]
1. …
2. …
3. Guarda la escena; lista nuevas señales y cómo se conectan (editor / .connect)

Resumen

  1. Las rutas fijas son un agujero en el prompt, no “MCP no puede Godot”
  2. Pega health_changed(current, max_health) antes de pedir ediciones
  3. Player solo emite; HUD solo conecta y actualiza ProgressBar
  4. Los enlaces entre objetos usan @export o conexión de señal del editor—no /root/...
  5. Acepta reorganizando Player y viendo la barra—no por “script generado OK”

Ahora tienes un conjunto de reglas de prompt Godot MCP pegable; inventario, registro de misiones y contadores de muertes pueden reutilizar el mismo patrón.

Más guías que te pueden interesar