Claude CodeでUnreal EngineのC++を書く — ビルドエラーの読み方
Unreal EngineのC++をClaude Codeで書くとき、規約をCLAUDE.mdとrulesに置き、ビルドの失敗をStopフックで読ませる手順と、Live Codingとの分担をまとめます。
Unreal EngineのC++は、Claude Codeに書かせること自体は難しくありません。つまずくのは、書いたあとです。ヘッダーを直してもビルドが通らず、エラーが数百行流れ、Claudeはその一部しか見ていない。この状態を避けるには、規約を先に渡し、ビルドの失敗を機械的に読ませる仕組みが要ります。
この記事では、UCLASSやUPROPERTYの書き方をCLAUDE.mdと.claude/rules/に置き、ビルドエラーをStopフックでClaudeに返し、Live Codingと役割を分ける流れを順に説明します。ツールセットやプラグインの話はUnreal Engine Skills for Claude Codeの解説に、MCP接続はUnreal EngineのMCP設定手順にあります。ここでは、ゲームプロジェクトのC++とビルドの失敗に絞ります。
Live Codingは何を担い、Claude Codeは何を担うか
Live Codingは、エンジンが動いたままC++をリビルドしてバイナリにパッチを当てる仕組みです。Unreal Editorでの編集、Play In Editor、デスクトップ向けパッケージビルドへのアタッチで使えます。コンソールやモバイル向けの起動では使えません。
ビルドの開始は、IDEまたはUnreal Engine上でCtrl + Alt + F11を押します。進行状況はLive Codingコンソールに出ます。これは通常のOutput Logとは別のログで、Live Codingのビルド情報だけが流れます。
Claude Codeが得意なのは、ソースを編集し、ファイルをまたいで整合を取ることです。パッチを当てるのはエディタ側の仕事です。この分担を前提にすると、次の割り切りができます。
- 大きな構造変更(新しいクラス、関数の追加、UPROPERTYの増減)は、Claudeに編集させたあと、フルビルドで通るかを先に確かめる
- 小さな挙動の調整は、Claudeの編集後にLive Codingで反映して動きを見る
Live Codingには、Claudeに知らせておきたい癖があります。コンストラクタ内(.cppファイル)で設定した既定値を変えても、既存のオブジェクトには反映されません。ヘッダー側で変えた既定値は反映されます。「値を変えたのに動きが変わらない」という報告をClaudeが受けたとき、コードの誤りと決めつけて直し続けないよう、CLAUDE.mdに書いておく価値があります。
また、Object Reinstancingが有効な状態では、再インスタンス化されるオブジェクトを指すポインターを自前で持つコードは、ReloadReinstancingCompleteDelegateとReloadCompleteDelegateで更新かキャッシュの無効化を行う必要があります。キャッシュ付きのコードをClaudeに書かせるときは、この点を頭に置くとレビューが速くなります。
UCLASSとUPROPERTYの規約はどこに置くか
規約は、書いてほしい内容を検証できる形にして、CLAUDE.mdか.claude/rules/に置きます。CLAUDE.mdは強制される設定ではなく文脈として扱われるため、「ちゃんと書く」のような曖昧な指示より、「ヘッダーの最後のincludeは.generated.h」のように確かめられる文のほうが守られやすい、というのが公式の説明です。1ファイルは200行未満が目安です。
Unrealでは、ヘッダーに書いたリフレクションのマクロ(UCLASS、UFUNCTION、USTRUCT、UENUMなど)を、Unreal Header Toolが解析して.generated.hを作ります。そのためマクロの誤りは、C++のコンパイル前の段で止まることがあります。その規約を、ヘッダーを触るときだけ読み込む形にすると、文脈を無駄にしません。
.claude/rules/のファイルは、pathsフロントマターで対象を絞れます。パス指定のルールは、Claudeが一致するファイルをRead・Write・Editしたときに読み込まれます。
---
paths:
- "Source/**/*.h"
---
# Unreal ヘッダーの規約(プロジェクト用の例)
- ヘッダーの最後のincludeは `ClassName.generated.h`
- UCLASSを付けたクラスは、`GENERATED_BODY()` をクラス本体の先頭に置く
- Blueprintから触るメンバーには `UPROPERTY(EditAnywhere, BlueprintReadWrite)`
などの指定子を明示し、省略しない
- UObjectを指す生ポインターのメンバーには、必ず `UPROPERTY()` を付ける
- 新しい関数やUPROPERTYを足したら、ビルドが通るまで次の作業に進まないプロジェクト共通の方針は、CLAUDE.md側に短く書きます。
# ビルドと検証
- C++を変更したら、Stopフックのビルドが通ることを完了条件とする
- ビルドが失敗したら、最初のエラー1件だけを直し、再ビルドで次を確かめる
- 「constructorで設定した既定値が既存インスタンスに反映されない」場合は、
コードではなくLive Codingの仕様を疑い、配置し直して確かめる
- エラーの原因が分からないときは、推測で直さず、関連するヘッダーと
Build.csを読んでから報告するここに書いた規約はチームごとの例で、Epicの標準ではありません。自分たちのコードベースに合わせて入れ替えてください。大きなヘッダーほど、ルールの数を絞ったほうが守られます。
ビルドの失敗はどうやってClaudeに返すか
規約を書いても、ビルドが通ったかをClaudeが自分で確かめる仕組みがなければ、失敗したまま「できました」と返ってきます。そこで、Claudeの応答が終わるタイミングで動くStopフックにビルドを任せます。
Stopフックがexit code 2で終わると、Claudeの停止が止められ、会話が続きます。stderrがClaudeに返るので、そこにエラーの抜粋を書けば、Claudeはそれを読んで直します。PostToolUseにも同じ仕組みがありますが、そちらはツールがすでに動いたあとにstderrが見えるだけで、編集のたびに数分のビルドが走ります。UnrealのビルドはPostToolUseの粒度には重いので、Stopに寄せるのが現実的です。PostToolUseの使い方はPostToolUse hookの解説にあります。
.claude/settings.jsonには次のように書きます。フックのタイムアウトは、commandタイプの既定が600秒です。ビルドがそれを超えるプロジェクトでは、timeoutを明示します。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/ubt-check.sh",
"timeout": 1200
}
]
}
]
}
}スクリプトは、Sourceの下に変更があるときだけビルドし、失敗したらログからエラー行だけを取り出してstderrに出します。ビルドのコマンドは環境ごとに違うため、次は枠組みの例です。UE_ROOT、ターゲット名、プロジェクトのパスは自分の環境に合わせて書き換えてください。
#!/bin/bash
# .claude/hooks/ubt-check.sh(例)
cd "$CLAUDE_PROJECT_DIR" || exit 0
# C++ やBuild.csに変更がなければ何もしない
git status --porcelain --untracked-files=all -- Source | grep -qE '\.(h|cpp|cs)$' || exit 0
LOG="$(mktemp)"
"$UE_ROOT/Engine/Build/BatchFiles/Build.bat" \
MyGameEditor Win64 Development \
-Project="$CLAUDE_PROJECT_DIR/MyGame.uproject" > "$LOG" 2>&1
if [ $? -ne 0 ]; then
# エラー行だけを抜き、Claudeに返すのは先頭の30行まで
grep -iE '(: (fatal )?error[ :]|error LNK|error C[0-9])' "$LOG" | head -30 >&2
# 該当行が0なら、ログの末尾40行をそのまま返す
[ -s "$LOG" ] && ! grep -qiE '(: (fatal )?error[ :]|error LNK|error C[0-9])' "$LOG" && tail -40 "$LOG" >&2
exit 2
fi
exit 0このスクリプトはWindows向けのBuild.batを呼んでいます。macOSやLinuxのエディタでは、同じEngine/Build/BatchFilesの下にある、OSごとのビルド用スクリプトに差し替えてください。パスと引数は環境で変わるので、手でビルドが通るコマンドを先に確かめてから、フックに入れます。errorだけでgrepすると、成功時の「0 error(s)」のような集計行まで拾ってしまうため、上の例では: error やerror LNKのような形に絞っています。UHTのError:やclangのfile:line:col: error:の書式も拾えるよう大文字小文字は区別せず、該当行が1つもなければログの末尾を返します。stderrが空のままexit code 2で終わると、Claudeは何も読めず、続行の上限まで空回りするためです。
ここで押さえておきたい点が3つあります。
- フックが返す
additionalContextや通常のstdoutは1万文字が上限で、超えた分はファイルに退避されます。ログ全体ではなく抜粋を返すほうが安全です - Stopフックが続けて会話を継続できる回数には上限があり、連続8回です。入力には
stop_hook_activeも渡され、すでにフックの結果で続行している状態を判別できます。直らないエラーで無限にループさせない設計は、この値と上限に頼れます - エディタを開いたまま外部からビルドした場合の挙動は、Live Codingのドキュメントに載っていません。Live Codingが有効な環境でどうなるかは、チームで一度試してください。ビルドが失敗しても、原因のメッセージは上の抜粋かログ末尾のかたちでClaudeに届きます
ビルドエラーはどの層で起きたかを分けて読む
UBT(UnrealBuildTool)は、Unreal Engineのソースを、さまざまなビルド構成でビルドする工程を管理するツールです。ビルド全体は、各モジュールの.Build.csと、UHTによる生成、コンパイル、リンクが連なっています。エラーが出た層が分かると、Claudeへの指示が絞れます。
| 層 | 見えやすい手がかり | 最初に疑う場所 |
|---|---|---|
| UHT(ヘッダー解析) | 見えやすい手がかりマクロ周辺の構文に関するエラー、.generated.h | 最初に疑う場所UCLASSなどの書式、GENERATED_BODY()の有無、.generated.hのinclude位置 |
| コンパイル | 見えやすい手がかりファイルと行番号つきのエラー(Windows・MSVCの例: C2065) | 最初に疑う場所宣言漏れ、includeの不足、型の不一致 |
| リンク | 見えやすい手がかり未解決シンボル(Windows・MSVCの例: LNK2019) | 最初に疑う場所.Build.cs の依存モジュール |
エラー番号はコンパイラーごとに変わります。MSVC以外の環境では番号ではなく、メッセージの内容(未宣言、未解決の外部シンボル)で層を判断します。
.Build.csは、モジュールの依存関係、追加ライブラリ、インクルードパスを制御するファイルです。別モジュールのクラスを使い始めたとき、リンクで落ちるなら、まず依存の追加漏れを見ます。Claudeにも「リンクエラーは .Build.cs を先に確認する」と指示しておくと、ソースを無闇に書き換える遠回りを避けられます。
次のようなプロンプトで、フックが返したエラーを層に分けさせると、直し方が揃います。
ビルドが失敗しました。次の3点を順に答えてください。
1. 最初のエラーは UHT・コンパイル・リンクのどれか
2. 原因として疑うファイルと理由
3. 最初のエラー1件だけを直す変更案
他のエラーは、最初の1件を直して再ビルドしてから扱います。最初の1件に絞るのは、C++の特性上、先頭のエラーが後続のエラーを連鎖的に生むことが多いためです。末尾のエラーから直すと、直した箇所が増えても件数が減らず、差分だけが膨らみます。
運用で迷いやすいところ
エディタの外でビルドしても、Live Codingで開いているエディタに自動で反映されるわけではありません。反映するにはCtrl + Alt + F11の操作が別に要ります。「フックのビルドが通った」は、コードがコンパイルできることの確認で、エディタの動作の確認ではない、と分けて考えてください。
Live Codingの設定にも、反映されない原因になるものがあります。Enable Live Codingを切ると、エディタはHot Reloadに戻ります。「Ctrl + Alt + F11を押しても何も起きない」ときは、コードより先にこの設定を見ます。新しいC++クラスは、Automatically Compile Newly Added C++ Classesが有効なときにLive Codingのコンパイルへ自動で回ります。Enable Reinstancingは、無効にすると新しい関数や変数群といった大きな変更が不安定になり、クラッシュしやすくなるため、切らない設定が勧められています。
また、Modulesの設定で、起動時に先読みするモジュールを選べます。作業中のモジュールを先読みしておくと反復が速くなりますが、増やすほどエディタの起動は遅くなります。エンジン本体のモジュールは、エンジンのソースを触っているとき以外は先読みしないのが目安です。大きなプロジェクトでは、Preload Named Modulesで作業中のモジュールだけを指定する手もあります。Claudeに複数モジュールを編集させる場合は、どのモジュールを先読みする設定かを、チームで揃えておくと迷いません。
CLAUDE.mdが長くなってきたら、/doctor prompt-auditで古い指示や互いに矛盾する指示を洗い出せます。このコマンドにはClaude Code v2.1.283以降が必要です。Unrealのエンジンバージョンを上げたときに、規約の古さを点検する用途に向いています。
まとめ
Unreal EngineのC++で効くのは、Claudeに書かせる量より、失敗を戻す経路の細さです。規約はpathsで絞った.claude/rules/へ、ビルドの成否はStopフックのexit code 2へ置きます。エラーは層で読み、最初の1件から直します。エディタへの反映はLive Codingの操作が別に要る点だけ、忘れずに残してください。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。