はじめに
「これで遊べると本気で思ってますか」。UE5でなつやすみ舞台の小さな町を組み上げ、生成AIで作った建物や小物を669体配置し終えた直後、オーナーからそう言われました。ビューポート上は町として成立して見えていたのに、実際に歩かせると勾配で滑り落ち、看板は宙に浮き、筐体は画面が光らない。「生成した」と「使える状態になっている」はまったく別だという話です。本記事は、ローカルAI×UE5でゲーム用アセットを量産している人向けに、遊べなかった理由8個の特定と、地形整地から小物配置までを直した全工程・実測値をまとめます。
直接原因5個 — なぜ遊べなかったのか
ビューポートのスクリーンショットだけでは気づけなかった不具合を、実際に歩かせて洗い出すと5つの直接原因が出てきました。
-
地形を一度も整地していない:商店街の建物28軒が標高差40m、勾配にして28%の山腹に建っていました。「歩けない」の唯一にして最大の原因です。
-
回転処理が抜けていた:ワールドを230m四方から141m四方へ縮小した際のスクリプトが、座標のスケールだけを扱い回転を一切扱っていない(該当コードが0行)。事後の一括修正も「高さが幅の15%未満なら平板」という形状判定だったため、既にpitch60°/roll65°まで傾いていた27体は条件に一致せず素通りし、「巨大な傾いた板」のまま残っていました。
-
テクスチャを取り込むコードが存在しない:544枚のテクスチャが未使用のまま放置。手順書には「Content Drawerへドラッグ」と手作業前提で書かれていました。
-
マテリアルが未接続:マテリアル割り当てスクリプトが
AlbedoTexパラメータを宣言だけして代入せず、UseTextureはStaticSwitchパラメータなのにScalarパラメータのセッターで叩いていました。しかも対象パスが旧レベルを向いたままで、既定値はDRY_RUN。 -
小物を配置するコードが無い:216種類の小物3Dアセットのうち、実際に町へ置かれていたのはわずか24個でした。
整地前の商店街。地面の勾配が28%あり、目線を合わせると建物の傾きがそのまま見える。
構造的原因3個 — なぜ見落としたのか
個々のバグより深刻だったのは、なぜここまで気づけなかったかという運用側の問題でした。
-
レベルを組み立てた処理のソースが1本も残っていない:13項目の作業のうち、ファイルとして残っていたのはわずか2つ。残りは全部インラインでUE MCP経由に流して実行し、そのまま消えていました。つまり再実行も修正もできない状態で「完成」を迎えていたということです。
-
「生成した」を「使った」と数えていた:3Dアセット216体を生成し終えた時点で進捗を語っていましたが、取込・マテリアル接続・配置という後工程が一度もタスクとして立っていませんでした。
-
合格判定を件数でやっていた:ワールド縮小処理は「669体すべて移動・接地成功」と報告されており、数字自体は正しいものでした。ただしそれは接地判定が通ったという意味でしかなく、実際に歩けるかどうかを一度も見ていないまま合格にしていました。当初計画に書いていた合格ラインは「2人でPIE起動し、街道を踏破できること」でしたが、その基準を自分で通していなかったのです。
対策 — レベルを触る操作は必ずファイルとして残す
原因6の「ソースが残っていない」を潰すため、以降の全操作をスクリプト化しました。以下の7本を順に実行する構成です。
ue_export_layout.py # 現物のTransformをJSON台帳へ書き出す(774体)
ue_flatten_ground.py # Landscapeを退避し、34m×36枚のCubeで平地を作る
ue_normalize_layout.py # 台帳上の回転をゼロへ正規化(UE操作なしで完結)
ue_apply_layout.py # 正規化済み台帳を反映(冪等・2パス)
ue_import_textures.py # テクスチャ84枚を一括取込
ue_wire_materials.py # マテリアルv2マスター+MI15種を正しく接続
ue_shot.py # 検証用スクリーンショット
この手順を通した結果、実測値は次の通りです。
項目実測値
傾いたまま残っていたアクター395体を除去
平地へ接地し直したアクター762体
新規配置した小物318体
新規配置した看板95枚
踏んだ罠5個 — MCP経由UE操作の落とし穴
台帳化・一括修正の過程で、UE Model Context Protocol(UE MCP)経由の操作特有の罠を5つ踏みました。
-
set_actor_transformは部分更新できない:位置だけを渡して呼ぶと、省略した回転・スケールが初期値に巻き戻ります。これに気づかず762体へ位置だけ送った結果、対象が一斉にワールド原点へ collapse しました。 -
PIE中はPIE複製ワールドを操作してしまう:Play In Editor中にMCP経由で変更しても、それは実行用に複製された別ワールドへの変更で、エディタ本体のレベルには反映されません。
-
delete_expressionはUE5.8でクラッシュする:マテリアルのノードを消す操作でエディタが落ちます。作り直す場合は既存ノードを消さず、新規マテリアル+親差し替えで対応しました。 -
クラッシュ後はCrashReportClientがMCPポートを握り続ける:さらに再起動時に「Restore Packages」の確認モーダルが出て、自動起動フローがそこで止まります。
-
サンドボックス実行では
import unrealが使えない:MCP越しの1呼び出しは約0.4秒かかるため、台帳774体のような規模を捌くにはバッチ設計が前提になります。
小物配置 — 実測なしでは倍率がバラバラ
小物未配置(原因5)を解消するために、まず実物の寸法を測るところから始めました。216種類の3Dアセットについて、生成時の自然な出力サイズを1回ずつ計測し、台帳(prop_catalog.json)にまとめます。そこから目標の実寸高さとの比を取ると、倍率の実測分布は5.1倍〜893.9倍でした。取込んだメッシュのスケールは統一されておらず、一律の倍率を仮置きすればほぼ確実にどこかが破綻します。
配置ロジックは次の3点です。
-
建物位置を台帳から取得し、**半径0.8〜3.2mの環に固定乱数(seed=19970815)**で小物を散らす。
-
地区ごとに使える小物カテゴリを絞る(神社の敷地に自動販売機を出さない、など)。
-
道の土のテクスチャは当初
TexScale 24で貼っていたところ、ひび割れ1本が40cm級に見えるほど間延びしていたため、70まで上げて実寸化。目安は「テクスチャ1枚が1.5〜2mに見える倍率」に置きました。
整地・小物配置・露出調整後の道路上ショット。看板95枚・小物318体が町に乗った状態。
最終到達点 — 筐体が立ち、画面が光り、キャラが地面に立った
仕上げの段階でも、原因が想像と違う箇所に隠れていました。
-
ゲームセンターの筐体が見えなかった真因は「Screenコンポーネントの StaticMesh が空」だったことです。ブループリントのグラフを書き換えた際にメッシュ参照が失われていました。修正は、テンプレート側ではなくインスタンス側のコンポーネントに
set_propertiesを当てると反映されることを確認して対応しています。あわせて対象アクター(Cabinet_A)の実測サイズが1.72cm(Blenderの1/100スケールのまま)だったため、実測から倍率96を算出して適用しました。 -
地面はPlane(板)ではなくCube(箱)で作る必要がありました。板のままだと、落下中のキャラクターがすり抜けてZ=-352cmまで落ち続けます。20cm厚の箱に置き換えて解決しました。
-
PlayerStartが建物の中に置かれていたのも落下の一因でした。上から下向きにトレースし、最初に当たる面がZ 0〜30cmの範囲にある場所を地面とみなして移動する処理で解消しています。
-
地面判定のトレース処理では、戻り値がZ座標ではなく距離である点も踏み違えました。「開始点のZ座標 − 戻り値」で高さを求める必要があり、戻り値そのものを高さとして扱うと数百cm単位でずれます。
-
最後に、エディタ上のスプライト(Billboardアイコン)は当たり判定を持たないため、接地判定は
only_colliding_components=Trueを指定して測る必要がありました。そうしないと、実体のない箱が下に膨らんで宙に浮いたような判定結果になります。
まとめ — 持ち帰れる学び
-
生成した≠使った。取込・接続・配置がタスクとして立つまでは、生成数がいくら多くても進捗ではない。
-
合格判定は件数ではなく実挙動で行う。今回でいえば「2人でPIE起動し、街道を踏破できるか」。
-
レベルを触る操作は必ずファイルとして残す。インラインで流した操作は再実行も修正もできない。
-
取込メッシュの倍率は実測なしでは決められない。今回の分布は5.1倍〜893.9倍という広さで、一律の仮値では必ずどこかが壊れる。

