
Day 08,我讓 Claude Code 可以自己執行 Godot。
但實際開發之後,我很快發現一件事:
如果只是改了一行程式碼,真的每次都要把整個遊戲跑起來嗎?
如果只是想確認 GDScript 有沒有寫錯,有沒有更快的方法?
我開始找 Godot 的指令列工具,最後找到 --check-only。
我先問 Claude Code:
「有沒有辦法不開編輯器,直接用命令列檢查 GDScript 有沒有語法錯誤?」
它給我的指令是:
godot --headless --script scripts/CardDatabase.gd --check-only
這裡有兩個比較重要的參數:
| 參數 | 作用 |
|---|---|
--headless |
不開視窗,直接在 Terminal 執行 |
--check-only |
只檢查語法,不執行程式 |
而 --check-only 有一個限制:
它必須搭配 --script 使用,而且一次只檢查指定的那一支腳本。
也就是說,它不是把整個 Godot 專案掃過一遍。
光看指令說明還不算數,我直接實測。
我在 CardDatabase.gd 的最後面補了一行故意寫錯的程式碼:
func broken(:
再執行:
godot --headless --script scripts/CardDatabase.gd --check-only
Terminal 出現:
Godot Engine v4.7.1.stable.official.a13da4feb - https://godotengine.org
SCRIPT ERROR: Parse Error: Expected parameter name.
at: GDScript::reload (res://scripts/CardDatabase.gd:112)
ERROR: Failed to load script "res://scripts/CardDatabase.gd" with error "Parse error".
Godot 很明確地告訴我:第 112 行的函式參數寫錯了,原本應該要有參數名稱。
也就是說,這次 --check-only 確實抓到了我故意製造的錯誤。
檔名、行號、錯誤原因,全都有。
這正是 AI 需要的資訊。
Claude Code 看到這段輸出,就能知道:
然後自己修改,再重新檢查。
看起來很完美。
但接下來,我踩到了第一個坑。
命令列工具有一個常見的慣例:
成功通常回傳 exit code 0,失敗則回傳非 0。
Shell 腳本就可以利用這個結果判斷指令到底成功還是失敗。
所以我先用正常的程式碼測一次,再故意把程式碼寫錯,看看 --check-only 的 exit code 會不會跟著改變。
godot --headless --script scripts/CardDatabase.gd --check-only >/dev/null 2>&1
echo "exit code = $?"
結果:
| 情況 | exit code |
|---|---|
| 程式碼正常版本 | 0 |
| 程式碼有語法錯誤版本 | 0 |
兩種情況都是 0。
這就麻煩了。
如果我直接用 $? 寫自動化腳本,讓 exit code = 0 時顯示「檢查通過」,就會出現問題。
例如下面這個檢查腳本:
# 範例:這是錯的方式
godot --headless --script "$1" --check-only
if [ $? -eq 0 ]; then
echo "檢查通過"
else
echo "有錯誤"
fi
它會把語法錯誤也當成成功。
最後不管程式碼有沒有寫錯,都可能得到:
檢查通過
這種問題特別麻煩。
因為你以為自己已經加了一層安全檢查,實際上這個安全檢查根本沒有發揮作用。
所以我不能直接相信 exit code。
那就改看錯誤訊息。
結果第二個坑就出現了。
既然 exit code 不能直接使用,我改成檢查輸出裡面有沒有 "SCRIPT ERROR"。
例如出現:
SCRIPT ERROR: Parse Error: Expected parameter name.
於是我寫了一個批次檢查,跑完整個 scripts/ 目錄:
# 範例:這也是錯的
for f in scripts/*.gd; do
if godot --headless --script "$f" --check-only 2>&1 | grep -q "SCRIPT ERROR"; then
echo "❌ $f"
fi
done
結果出現:
❌ scripts/BattleScene.gd
❌ scripts/RewardScene.gd
奇怪了。
這兩個檔案明明都可以正常執行,為什麼檢查會說它們有錯?
我把其中一個檔案單獨拿出來看,發現:
SCRIPT ERROR: Compile Error: Identifier not found: Global
at: GDScript::reload (res://scripts/BattleScene.gd:74)
這裡的 Global 是我的 Autoload 單例。
我在 project.godot 裡註冊了它,所以正常執行遊戲時,其他腳本可以直接使用 Global。
但現在使用的是:
godot --headless --script scripts/BattleScene.gd --check-only
這是一種單檔檢查模式。
Godot 只載入我指定的那一支腳本,不會把專案裡的 Autoload 一起載入。
所以:
正常執行遊戲
BattleScene.gd
↓
可以找到 Global
↓
正常
但單檔檢查:
只載入 BattleScene.gd
↓
找不到 Global
↓
出現 Compile Error
所以問題不是我的程式碼真的寫錯。
是我使用的檢查方式,讓它產生了假警報。
整理起來就很清楚:
| 檔案 | 有沒有引用 Global |
單檔檢查結果 |
|---|---|---|
CardDatabase.gd |
沒有 | ✅ 通過 |
Card.gd |
沒有 | ✅ 通過 |
Global.gd |
它自己就是 | ✅ 通過 |
BattleScene.gd |
有 | ❌ 誤報 |
RewardScene.gd |
有 | ❌ 誤報 |
前面測試時,真正的語法錯誤長這樣:
SCRIPT ERROR: Parse Error: Expected parameter name.
而 Autoload 造成的問題是:
SCRIPT ERROR: Compile Error: Identifier not found: Global
這兩種錯誤其實不一樣。
| 訊息 | 意思 | 這次要不要當成錯誤 |
|---|---|---|
Parse Error |
語法寫錯 | ✅ 要 |
Compile Error: Identifier not found |
單檔模式找不到 Autoload | ❌ 不要 |
所以我把檢查條件從:
SCRIPT ERROR
縮小成:
Parse Error
改成:
# ✅ 正確
godot --headless --script scripts/CardDatabase.gd --check-only 2>&1 \
| grep -q "Parse Error" \
&& echo "❌ 有語法錯誤" \
|| echo "✅ 通過"
這裡的 2>&1 不能省略。
因為 Godot 的錯誤訊息會輸出到另一個訊息管道,如果直接用 grep 去找 Parse Error,可能抓不到。
2>&1 的作用,就是把這些錯誤訊息也交給後面的 grep 檢查。
簡單來說:
Godot 輸出錯誤訊息
↓
2>&1
↓
交給 grep 搜尋
這樣 grep 才能找到 Parse Error。
接著把修正後的檢查套到整個 scripts/:
for f in scripts/*.gd; do
if godot --headless --script "$f" --check-only 2>&1 | grep -q "Parse Error"; then
echo "❌ $f"
fi
done
這次正常的程式碼不再出現:
❌ scripts/BattleScene.gd
❌ scripts/RewardScene.gd
誤報歸零。
但我還有一個問題:
如果一支腳本同時有真正的語法錯誤,又引用了 Autoload,會不會反而漏掉真正的錯誤?
所以我又把 BattleScene.gd 故意寫壞。
它原本就引用 Global,這次再加上一個真正的語法錯誤。
結果輸出是:
SCRIPT ERROR: Parse Error: Expected parameter name.
還是 Parse Error。
不是前面的 Compile Error。
原因是:
解析(parse)發生在編譯(compile)之前。
語法都還沒通過,根本還沒走到查找 Global 的階段。
所以這次實測確認:真的有語法錯誤時,Parse Error 會先出現。
這讓我比較放心把它放進自動化檢查。
這次我沒有只看 Claude Code 給我的答案。
我自己在 Terminal 做了一輪完整測試:
--check-only
scripts/ 目錄大約十分鐘。
最後確認了四件事:
Parse Error 會先於 Compile Error 出現這些都是我自己跑過之後才確定的。
所以現在我不會只問 AI:「這個指令可以嗎?」
我還會繼續追問:
「它成功時怎麼回應?」
「故意弄錯時又怎麼回應?」
「如果一次跑很多檔案呢?」
這些問題,才真正決定這個指令能不能拿來做自動化。
到 Day 09 為止,我手上已經有三種不同層次的檢查:
| 方法 | 成本 | 可以檢查什麼 |
|---|---|---|
--check-only |
最輕,單一檔案 | 語法 |
--headless --import |
中,整個專案 | 場景、資源、設定等問題 |
godot -e + F5 |
最重,需要人看 | 遊戲實際執行與畫面 |
所以現在的使用方式變成:
改一行程式碼,可以先跑
--check-only。
改完一個功能,再跑整個專案的 headless 檢查。
完成一段功能後,再實際開遊戲確認。
三種方式各自負責不同的事情。
命令列檢查很好用,但它有明確的範圍。
我之前就遇過一個例子。
AI 幫我建立專案骨架之後,我在編輯器裡打開專案。
開得起來,畫面正常,也沒有任何紅字。
但按 F5 執行之後,卻直接報錯:
找不到場景。
後來才發現,project.godot 裡設定的主場景指向 RewardScene.tscn,但那個檔案根本還沒有建立。
所以有時:「語法沒錯」不等於「遊戲跑得起來」。
--check-only 檢查的是單一檔案的語法。
它不知道:
所以驗證還是要分層:
語法用 --check-only,實際行為則要真的執行。
Day 10 就會來做這件事:讓 AI 寫一支跑完就刪掉的測試腳本,直接確認遊戲裡的功能真的會動。
今天的發現,其實不是多學了一個指令。
而是我把一個「看起來可以自動化」的檢查,從頭測了一遍。
結果發現:
不能只看 exit code。
這次有語法錯誤時,exit code 還是 0。
不能看到 SCRIPT ERROR 就全部判定為錯誤。
單檔模式下,Autoload 會造成誤報。
要先知道自己到底想檢查什麼。
這次真正要抓的是 Parse Error。
靜態檢查不能取代實際執行。
它能抓語法問題,但不知道遊戲實際跑起來會發生什麼。
這兩個實驗,讓我學到寶貴的經驗:
不要只相信工具「看起來應該會這樣工作」,成功和失敗都自己測一次。
尤其當這個工具要成為你的自動化安全網時,更值得先花幾分鐘確認它到底在做什麼。
明天 Day 10,我們把驗證再往前推一步:
不只檢查語法,而是讓 AI 寫一支跑完就刪掉的測試腳本,自己證明遊戲真的會動。