【Unreal Engine】stat unitで重さの原因を特定する:推測で最適化しない

作成: 2025-12-12最終更新: 2026-07-20

UE5のパフォーマンス調査はstat unitから始まります。Frame・Game・Draw・GPUの4つの数字が何を意味するか、どれがボトルネックかの判断表、stat gameやGPU Visualizerへの掘り下げ、重いレベルの犯人を特定する実践まで解説。

レベルにアセットを並べていったら、いつのまにか動きがカクつくようになった。とりあえずポストプロセスを弱めて、テクスチャ解像度を下げて、影の設定をいじってみる。それでも変わらない。

このとき起きているのは、原因が分からないまま対処している状態です。UE5には、重さの原因が CPUのゲームロジック・CPUの描画処理・GPU のどこにあるかを数秒で切り分けるコマンドがあります。この記事では、stat unit が表示する4つの数字の読み方と、そこから原因を絞り込んでいく手順を解説します。

4つの数字が並ぶ計測パネルと、それを見上げるソフトブルーの人形

この記事でわかること

  • 体感で判断すると 見当違いの場所を直してしまう 理由
  • stat unitFrame / Game / Draw / GPU が何を測っているか
  • 4つの数字から ボトルネックを判断する表
  • stat game / stat scenerendering / GPU Visualizer への 掘り下げ
  • 実践:重いレベルを開いて、 犯人を特定して直すまで を1本通す

Sponsored

なぜ「まず測る」なのか

パフォーマンスの問題には、厄介な性質があります。 症状はどれも同じ「カクつく」なのに、原因はまったく違う場所にある ということです。

テクスチャを軽くしてもフレームレートが変わらないなら、そもそもボトルネックはテクスチャではありません。この状態で設定をいじり続けると、画質だけが下がって速度は元のまま、という結果になります。

体感で当てずっぽうに設定を変える人形と、計測パネルを見て1箇所だけを直す人形の対比

もう1つ、最初に頭を切り替えておきたいのが 単位 です。パフォーマンスの話ではFPS(1秒あたりのコマ数)よりも、 フレームタイム(1コマにかかった時間・ミリ秒) で考えます。

目標FPS許される1フレームの時間
30 FPS33.3 ms
60 FPS16.6 ms
120 FPS8.3 ms

FPSで見ると誤解が起きます。120FPSから60FPSへ落ちるのも、40FPSから30FPSへ落ちるのも、どちらも 8.3ミリ秒の悪化 で、負荷としては同じ大きさです。「FPSが半分になった」と「FPSが10しか下がっていない」は、体感ほど差がありません。ミリ秒で見ると、この2つが同じ規模の出来事だと分かります。

だから計測コマンドは、FPSではなくミリ秒を表示します。 足し引きできる単位で見る ためです。


stat unit:4つの数字の意味

プレイ中にコンソール(既定では ` キー)を開き、stat unit と入力します。画面右上に数字が並びます。

コンソールが開かないとき: 日本語キーボードでは半角/全角キーと干渉して開けないことがあります。Project Settings → Engine → Input → ConsoleConsole Keys にキーを追加してください。

Frame・Game・Draw・GPUの関係図。Game(CPU)とDraw(CPU)とGPUが順に流れ、Frameはその全体を表す
項目測っているもの誰が働いているか
Frame1フレーム全体にかかった時間全体の結果
GameBlueprint・C++・AI・物理などのゲームロジックCPU(ゲームスレッド)
Draw「これを描いて」とGPUに指示を出す処理CPU(描画スレッド)
GPU実際にピクセルを塗る処理GPU

ここでいちばん誤解されやすいのが Draw です。Drawは「描く時間」ではなく、 CPUが描画命令を組み立てて渡す時間 です。実際に描くのはGPUで、そちらは GPU に出ます。オブジェクトを1000個置いたときにDrawが伸びるのは、GPUが疲れているからではなく、CPUが1000回分の指示書を書いているからです。

そしてもう1つ、この3つは 同時に走っています 。UEはゲームスレッド・描画スレッド・GPUをパイプライン処理していて、GPUが今のフレームを塗っている間に、ゲームスレッドは次のフレームを計算しています。

だから Frameは3つの合計ではありません 。3つのうちいちばん遅いものに引きずられて決まります。Game 5ms / Draw 4ms / GPU 15ms なら、Frameは24msではなく15ms強になります。 列で並んだ処理の速度は、いちばん遅い担当者が決める わけです。

この性質が、最適化の方針を決めます。 いちばん大きい数字以外を削っても、Frameは動きません。

Sponsored

どれがボトルネックかを判断する

4つの数字が出たら、次の順で読みます。

  1. Frame を見て、目標(60FPSなら16.6ms)を超えているか確認する
  2. 超えていたら、 Game・Draw・GPU のうちFrameにいちばん近い数字 を探す
  3. その1つだけを、最適化の対象にする
Game・Draw・GPUの3本の棒グラフのうち、最も高い棒にだけ印がつく判断イメージ
いちばん大きいのが疑うものよくある原因
GameゲームロジックEvent Tickの使いすぎ、毎フレームの Get All Actors Of Class、重いAI、大量のキャラクター
Draw描画命令の数オブジェクトが多すぎる、マテリアルスロットが多い、インスタンス化されていない、影を落とす物が多い
GPU塗る負荷シェーダーが複雑、半透明の重なり(オーバードロー)、ポストプロセス、解像度が高い

判断のときに気をつけたい点が2つあります。

GPUの数字が張り付いていたら、垂直同期を疑う。 すべての数字が16.6ms前後で不自然に安定しているなら、それは限界ではなく フレームレート制限に当たっているだけ です。t.MaxFPS 0 で上限を外し、エディタの Editor Preferences → Performance にあるスムージング設定も外してから測り直してください。

GameとDrawが近い値で並んでいるときは、まずGameを疑う。 ゲームスレッドが遅れると描画スレッドが待たされ、Drawの数字にその待ち時間が乗って見えることがあります。Gameを軽くしたらDrawも一緒に下がった、というのはよくある話です。


1段深く掘る:Game / Draw / GPU それぞれの次の一手

担当が分かったら、その中のどこが重いのかを調べます。 stat unit は「誰が」までしか教えてくれません。 「何が」は次のコマンドで見ます。

stat unitから、Gameならstat game、Drawならstat scenerendering、GPUならGPU Visualizerへ分岐する掘り下げの流れ図
ボトルネック次に打つもの見えるもの
Gamestat gameTick、AI、物理など処理カテゴリ別の時間
Drawstat scenerenderingドローコール数、可視プリミティブ数、カリングの処理時間
Drawstat initviewsフラスタムカリング・オクルージョンで何個除外されたか
GPUGPU Visualizer(Ctrl + Shift + ,描画パス(Base Pass / Shadow Depths / Lighting / Post Processing)ごとの時間
ロードのカクつきstat streamingテクスチャ・メッシュのストリーミング状況

stat game で見たいのは、まず Tick Time です。ここが大きければ、毎フレーム動いているアクターが多すぎます。Tickを減らす具体的な手立ては Tickを使わないBlueprint設計 にまとめてあります。

stat scenerendering では、Mesh draw calls の数を見ます。数千という単位になっていれば、描く対象そのものを減らす段階です。

GPU Visualizer は、プレイ中に Ctrl + Shift + ,(Ctrl + Shift + カンマ)を押すと開きます。コンソールに profilegpu と打っても同じです。1フレーム分のGPU処理を分解した一覧が出るので、時間の大きいパスから順に見ていきます。Shadow Depths が飛び抜けていれば、動的な影を落とす物が多すぎるということです。

Unreal Insights は、この先の道具です。stat が「今この瞬間の数字」を見るのに対し、Insightsは 数十秒分を丸ごと記録して、後からタイムライン上で見返す ためのツールです。「30秒に1回、原因不明のスパイクが出る」のように、瞬間を目で捉えられない問題を追うときに使います。エディタのツールバー右側にあるトレースアイコンから記録を開始できます。まずは stat unit で足りるので、ここでは入口の紹介だけにしておきます。なお、新しい敵やエリアが出た瞬間に固まるロード起因のスパイクなら、そもそも読み込みを分散させる ソフト参照と非同期ロード が効きます。

Sponsored

実践:重いレベルの犯人を特定する

オープンワールドの森に木を並べたとき、タワーディフェンスで敵が100体を超えたとき、ホラーゲームで小物を敷き詰めたとき。 「作り込んだら急に重くなった」 はどのジャンルでも起きます。ここでは重いレベルを意図的に作り、犯人を捕まえて直すまでを1本通します。

計測前は22.5msだったフレームタイムが、犯人を直したあと12.8msへ下がる様子を示した棒グラフ

再現条件を作る

Third Personテンプレートの ThirdPersonMap を開き、次の2つを追加します。

追加するもの内容個数
木のStatic Mesh三角形 8,000程度のもの(Starter Contentの SM_Rock でも可)2,000個
BP_SpinnerActorを新規作成。Static Mesh Component を1つ追加し、Event TickAdd Actor Local Rotation(Delta Rotation の Yaw に Delta Seconds × 90800個

配置は手作業でなくてかまいません。1個置いて Alt + ドラッグで複製するか、レベルブループリントの BeginPlay から Spawn Actor from Class をループで回してください。

ステップ1:スタンドアロンで測る

ツールバーの Play の横にあるドロップダウンから Standalone Game を選んで起動します。PIEはエディタの描画が混ざるので、ここでは避けます。

` キーでコンソールを開き、stat unit を実行します。次のような数字が出ます。

Frame:  22.51 ms
Game:   21.42 ms
Draw:    8.23 ms
GPU:    12.09 ms

Frame 22.51ms は 44FPS 相当です。60FPSの目標(16.6ms)に届いていません。

Frameにいちばん近いのは Game の 21.42ms です。犯人はゲームロジック側で、木の描画(Draw / GPU)ではありません。ここでテクスチャや影の設定をいじっていたら、時間を無駄にしていました。

ステップ2:Gameの中身を割る

続けて stat game を実行します。一覧の中の Tick Time に注目してください。

Gameの21.42msのうち、大半がTickに消えているはずです。BP_Spinner が800個、毎フレーム回転処理を実行しているのだから当然です。

ステップ3:直して、測り直す

BP_Spinner を開き、Class DefaultsActor Tick → Start with Tick Enabled のチェックを外します。回転は止まりますが、ここでは負荷の切り分けが目的です。

もう一度 Standalone Game で起動して stat unit を実行します。

Frame:  12.84 ms
Game:    4.11 ms
Draw:    8.02 ms
GPU:    12.01 ms

Game が 21.42ms から 4.11ms へ落ち、Frame は 22.51ms から 12.84ms になりました。目標の16.6msを下回っています。

そして数字の並びが変わったことにも注目してください。今いちばん大きいのは GPU の 12.01ms です。犯人を1人捕まえると、次の犯人が前に出てきます。ここからさらに詰めるなら、次の対象はGPU側です。

うまくいかないときの切り分けです。

  • 数字が16.6msあたりで動かない → 垂直同期かフレーム上限に当たっています。t.MaxFPS 0 を実行してから測り直してください
  • Gameが下がらないBP_Spinner 以外にTickしているものがあります。stat gameTick Time をもう一度確認してください
  • PIEと数字が大きく違う → 正常です。PIEはエディタの描画が乗るので、Standaloneより重く出ます

ポイントは2つです。

  • いちばん大きい数字だけを相手にする: Game 21ms / Draw 8ms の状態でDrawを半分にしても、Frameは1ミリ秒も動きません。3つのうち最大のものを1つ選び、それが最大でなくなるまで直す。この繰り返しが最適化の基本形です
  • 直したら必ず測り直す: 犯人が入れ替わるからです。上の例ではGameを直した瞬間にGPUが最大になりました。測る → 直す → また測る、をセットで回してください

Gameが重いと分かったなら Tickを使わないBlueprint設計 へ。弾やエフェクトを毎フレームSpawnしているのが原因なら オブジェクトプーリング で使い回します。Drawが重いなら、次の記事 LOD・カリング・オクルージョン で描く量そのものを減らします。

Sponsored

おまけ:先に知っておくと良いこと

関数まで降りたくなったらUnreal Insights。 stat は「どのスレッドが詰まっているか」までを教える道具です。「どのActorの、どの処理が何ミリ秒か」まで確定させたくなったら、記録して後から読む Unreal Insights が次の一手になります。

stat unitgraph で変動を見る。 stat unit の数字は目まぐるしく変わるので、瞬間的なスパイクを読み取りにくいことがあります。stat unitgraph を実行すると、同じ4項目が折れ線グラフで表示されます。「特定の敵が出現した瞬間だけ跳ねる」といった問題は、グラフの方が見つけやすいです。

表示を消すのは同じコマンド。 stat 系のコマンドはトグルです。もう一度 stat unit と打てば消えます。stat none ですべての表示を一括で消すこともできます。

計測は同じ場所・同じ視点で。 カメラの向きが変われば見えるものが変わり、DrawもGPUも変わります。最適化の前後を比べるときは、必ず同じ位置・同じ方向から測ってください。数値の差が本当に修正の効果なのか、単に見ている景色が違うだけなのかが分からなくなります。

エディタでのシェーダーコンパイル中は測らない。 右下にコンパイルの進捗が出ている間は、CPUがそちらに取られて数字が跳ねます。落ち着いてから測ってください。

RHIT という行が見えることがあります。 RHIスレッド(描画命令をグラフィックスAPIへ流す専用スレッド)の時間です。通常は気にしなくて構いませんが、Drawが低いのにここだけ高い場合は、そこで詰まっていると判断できます。


まとめ

段階やること
1. 大づかみstat unit を実行し、Frame・Game・Draw・GPU を見る
2. 犯人の特定Game・Draw・GPU のうち Frameにいちばん近い数字 を1つ選ぶ
3. 掘り下げGameなら stat game、Drawなら stat scenerendering、GPUなら GPU Visualizer
4. 修正その1箇所だけ を直す
5. 再計測もう一度 stat unit 。犯人が入れ替わっていないか確認する

押さえておきたいのは3点です。 Frameは3つの合計ではなく、いちばん遅いものに引きずられる こと。 Drawは描く時間ではなく、CPUが描画命令を出す時間 であること。そして 最大の数字以外を削ってもFrameは動かない こと。

この3つが腑に落ちると、「とりあえず影を切る」といった当てずっぽうの最適化から抜けられます。

次の記事では、Drawが犯人だったときの直し方として LOD・カリング・オクルージョン を扱います。その次は レベルストリーミングとWorld Partition で、そもそも読み込む量を減らす方法へ進みます。

あなたのプロジェクトを一度 Standalone Game で起動して、stat unit を打ってみてください。いちばん大きい数字は、GameとDrawとGPUのどれでしたか。

さらに学ぶために