Godot 4:用信號規範提示詞,讓 MCP 把血條接到玩家血量
在 Cursor 裡向 Godot MCP 下指令前先寫清信號契約:用 health_changed 連 HUD,避免深層 get_node 硬路徑;含提示詞模板、GDScript 範例、反例與 Play 驗收。
- Godot MCP
- Godot 4
- 信號
- HUD
- GDScript
- Cursor
- AI game development
- tutorial
Godot 裡最常見的「AI 改完就斷」不是語法錯誤,而是血條腳本裡寫死了:
$"/root/Game/Player/HitPoints".hp # 或 get_node("../../Player/...")
場景一重組、Player 一做成子場景實例,這條路徑就失效。Godot MCP 會忠實地依你的提示接線——提示裡若不禁止硬路徑,模型就會繼續造硬路徑。
本文只解決一件事:在 Cursor(或 Claude Code)裡先給出信號契約,再讓 MCP 把玩家血量接到 HUD 血條。不重複安裝步驟,也不展開整關平台跳躍。
| 步驟 | 做什麼 |
|---|---|
| 1 | 定信號名、參數、誰發射 / 誰接收 |
| 2 | 用帶約束的提示詞讓 MCP 改腳本與連接 |
| 3 | Play 驗收;對照反例修提示詞 |
相關閱讀:
官方背景:Scene organization 強調子場景對外用信號 / 匯出,而不是深層路徑。
為什麼「信號規範」要寫進提示詞
| 做法 | 短期 | 重組場景後 |
|---|---|---|
get_node("../Player/Health") | 快 | 常斷 |
@onready var bar = %HealthBar(唯一名) | 同場景可用 | 跨場景仍脆 |
signal health_changed(current, max) + 連接 | 多寫兩行 | HUD 與 Player 可各自改樹 |
MCP 沒有「專案架構記憶」,除非你每次提示都寫約束。把規範寫成可貼上的契約塊,比事後在 diff 裡清硬路徑便宜。
先定最小節點骨架
不必一次做完整 UI。第一版只要:
Game (Node2D / Node)
├── Player (CharacterBody2D) ← 掛 player_health.gd
│ └── ...
└── HUD (CanvasLayer)
└── HealthBar (ProgressBar 或 TextureProgressBar) ← 掛 hud_health.gd
命名建議:
| 節點 | 建議名 | 說明 |
|---|---|---|
| 玩家根 | Player | 掛生命腳本 |
| HUD 層 | HUD | CanvasLayer |
| 血條 | HealthBar | ProgressBar;max_value = 1 用比例,或用絕對血量 |
HUD 若來自 AI Studio 匯出,進工程後盡快改成穩定英文名,再接線。
信號契約(複製到每次提示開頭)
把下面整段當作專案約定,下指令時貼在最前面:
【信號契約 — 血量】
1. 僅由 Player 上的生命腳本發射信號:
signal health_changed(current: float, max_health: float)
命名用過去式/已發生語意;參數固定為 (current, max_health)。
2. HUD 血條腳本只連接該信號,更新 ProgressBar;禁止在 HUD 裡 get_node 到 Player。
3. 禁止:跨場景深層 NodePath、Autoload 單例當血量總線(本任務不需要)、在 _process 裡輪詢找 Player。
4. 連接方式優先:編輯器裡信號連接,或在 HUD 腳本用 @export NodePath / 匯出 Node 引用指向 Player 後 connect;不要寫死 "/root/..."。
5. 受傷、治療只改 Player 側數值並 emit;UI 不改血量。
這一段就是本文的核心:規範先於實作。
步驟 1:唯讀冒煙(確認 MCP 看見場景)
列出目前編輯場景的根節點子級名稱。不要修改任何檔案或節點。
確認能看到 Player 與 HUD(或你的等價名)後再寫入。
步驟 2:讓 MCP 在 Player 上實作「只發射」
提示詞範例(接在契約後面):
【信號契約 — 血量】(同上,完整貼上)
在 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)
臨時驗收傷害(正式專案可刪):
在 Player 上增加臨時 debug_damage.gd:按下 H 鍵呼叫同節點(或父節點)上 PlayerHealth.take_damage(10)。僅用於接線驗收。不要動 HUD。
若生命腳本直接掛在 Player 根上,把 extends Node 改成與根類型一致(例如 CharacterBody2D),或把 PlayerHealth 做成子節點 Health——二選一寫進提示,避免 MCP 猜錯掛載點。
步驟 3:HUD 只訂閱,並用匯出引用找 Player
【信號契約 — 血量】(完整貼上)
建立 res://scripts/hud_health.gd 掛到 HUD 或 HealthBar:
- @export var player_health: PlayerHealth # 或 Node,再 as PlayerHealth
- @onready var bar: ProgressBar = $HealthBar # 若腳本掛在 HUD 上;路徑僅限 HUD 子樹內部
- _ready:斷言 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 指到 Player 上的 PlayerHealth(或 Health 子節點)。儲存場景。
參考實作:
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
HUD 內部用 $HealthBar 可以接受——那是同場景子樹,重組 HUD 時一起改;禁止的是跨到 Player 的深層路徑。
也可用編輯器「節點 → 連接信號」由 MCP 完成;提示裡寫清:
用編輯器信號連接:從 Player(PlayerHealth)的 health_changed 連到 HUD 腳本的 _on_health_changed。不要手寫 NodePath 字串。
步驟 4:Play 驗收清單
- 進場景瞬間:血條為滿(或等於目前比例)
- 按 H:
bar.value下降 - 扣到 0:條空;若有死亡邏輯,不因 UI 再改
current heal後條回升- 把
Player拖成更深一層子節點(例如放進Entities)再 Play:只要匯出引用還在,血條仍應更新——這是相對硬路徑的核心收益
若重組後斷了,先查匯出引用是否變成空,而不是讓 MCP「再寫一條新 get_node」。
驗收提示:
Play 模式下根據報錯診斷:若信號未觸發,檢查是否 connect;若 player_health 為空,只修匯出引用。不要改用 get_node 絕對路徑作為「修復」。先診斷,等我確認再改。
反例:看到這些就改提示詞,不要合併進主幹
| 反例程式碼 / 行為 | 問題 | 提示詞怎麼加約束 |
|---|---|---|
get_node("/root/Game/Player") | 根路徑一變就斷 | 「禁止 /root 與絕對 NodePath」 |
get_parent().get_node("Player") | 依賴兄弟關係 | 「HUD 不得假設與 Player 的相對層級」 |
Autoload GlobalHealth.hp | 全域狀態難測、易成垃圾桶 | 「本任務禁止新建 Autoload」 |
HUD 的 _process 裡每幀找 Player | 慢且脆 | 「禁止輪詢;只用信號」 |
信號叫 update / set_hp | 語意不清、易撞名 | 「信號名必須是 health_changed」 |
決策文裡「何時用手寫」仍然成立:熱路徑移動、複雜戰鬥結算可以手寫;血條接線這種樣板,用帶契約的 MCP 提示通常更划算。詳見 Godot MCP vs 手寫腳本。
可複用的「契約 + 任務」提示骨架
以後換藍條、Boss 條,只改信號名與節點名:
【信號契約】
- 發射方:<節點/腳本>
- signal <name>(...)
- 接收方:<HUD 腳本方法>
- 禁止:絕對 NodePath、Autoload 總線、_process 輪詢
【任務】
1. …
2. …
3. 儲存場景;列出新增信號與連接方式(編輯器連接 / .connect)
小結
- 硬路徑是提示詞漏洞,不是 MCP「不會 Godot」
- 先貼
health_changed(current, max_health)契約,再讓模型改腳本 - Player 只 emit;HUD 只 connect + 改
ProgressBar - 跨物件用
@export引用或編輯器信號連接,不用/root/... - 用「重組 Player 父節點後仍能掉血」做驗收,而不是「腳本產生成功」
做完這一段,你就有一份可貼上的 Godot MCP 提示規範;後面背包、任務日誌、擊殺計數都可以同一套路擴展。
繼續閱讀
你可能還會喜歡這些文章
用 AI 對 PSD 切圖並拼成 Unity UGUI 預製體
Photoshop 分層 PSD 用 AI 切圖並拼 Unity UGUI 預製體:VberAI Studio 輸出帶層級的 Prefab,不是只切散圖再手工重搭。
- vberai
- ai-studio
- psd
- unity
Godot 4 HUD 與 Theme:Control 樹交接與 Play 驗收
Figma 稿落到 Godot 4 時 Theme、StyleBox、容器錨點與 minimum_size 怎麼驗收;與 Unity UGUI 清單對照,附 Play 步驟與回修路徑。
- 遊戲UI設計
- 遊戲開發AI提效
- ui-to-engine
- godot
我們如何為 Godot 打造即時 MCP Server
拆解 VberAI 的 Godot MCP 架構:即時 Model Context Protocol 服務如何在不卡死場景樹的前提下,把 Godot 編輯器接到 AI 客戶端。
- vberai
- godot
- mcp
- architecture