← К блогу

Промпт для Godot: полоска HP через сигналы и Godot MCP

Промпт для Godot разработки с MCP: контракт сигнала health_changed, подключение HP-бара (полоска HP) без get_node — шаблон для Cursor, пример GDScript, приёмка в Play.

Опубликовано
  • Godot MCP
  • Godot 4
  • сигналы
  • HUD
  • GDScript
  • Cursor
  • AI game development
  • tutorial

Промпт для Godot: HP‑бар через сигналы (Godot MCP)

Типичный запрос из поиска — «godot hp bar» или «промпт для godot разработки»: как попросить Godot MCP в Cursor подключить полоску HP, не ломая дерево сцен.

Главная ошибка — жёсткий путь в промпте:

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

Перестроите дерево или сделаете Player дочерней сценой — путь умирает. Godot MCP следует промпту буквально — если не запретить жёсткие пути, модель будет их изобретать снова.

Эта статья решает одну задачу: в Cursor (или Claude Code) сначала задать контракт сигнала, затем поручить MCP подключить HP игрока к полоске HUD. Без установки и без полного платформера.

ШагЧто делать
1Имя сигнала, аргументы, кто испускает / кто принимает
2Промпт MCP с ограничениями для скриптов и соединений
3Приёмка в Play; правка промпта по антипаттернам

См. также:

Официальный контекст: Scene organization — дочерние сцены экспонируют сигналы / экспорты, а не глубокие пути.

Зачем правила сигналов писать в промпт

ПодходКраткосрочноПосле реорганизации
get_node("../Player/Health")БыстроЧасто ломается
@onready var bar = %HealthBar (уникальное имя)Норм в одной сценеХрупко между сценами
signal health_changed(current, max) + connectНа две строки большеHUD и Player меняют дерево независимо

У MCP нет «памяти об архитектуре», пока вы не повторяете ограничения. Вставляемый блок контракта дешевле, чем вычищать жёсткие пути из каждого diff.

Минимальный скелет узлов

Полный UI не нужен. Для v1 достаточно:

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

Именование:

УзелРекомендуемое имяПримечания
Корень игрокаPlayerСкрипт здоровья
Слой HUDHUDCanvasLayer
ПолоскаHealthBarProgressBar; абсолютное HP или max_value = 1

Если HUD из AI Studio, переименуйте в стабильные английские ID перед подключением.

Контракт сигнала (вставлять в начало каждого промпта)

Считайте это правилом проекта и вставляйте в начало инструкции:

【Контракт сигнала — HP】
1. Испускает только скрипт здоровья на Player:
   signal health_changed(current: float, max_health: float)
   Именование в прошедшем / завершённом времени; аргументы всегда (current, max_health).
2. Скрипт полоски HUD только подключает этот сигнал и обновляет ProgressBar; get_node к Player из HUD запрещён.
3. Запрещено: глубокие NodePath между сценами, Autoload как шина HP (здесь не нужен), опрос Player в _process.
4. Предпочтительно: соединение сигналов в редакторе, или @export NodePath / экспорт Node на HUD с указанием на Player и .connect; не хардкодить "/root/...".
5. Урон и лечение меняют только значения на Player и emit; UI не меняет HP.

Этот блок — суть статьи: контракт до реализации.

Шаг 1: Дымовой тест только на чтение (MCP видит сцену)

Перечисли имена дочерних узлов корня текущей редактируемой сцены. Не изменяй файлы и узлы.

Пишите только после появления Player и HUD (или эквивалентов).

Шаг 2: MCP реализует на Player «только испускание»

Пример промпта (после контракта):

【Контракт сигнала — HP】(вставить полный блок выше)

В res://scripts/player_health.gd (создать при отсутствии) и прикрепить к Player:
- @export var max_health: float = 100.0
- var current: float
- signal health_changed(current: float, max_health: float)
- В _ready: current = max_health; один раз emit health_changed
- take_damage(amount), heal(amount); зажать 0..max_health; emit после каждого изменения
- Не ссылаться на HUD; не get_node в UI
Сохранить и сообщить типы аргументов сигнала.

Эталонная реализация (после генерации моделью — ревью):

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)

Временный урон (удалить в продакшене):

Добавить временный debug_damage.gd на Player: по H вызывать PlayerHealth.take_damage(10) на том же узле (или родителе). Только для приёмки подключения. HUD не трогать.

Если скрипт на корне Player, замените extends Node на тип корня (напр. CharacterBody2D), или сделайте PlayerHealth дочерним Health — укажите одно в промпте, чтобы MCP не угадывал точку монтирования.

Шаг 3: HUD только подписывается, Player через экспорт

【Контракт сигнала — HP】(полный блок)

Создать res://scripts/hud_health.gd на HUD или HealthBar:
- @export var player_health: PlayerHealth   # или Node, затем as PlayerHealth
- @onready var bar: ProgressBar = $HealthBar  # если скрипт на HUD; путь только внутри поддерева 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
  (только доля 0..1: bar.max_value = 1; bar.value = current / max_health)
- Запрещён get_node к Player; запрещены абсолютные пути /root
Затем: в редакторе назначить экспорт player_health на PlayerHealth (или дочерний Health) на Player. Сохранить сцену.

Эталон:

extends CanvasLayer

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

func _ready() -> void:
	assert(player_health != null, "Assign PlayerHealth in the 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 внутри HUD допустим — тот же поддерево. Запрещён глубокий путь через к Player.

Через MCP можно и «Узел → Подключить сигнал» в редакторе:

Подключить в редакторе: health_changed Player (PlayerHealth) → _on_health_changed скрипта HUD. Не писать строки NodePath вручную.

Шаг 4: Чеклист приёмки в Play

  1. При входе: полоска полная (или верная доля)
  2. H: bar.value падает
  3. До 0: полоска пуста; UI не пишет current
  4. После heal: полоска растёт
  5. Переместите Player глубже (напр. в Entities) и снова Play: полоска обновляется, если экспорт остался — главный выигрыш над жёсткими путями

Если после реорганизации сломалось — чините пустой экспорт, не просите MCP «ещё один get_node».

Промпт приёмки:

Диагностика по ошибкам Play: если сигнал не срабатывает — проверить connect; если player_health null — только исправить экспорт. Не «чинить» абсолютными get_node. Сначала диагностика; ждать моего OK перед правками.

Антипаттерны: меняйте промпт, не мержите

АнтипаттернПроблемаДобавить в промпт
get_node("/root/Game/Player")Смена корня ломает путь«Запрет /root и абсолютных NodePath»
get_parent().get_node("Player")Зависимость от соседей«HUD не предполагает относительную иерархию с Player»
Autoload GlobalHealth.hpГлобальный мусор«Новый Autoload для этой задачи запрещён»
HUD ищет Player в _process каждый кадрМедленно и хрупко«Без опроса; только сигналы»
Сигнал update / set_hpРазмыто, коллизии имён«Имя сигнала — health_changed»

Ручное написание по-прежнему выигрывает для горячего движения и сложной боёвки; подключение полоски HP — шаблон, где промпт MCP с контрактом обычно дешевле. См. Godot MCP vs ручные скрипты.

Переиспользуемый каркас «контракт + задача»

Для маны / босса меняйте только имена сигнала и узлов:

【Контракт сигнала】
- Испускатель: <узел/скрипт>
- signal <name>(...)
- Получатель: <метод скрипта HUD>
- Запрещено: абсолютный NodePath, Autoload-шина, опрос в _process

【Задача】
1. …
2. …
3. Сохранить сцену; перечислить новые сигналы и способ подключения (редактор / .connect)

Итог

  1. Жёсткие пути — дыра в промпте, а не «MCP не умеет Godot»
  2. Вставьте контракт health_changed(current, max_health) до запроса правок
  3. Player только emit; HUD только connect и обновление ProgressBar
  4. Между объектами — @export или соединение в редакторе, не /root/...
  5. Приёмка — реорганизация Player и работающая полоска, а не «скрипт сгенерирован»

Теперь у вас вставляемый набор правил промпта Godot MCP; инвентарь, журнал квестов и счётчик убийств расширяются тем же шаблоном.

Другие статьи, которые могут вам понравиться