草原に木を3,000本並べた。見た目は満足だが、stat unit を出すと Draw が伸びている。木を減らすしかないのだろうか。
減らさずに済む方法があります。同じメッシュを大量に置くとき、描画命令を1回にまとめる——これがインスタンシングです。 この記事では、Drawが増える理由から、ISM・HISM・Foliageの使い分け、Blueprintからの操作、そしてNaniteを使っているなら要らないのかという疑問までを扱います。
この記事でわかること
- 1個のメッシュ=1回の描画命令(Drawが伸びる正体)
- インスタンシングは 位置だけを配列で渡す
- ISM / HISM / Foliage の使い分け(迷ったらISM)
- 距離の間引きは
Cull Distanceを自分で入れるAdd Instanceと インデックス管理の注意- Naniteを使っているなら要るのか
- 実践: 3,000本をISMに置き換えてDrawを比べる
Drawが増える理由
statコマンド の stat unit に出る Draw は、CPUがGPUへ「これを描いて」と命令を出す作業にかかった時間です。
そしてざっくりした見立てとして、同じメッシュを別々のActorとして大量に出すと、命令の数が増えやすいと考えてください。

木を3,000本置けば、命令も3,000回に近い数になります。1回あたりは短くても、それだけ積み上がれば Draw は伸びます。
「1 Actor = 1 命令」は、あくまで入口の考え方です。 実際の数は マテリアルスロットの数・描画パス・影・カリング などにも左右されるので、ぴったり3,000回とは限りません。ここでは 「数が増えると命令も増える」 という感覚だけ掴んでください。正確な数は、あとで
stat rhiで実測します。
ここで大事なのは、GPUが重いのではなくCPUが重いということです。木のポリゴ ン数を半分にしても、命令の回数は3,000回のままなので Draw は下がりません。
| 症状 | 原因 | 効く対策 |
|---|---|---|
Draw が大きい | 命令の回数が多い | インスタンシング、カリング |
GPU が大きい | 描く量(ポリゴン・ピクセル)が多い | LOD、解像度、シェーダーの軽量化 |
stat unit で Draw が突出しているなら、この記事の対象です。GPUが大きい場合は LODとカリング や Nanite の担当になります。
まとめて1回で描く
インスタンシングの考え方は、拍子抜けするほど単純です。
「この形を、この3,000箇所に描いて」と1回で伝える。

送るものは2つだけです。
- メッシュの情報(形とマテリアル)を 1つ
- 位置・回転・スケール を 3,000個ぶんの配列
これで命令は 1回 になります。GPUは同じ形を場所を変えて3,000回描きますが、CPUの仕事は1回ぶんです。
条件は、全部が同じメッシュで、同じマテリアルであることです。木の種類が3つあれば、3回の命令になります(それでも3,000回よりは圧倒的に少ない)。
同じ形でも見た目は変えられます。 インスタンスごとに回転とスケールを変えられるので、木を並べても機械的には見えません。色や質感を個体ごとに変えたい場合は、Material Instance の
Per Instance Custom Dataを使う道もあります。
3つの選択肢
UEでインスタンシングを使う道は3つあります。内部の仕組みは繋がっています。

| 方法 | 何をするか | 向いているもの |
|---|---|---|
| Instanced Static Mesh(ISM) | Componentに位置を追加して増やす | まず試す基本。動かす可能性があるもの、Naniteメッシュ |
| Hierarchical ISM(HISM) | ISM + 階層構造を作る | 数千規模でほぼ動かない草木や岩 |
| Foliageツール | エディタで塗って配置する。内部はHISM | 手で配置したい自然物 |
迷ったら、まずISMから試してください。 「大量ならHISM」という言い方をよく見かけますが、これは古いUEの前提です。現行のISMはGPU側でインスタンスごとのカリングとLODを行うので、「ISMは全部描き続ける」わけではありません。
HISMが効くのは、数千規模で、ほぼ動かないものです。HISMは配置を階層構造(ツリー)として持つため、広い範囲から「見えている塊」を素早く絞り込めます。ただしその構造を作るコストがかかり、インスタンスを動かすたびに作り直しが必要になります。
| 状況 | 選ぶもの |
|---|---|
| 数十〜数百、あるいは動かす可能性がある | ISM |
| Naniteが有効なメッシュ | ISM(Nanite側の仕組みと素直に噛み合う) |
| 数千規模で、置いたら動かさない草木・岩 | HISM |
| エディタで手で塗りたい | Foliageツール(中身はHISM) |
Foliageツールは、HISMを手で塗るためのUIです。中身は同じなので、「エディタで配置したい」ならFoliage、「Blueprintから動的に増やしたい」ならComponentを自分で置く、という使い分けになります(→ ランドスケープの基本)。
そして、どちらが速いかは実測で決めてください。 同じ数・同じ視点で置き換えて stat unit を比べる。これがこの領域の唯一の正解です。
Blueprintから使う
Blueprintでは、Instanced Static Mesh Component(または Hierarchical Instanced Static Mesh Component)を追加し、メッシュを1つ指定してから、位置を足していきます。
| ノード | 何をするか | 戻り値 |
|---|---|---|
| Add Instance | インスタンスを1つ追加する | 追加された番号(Index) |
| Remove Instance | 指定した番号を削除する | 成否 |
| Update Instance Transform | 指定した番号の位置を変更する | 成否 |
| Clear Instances | 全部消す | — |
Add Instance に渡すのは Transform(位置・回転・スケール)です。ループで3,000回呼べば3,000本並びます。
そして、ここに定番の落とし穴があります。

Remove Instance で途中の1つを消すと、後ろの番号が繰り上がります。
- 削除前:
01234 2を削除 →0123(元の3が2になり、4が3になる)
「この木は3番」と変数に持っていた場合、その3番は別の木を指すようになります。
対策は2つです。
- 番号を保持しない: 消すときにその場で番号を取得して即座に消す
- 消さない: 位置を地面の下へ動かす(
Update Instance Transform)ことで「消したように見せる」
これは配列をループ中に削除するときと同じ構造の問題です。インスタンスは内部的に配列で管理されているので、配列の注意点がそのまま当てはまります。
効かない場合とNaniteとの関係
インスタンシングが向かない場面もあります。
| 状況 | 理由 |
|---|---|
| マテリアルが違う | 別のバッチになる。1回にまとまらない |
| 個別に動かしたい | 動かすには Update Instance Transform が必要 で、数が多いと逆に重い |
| 個別に当たり判定やイベントが要る | インスタンスはActorではないので、Overlap や個別のロジックを持てない |
3つ目が実用上いちばん効きます。 インスタンスは「描画されるだけの存在」です。木を撃って倒したい、宝箱を開けたいといった個体ごとの挙動が要るものはActorのままにしてください。
そして、多くの人が気にする Naniteとの関係 です。

Nanite が有効なメッシュは、Nanite側でまとめて処理されます。 そのため、Naniteメッシュを大量に置いている場合、ISMへ置き換えても Draw の削減幅は小さくなります。
ただし、NaniteはすべてのメッシュにそのままON にできるわけではありません(対応状況はバージョンで前進しています)。また既存プロジェクトでNaniteを使っていないメッシュも当然あります。
判断は簡単です。stat unit の Draw を測って、置き換えて、もう一度測る。 減れば効いています。減らなければ、そのケースでは別の対策(LODとカリング)を 見てください。推測ではなく数値で決めるのが、この領域の唯一の正解です(→ Unreal Insights)。
実践:3,000本をISMにしてDrawを比べる
オープンフィールドの森、ローグライクの瓦礫、タワーディフェンスの障害物。同じものを大量に置く場面はどのジャンルにもあります。ここでは実際に置き換えて、数値がどう動くかを見ます。
再現する状態を作る
| 用意するもの | 内容 |
|---|---|
| メッシュ | 好きなStatic Mesh 1つ(Starter Contentの SM_Rock など) |
| 配置数 | 3,000個 |
| 計測環境 | Standalone Game で起動(エディタのPlayではない) |
まず Actorとして3,000個 置きます。手で置くのは無理なので、Blueprintで並べます。
BP_ForestA(Actor)を作り、Event BeginPlay で For Loop(0〜2999)を回し、Spawn Actor from Class でStatic Mesh Actorを格子状に配置します。

① Actor版のDrawを測る
Standaloneで起動し、コンソールに stat unit と入力してください。Draw の数値を書き留めます(同時に stat rhi の DrawPrimitive calls も見ると、命令の回数そのものが分かります)。
測るときはカメラの位置と向きを固定してください。 見える範囲が変わると数値も変わるので、前後の比較になりません。
② ISM版に置き換える
BP_ForestB(Actor)を新しく作ります。
- Componentに
Instanced Static Meshを追加し、名前をTreesにする - 詳細パネルの
Static Meshに、①と同じメッシュを指定する Event BeginPlayでFor Loop(0〜2999)を回す- ループの中で、格子状の位置から
Make Transformを作り、Add Instance(Target:Trees)へ渡す
Spawn Actor from Class が Add Instance に変わっただけで、配置のロジックは①と同じです。
③ 遠くを間引く(値を明示する)
距離で間引く動きは、自分で値を入れないと起きません。Componentの詳細パネルで2つ設定します。
| 項目 | 値 | 意味 |
|---|---|---|
Instance Start Cull Distance | 4000 | この距離から薄れ始める |
Instance End Cull Distance | 6000 | この距離で完全に消える |
単位はcmなので、40m〜60mで消える設定です。値を入れずに「遠くが消えるはず」と期待しても消えません。ここは自動ではなく、自分で決める数値です。

BP_ForestB(イベントグラフ)
Event BeginPlay
→ For Loop(First = 0, Last = 2999)
Loop Body:
// 格子状に並べる(50列 × 60行、間隔400)
x = (Index % 50) * 400.0
y = (Index / 50) * 400.0
t = MakeTransform(
Location = (x, y, 0.0),
Rotation = (0, 0, RandomFloatInRange(0, 360)), // 向きを散らす
Scale = (1.0, 1.0, RandomFloatInRange(0.8, 1.2)))
→ Trees.AddInstance(Transform = t, bWorldSpace = false)
確かめる
BP_ForestB に差し替えて、①とまったく同じ位置・同じ向きからStandaloneで起動し、stat unit を出してください。
見た目はほぼ同じ(岩が3,000個並んでいる)のに、Draw の数値がはっきり下がっています。stat rhi の DrawPrimitive calls を見れば、3,000回近くあった命令が大きく減っているのが分かります。
続いて手順③の効果を見ます。カメラを後ろへ引いて、岩の群れから離れてください。 End Cull Distance の 6000(60m)を越えたあたりから、遠くの岩が消えていきます。値を 6000 から 3000 に変えれば、消え始める位置が手前に寄るのが見えます。これは設定した値どおりの挙動で、自動ではありません。
うまくいかないときの切り分けです。
- 1個も表示されない →
Static Meshの指定を忘れています。Componentの詳細パネルで確認してください - 全部が同じ場所に重なっている →
Make Transformに渡すLocationが全ループで同じです。Indexから座標を計算できているか確認します - Drawが下がらない → 元のメッシュが Nanite有効である可能性が高いです。すでにまとめて処理されているので、この置き換えの効果は出ません
- 遠くの岩が消えない →
Instance Start / End Cull Distanceが0のままです。0は「間引かない」の意味なので、値を入れてください - 数値が毎回違う → カメラの位置か向きが変わっています。同じ場所から測り直してください
ポイントは2つです。
- Drawが大きいときにだけ効く:
stat unitで Draw が突出しているのを確認してから使ってください。GPUが原因の重さには効きません - 個別の挙動が要るものは置き換えない: インスタンスはActorではないので、当たり判定やイベントを個別に持てません。背景の飾りだけをISMへ移すのが基本の線引きです
- 距離の間引きは自分で値を入れる:
Instance Start / End Cull Distanceは既定で0(=間引かない)です。「置き換えたのに軽くならない」ときは、まずここを見てください
木を「エディタで手で塗りたい」なら、同じ仕組みをUIから使える Foliageツール が便利です(→ ランドスケープの基本)。
おまけ:先に知っておくと良いこと
PCGは内部でこれを使っている。 PCG の Static Mesh Spawner が置くメッシュは、内部でインスタンス化されています。「数千本の木を撒いても比較的軽い」のは、この記事の仕組みが効いているからです。PCGを使っているなら、すでにインスタンシングの恩恵を受けています。
マテリアルは1つに寄せる。 木の葉と幹でマテリアルが2つあると、それだけでバッチが2つに分かれます。背景の飾りは、可能ならマテリアルを1つにまとめるとバッチ数が減ります(テクスチャをアトラス化する、という発想です)。
エディタツールと組み合わせる。 Editor Utility Widget で「選択したActorをまとめてISMに変換する」ツールを作ると、既存のレベルを一括で最適化できます。手作業で置いた大量のActorがある場合に効きます。
当たり判定は持てる。 インスタンスは個別のイベントを持てませんが、コリジョン自体はインスタンスごとに機能します(プレイヤーは木に衝突します)。「ぶつかるけれど、個別に反応はしない」——背景の障害物にはこれで十分です。
まとめ
stat unitの Draw は命令の回数 。数が増えれば命令も増える(正確な数は実測する)- インスタンシングは 「この形を、この3,000箇所に」と1回で伝える
- 迷ったらまずISM 。現行ISMもGPU側でインスタンス単位のカリングとLODを行う。 HISMは数千規模でほぼ動かないもの向け
- 距離の間引きは
Instance Start / End Cull Distanceを自分で入れる (既定は0=間引かない) Add Instanceは 番号を返す 。Remove Instanceで 番号が繰り上がる点に注意- Naniteが有効なら効果は小さい 。測って、置き換えて、また測る
3,000本の木を減らさずに Draw を下げられる、というのがこの機能の価値です。あなたのレベルで、いちばん数が多いメッシュは何でしょうか。