はじめに
最近、Fortniteのクリエイター向けエディター UEFN で、農業タイクーンを作っています。畑で作物を育てて、収穫して、真ん中の売り場まで運んで売る。そんなゲームの「Vertical Slice(一通り遊べる最小版)」を作るのが今回の目標です。
せっかくなので、AIをがっつり使ってみることにしました。体制はこんな感じです。
- Claude Code(Opus):現場監督。作業の分担を決めて、自分でも手を動かしてもらう
- Codex 2体:1体はモデリング担当、もう1体はVerse(UEFNのプログラミング言語)担当
結果から言うと、
- ゲームの土台(植える・育つ・収穫・売る・セーブ)は 初日のうちに動きました
- でも 「ニンジンを手に持っているように見せる」だけで、残りの時間がほぼ全部溶けました
UEFNを触ったことがある人なら「あるある」と言ってもらえそうな2日間だったので、日記としてまとめておきます。
この記事でわかること
- AIエージェント3体で分担してUEFN開発をしてみて、うまくいったこと・ハマったこと
- UEFNのMCP(AIからエディターを操作する仕組み)でできること・できないこと
- 「低ポリなのにのっぺりする」を直した、グラデーションテクスチャの小技
- Fortniteでプレイヤーに物を「持たせる」方法の比較
こんな人向け
- UEFN・Verseを触り始めた人
- AIコーディングエージェントでゲームを作ってみたい人
- 「Fortniteでプレイヤーに独自の物を持たせたい」と思って検索してきた人(たぶん一番多い)
環境
| 項目 | 内容 |
|---|---|
| エディター | UEFN(Unreal Editor 6.0系 / Fortnite 42.x) |
| 現場監督 | Claude Code(Opus) |
| モデリング・コーディング | Codex CLI(最初は gpt-6-astra。今後は Sol 系に変更予定) |
| 3D制作 | Blender 5.1(Pythonスクリプトで自動生成) |
| エディター操作 | UEFN内蔵のMCPサーバー |
1日目:AI 3体で土台を作る
設計書を渡して、分担してもらう
私が用意したのは、作物・畑・運搬物・売り場の「3Dモデル制作指示書」です。これをClaude Codeに渡して、「Codex 2体にモデリングとコーディングを任せて、あなたも何か手を動かしてね」とお願いしました。
Claude Codeは、Codexに作業を投げている間も、
- 3体共通のルール表(ファイルの担当、命名、寸法、原点、座標、値段などの仮値)を作る
- 前のプロジェクトのモデルを、消さずにアーカイブしておく
- UEFNへの取り込みと配置を自動でやるスクリプトを書く
を並行して進めてくれました。指示を出すだけの「監督」にならないよう頼んでおいたのが良かったです。
一番効いたのは「ファイルの担当表」
最初に「このフォルダーはCodex A、このフォルダーはCodex B」と担当を決めてもらったので、AI同士が同じファイルを同時に書き換える事故は一度も起きませんでした。
いきなりのつまずき:モデルが使えない
最初は gpt-6.1-sol でCodexを動かそうとしたのですが、
The 'gpt-6.1-sol' model is not supported when using Codex with a ChatGPT account.
ChatGPTアカウントでは使えないモデルでした。使えるモデルを探してもらい、gpt-6-astra で再スタート。モデリングは推論の深さ high、Verseは手元でコンパイルできないので一段深い xhigh にしてもらいました。
……で、2体同時に回したら、数十分で利用上限に到達。モデリング担当は42個のモデルを作り切っていましたが、コーディング担当は書きかけで止まってしまいました。
Codexを複数同時に動かすと、クレジットが想像以上に速く減ります。次からは作業を小さく区切って、Sol系のモデルで回すことにしました。
UEFNが開かなくなった
書きかけのVerseが入った状態でUEFNを開いたら、プロジェクト自体が開かなくなりました。原因は2つでした。
- アセット参照用に作った
VSArt.Materialsという名前が、もともとあるContent/Materialsフォルダーと名前がぶつかっていた - まだ取り込んでいないモデル(
VSArt.Meshes.CROP_Carrot_S1_Plotなど)をVerseが参照していた
Script error 3532: The module (/invaliddomain/AIGridProbe/VSArt:)Materials ... is ambiguous with this definition
UEFNは開くときにContentの中のVerseを全部コンパイルするので、「後で取り込む予定のモデルを使うコード」を先に置いておけないんですね。それからは、モデルを取り込むまで、Verseは作業用フォルダーに置いておくルールにしました。
おまけで、止めたはずのCodexが裏で生き残っていて、再開しようとしたら「すでに書き込み中のプロセスがある」と怒られる、という事件もありました。
Verseのコンパイルエラーあるある
コンパイルせずに書かれたVerseだったので、実際のUEFNでエラーが2種類出ました。
① 標準の関数と同じ名前の変数
# NG: SpatialMath の Distance 関数と名前がぶつかる
Distance := (Center.Z - Eye.Z) / Forward.Z
RayLength に名前を変えて解決です。
② モジュール直下でクラスを作れない(error 3512)
# NG: class だと、モジュール直下の定数で作れない
vs_crop_rule := class<final>: ...
# OK
vs_crop_rule := struct<computes>:
Kind:int
Name:string
SeedCost:int
...
struct に変えただけでは通らず、<computes> を付けてようやく通りました。
MCPで取り込み・配置まで、ほぼ全自動
UEFNのMCPを使うと、モデルの取り込み、材質の設定、当たり判定の設定、ワールドへの配置まで、AIが自動でやってくれます。
- モデル42個を取り込んで、材質を割り当て
- 当たり判定は「畑・作物・運搬物はなし、柵と売り場は簡単な形だけ」になっているか自動でチェック
- 前のゲームのアクター35個を片付けて、売り場・畑36区画・柵を 189個配置
ここは見ていて本当に気持ちよかったです。
ただ、Verseのデバイスに「どのボタンを使うか」を設定する部分だけは、MCPからできませんでした。何度やっても「有効なボタンではない」と言われるので、そこだけは私が詳細パネルで手動設定しました(1分で終わるけど、ちょっと悔しい)。
最初に遊んだときの自己テストは、81項目すべて合格でした。
Codexが作った最初の作物。仕様どおりで、成長段階も見分けられる……けど
「モデルがいまいち」問題
いざ遊んでみて、正直な感想はこうでした。
玩具っぽさが足りない。のっぺりしている。色が地味。
最初の畑。どう見ても茶色い板が置いてあるだけ
そこで、Claude Codeに「設計書の世界観に合わせたニンジンを作ってみて」と頼みました。
のっぺりの原因は「1部位1色」
原因を調べてもらうと、最初のモデルは16色のパレットで、部位ごとに1色で塗っていたことが分かりました。低ポリでこれをやると陰影がほとんど出ないので、平べったく見えてしまうそうです。
そこで、グラデーションのランプテクスチャ に変えてもらいました。
- 256×256の画像を細い帯に分けて、帯ごとに「暗い色 → 明るい色」のグラデーションにする
- モデルの高さや「根元 → 先端」の位置に合わせて、帯のどこの色を使うかを決める
- ニンジンの根は先端が濃い赤橙で、肩が明るい橙。葉は根元が深緑で、先端が黄緑
低ポリのイラスト調表現ではよくある手法らしいのですが、効果はてきめんでした。
上段:作り直したニンジン(S1〜S4、収穫物)。下段:最初のもの
形も、根を太く丸くして、玩具っぽい浅い溝を入れてもらいました。収穫できる段階には 金色の星 が付きます。「もう収穫できるよ」の合図も兼ねた、ちょっとしたファンタジー要素です。
本当の犯人は畑だった
ニンジンを見ながら、ふと思いました。
……あれ、地味なのって畑のほうかもしれない。
そう伝えて作り直してもらったのがこちらです。
- 四隅を丸くして、縁を地面までなだらかに下げる(「置いた板」から「盛った土」へ)
- 作物の列に合わせて、ぷっくりした うね を2本
- 溝は暗い土、うねの頂上は明るい土のグラデーション
- 隅にちょっとだけ小石と草
手前の3区画が新しい畑。奥の1区画が最初の畑
最初は四隅が格子状にギザギザになったり、うねが低すぎて見えなかったりで、何度かレンダーを回しています。Blenderをスクリプトで動かしているので、数値を変えて作り直すのが速いのは助かりました。
UEFNで同じ名前のアセットを差し替えるときの注意
MCPの取り込みは、同じ名前での上書きを受け付けません。古いアセットを Archive_v01 フォルダーへ移して、元の場所に残る リダイレクタ(転送用の目印)だけ を消してから、同じ名前で取り込み直しました。Verseは同じ名前で参照しているので、コードは変えずに済みます。
ついでに、灰色の格子模様だった地面も草色に変えてもらいました。これだけでも画面の印象がかなり変わります。
2日目:ニンジンを「持たせたい」
ここからが本番(?)です。収穫したニンジンを運ぶとき、ニンジンがなぜか 頭の上に浮いて いました。そこで聞いてみました。
アイテムとして手に持ったり、実際に運んでいるように見せることはできない?
ここから長い旅が始まりました。
試行1:位置を追いかけさせる(頭上に出るバグ)
最初の作りは、サーバーで毎フレーム、ニンジンをキャラクターの前に動かす方式でした。頭の上に出ていた原因は単純で、
- キャラクターの位置を「足元」だと思って、そこから +100cm していた
- 実際の基準は 体の中心(地面から約90cm) だった
足すと約190cm、つまり頭の上です。ここはすぐ直りました。
試行2:Fortnite標準の「運搬物スポナー」(頭上に掲げる)
Fortniteには「運搬可能物スポナー」という仕掛けがあって、持ち物の見た目を自分のモデルに変える設定 があります。試してみると、ニンジンを本当に拾って運べました。ただ、
- 持ち方は 両手を頭の上に掲げるポーズ で固定
- ニンジンの位置をずらそうとすると、今度はスポナーから出てこなくなる(地面の下に生まれて落ちていく)
見た目がどうしても合わず、ここは見送りました。
試行3:自作アニメーションを上半身だけに流す
次に、AIがVerseの定義ファイル(digest)を読んで見つけてくれたのが、play_animation_controller の 上半身だけ にアニメーションを流す指定です。
Instance := Controller.Play(Anim,
?Layer := play_animation_layer.UpperBody,
?PlayCount := 100000.0,
?BlendInTime := 0.2, ?BlendOutTime := 0.2)
これなら脚は歩き・走りのままで、腕だけを「前で抱える」形にできます。
問題は、そのアニメーションをどう作るか。Epicの用意しているキャラクターのデータは保護されていて、開くことも複製することもできません。結局、公式の手順どおり NPCスポナーをシーケンサーに入れて、FK Control Rigで腕の骨を回してベイク しました。ここは画面操作が必要なので、AIに手順を教えてもらいながら私がやっています。
ポーズを付けるために置いたNPCスポナー
FK Control Rigで上腕と前腕を回してキーを打ち、「アニメーションシーケンスをベイク」
ベイクしたアニメーションをデバイスに設定しようとすると、MCPからはやはり弾かれました。そこで、Verseのコードに直接アセットを書く 方法に切り替えています。
# Content/vs_art_access.verse
VSArt<public> := module:
Meshes<public> := module{}
Anim<public> := module{}
# デバイス側
@editable
HoldAnimations:[]animation_sequence = array{VSArt.Anim.A_VS_HoldFront}
抱える姿勢が出たときは、思わず「完璧!」と言いました。
Play は、if の条件や option{} の中では呼べません(Verseの no_rollback の制約)。set X = option{Controller.Play(...)} と書くとエラーになるので、一度変数に受けてから option{} に包みます。
試行4:キャラクターの「子」にして遅れをなくす
姿勢は完璧になったのですが、走るとニンジンが少し遅れて付いてきます。サーバーで毎フレーム動かしているので、通信の遅れがそのまま見えていたんですね。
fort_character には GetEntity() があるので、ニンジンをキャラクターの 子 としてくっつけてもらいました。
Parent.AddEntities(array{E})
E.SetLocalTransform(FromTransform(transform{
Translation := Offset, # 前に55cm、高さ5cm
Rotation := IdentityRotation(),
Scale := vector3{X := 1.0, Y := 1.0, Z := 1.0}}))
動きは滑らかになりました。でも、遊んでいてやっぱり気になります。
ニンジンが視点に付いてきているのはわかるけど、キャラクターが持っているようには見えない……
キャラクターの「体の向き」に付いているだけなので、手とは連動していないんです。
試行5:アビリティ+Niagaraで「手の骨」に付ける
さらに調べてもらうと、Fortnite 42.00で入った アビリティ の仕組みに、エフェクトを 右手・左手・胸 などに付ける設定があることが分かりました。
Ability := fort_template_ability:
AbilityElements := array:
fort_ability_particle_system_element_span:
BeginTime := 0.0
EndTime := 100000.0
ParticleSystem := VSArt.FX.NS_VS_CarryCarrot_asset
ParticleAttachment := fort_ability_attach_socket.RightHand
Niagara(エフェクトの仕組み)は、粒の代わりに モデル を表示できます。そこで、「ニンジンのモデルを1個だけ、消えずに表示し続けるエフェクト」を作って、アビリティで手に付けるという作戦です。エフェクトの作成から設定まで、全部MCP経由でAIがやってくれました。
Niagaraアセットの参照名に注意
Verseでは NS_VS_CarryCarrot は「エフェクトの部品」の型として出てきます。アビリティに渡す particle_system 型は NS_VS_CarryCarrot_asset という別の名前になっていました。プロジェクトのdigest(...-Assets.digest.verse)を見ると分かります。
試行5.5:スポナー × 上半身アニメ(失敗)
途中で、私から「スポナーのニンジンを拾うときのモーションを変えれば、いい感じになるんじゃない?」と提案してみました。拾ったときに、さっきの抱える姿勢を上半身に流す作戦です。
標準の運搬ポーズと抱える姿勢が混ざって、片腕だけ上がった状態に
結果はご覧のとおり。運搬物の持ち方はFortnite側で決まっていて、上書きしきれませんでした。この作戦はここで撤退です。
現在地:右手には付いた、けど黒い
アビリティで右手に付けた結果がこちらです。
手には付いた! でも真っ黒で、上に向かって突き出ている
-
黒い:材質が「Niagaraでモデルを表示するのに使う」設定(
bUsedWithNiagaraMeshParticles)になっていなかった - 向き:手の骨の向きに合わせて、回転させる必要がある
材質の設定は直してもらい、向きは今、スクリーンショットを撮りながら角度を詰めているところです。
持たせ方の比較まとめ
| 方法 | 見た目 | 手間 | 結果 |
|---|---|---|---|
| 位置を追いかけさせる(サーバーで毎フレーム移動) | 体の前に浮く。走ると遅れる | 小 | △ |
| 運搬物スポナー(見た目を自作モデルに) | 本物の持ち物。でも頭上に掲げる | 小 | × 見た目が合わない |
| 上半身アニメ+キャラクターの子にする | 抱える姿勢で滑らか。手とは連動しない | 中 | ○ 今の安定版 |
| アビリティ+Niagaraで手の骨に付ける | 本当に手に付く | 大 | 調整中 |
| 独自武器テンプレート | 武器として片手で持つ | 中 | 未挑戦(銃の構えになる・ベータ機能) |
やってみて分かったこと
-
UEFNのMCPは強いけど、万能ではない
- 取り込み、材質、当たり判定、配置、Niagaraの作成、Verseのビルドまでできる
- デバイスに「どのボタン/どのアニメを使うか」を設定する部分はできない → Verseに直接書くか、手動で設定
- Epicのキャラクターなどのアセットは開けないし、複製もできない
- ゲーム中に変更をプッシュしようとすると「Refreshコマンドは使用できません」になることがある → エディターのボタンからなら通る
-
Verseは「効果(effect)」の制約が厳しい:
no_rollbackの関数はifの条件やoption{}の中では呼べない -
困ったらdigestを読む:Webのドキュメントに載っていない引数(
play_animation_layer.UpperBodyなど)が普通に書いてある - AIを複数動かすなら、最初にファイルの担当を決める:これだけでAI同士の衝突はゼロ
- 見た目の違和感の原因は、気にしていた場所とは別にあることがある(ニンジンだと思ったら畑だった)
おわりに
AI 3体体制は、土台づくりの速さという点では大成功でした。設計書を渡したその日のうちに、「植える → 育つ → 収穫 → 売る → セーブ」が一通り動いています。
一方で、「手に持っているように見える」みたいな 見た目の手触り の部分は、結局、遊んで、スクリーンショットを撮って、AIに伝えて、直してもらって……の繰り返しでした。ここはまだ、人間が遊んで判断するところだなと感じています。
ニンジンがちゃんと手に収まったら、続きを書きます。ここまで読んでいただき、ありがとうございました!







