Unityを触り始めた頃って、つい Update() に「移動処理」「入力処理」「UI更新」「デバッグ表示」…と、全部を詰め込みがちですよね。動いているうちは良いのですが、だんだんと
- 何がどこで更新されているのか分からない
- ちょっとした修正で他の処理が壊れる
- パフォーマンスのボトルネックが追えない
といった問題にぶつかります。
特に「ゲームが重い気がする」「どのくらいFPSが出ているのか知りたい」といったとき、巨大な GameManager の一部にFPS計測ロジックをねじ込んでしまうと、あとで分離するのが大変です。
そこでこの記事では、「FPS表示だけ」を責務に持つ小さなコンポーネントとして、画面隅に現在のフレームレートを表示する FPSCounter を作っていきます。どのシーンにも簡単に追加できて、プレイヤーや敵のロジックとは完全に独立した「デバッグ用UI」として使えるようにしておきましょう。
【Unity】軽量デバッグHUDで快適計測!「FPSCounter」コンポーネント
今回作る FPSCounter は、
- 一定間隔でFPSを計測して
- UI Text(TextMeshPro)に表示し
- 画面の好きな隅に固定表示できる
という、小さなデバッグ専用コンポーネントです。
フルコード(Unity6 / C#)
using UnityEngine;
using TMPro; // TextMeshPro を使う場合
/// <summary>
/// 現在のFPS(フレームレート)を画面隅に表示するコンポーネント。
/// ・TextMeshProUGUI を使った軽量なデバッグ用HUD
/// ・Update に全部書かず、「FPS表示」という責務だけを担当
/// </summary>
[DisallowMultipleComponent]
public class FPSCounter : MonoBehaviour
{
// ==== 参照設定 ====
[Header("表示先")]
[Tooltip("FPSを表示する TextMeshProUGUI コンポーネント")]
[SerializeField] private TextMeshProUGUI fpsLabel;
// ==== 表示設定 ====
[Header("更新間隔")]
[Tooltip("FPSを何秒ごとに更新するか(例: 0.5秒ごとに計測)")]
[SerializeField] private float updateInterval = 0.5f;
[Header("小数点以下の桁数")]
[Tooltip("FPS表示の小数点以下の桁数(0 にすると整数表示)")]
[SerializeField] private int decimalPlaces = 0;
[Header("色分けしきい値")]
[Tooltip("このFPS以上なら goodColor で表示")]
[SerializeField] private float goodFpsThreshold = 55f;
[Tooltip("このFPS未満なら badColor で表示(その間は warnColor)")]
[SerializeField] private float badFpsThreshold = 30f;
[Header("表示色")]
[SerializeField] private Color goodColor = Color.green;
[SerializeField] private Color warnColor = Color.yellow;
[SerializeField] private Color badColor = Color.red;
[Header("ラベル設定")]
[Tooltip("ラベルのプレフィックス(例: \"FPS:\") 空文字なら数値のみ表示")]
[SerializeField] private string prefix = "FPS: ";
// ==== 内部状態 ====
private float timeAccumulator = 0f; // 経過時間の蓄積
private int frameCount = 0; // 経過フレーム数
private float currentFps = 0f; // 直近で計測したFPS
private void Reset()
{
// コンポーネント追加時に、同じGameObject上の TextMeshProUGUI を自動取得しておく
if (fpsLabel == null)
{
fpsLabel = GetComponent();
}
}
private void Awake()
{
// fpsLabel が未設定の場合は、同じ GameObject から探す
if (fpsLabel == null)
{
fpsLabel = GetComponent();
}
if (fpsLabel == null)
{
Debug.LogWarning(
"[FPSCounter] TextMeshProUGUI が割り当てられていません。" +
"Canvas 上の TextMeshProUGUI をアタッチしてください。",
this
);
}
// updateInterval が 0 以下にならないようにガード
if (updateInterval <= 0f)
{
updateInterval = 0.5f;
}
// 小数点以下の桁数も最低0に制限
if (decimalPlaces < 0)
{
decimalPlaces = 0;
}
}
private void Update()
{
// FPS計測用の蓄積処理
timeAccumulator += Time.unscaledDeltaTime; // TimeScale の影響を受けないよう unscaled を使用
frameCount++;
// 一定時間ごとにFPSを更新
if (timeAccumulator >= updateInterval)
{
// 平均FPS = 経過フレーム数 / 経過時間
currentFps = frameCount / timeAccumulator;
// 次の計測に向けてリセット
timeAccumulator = 0f;
frameCount = 0;
// ラベル更新
UpdateLabel();
}
}
/// <summary>
/// FPS表示用ラベルのテキストと色を更新する。
/// </summary>
private void UpdateLabel()
{
if (fpsLabel == null)
{
return;
}
// 小数点以下の桁数に応じてフォーマット
string format = decimalPlaces > 0
? "F" + decimalPlaces.ToString()
: "F0";
string fpsText = currentFps.ToString(format);
// プレフィックス + FPS値 の形で表示
fpsLabel.text = prefix + fpsText;
// FPSに応じて色分け
if (currentFps >= goodFpsThreshold)
{
fpsLabel.color = goodColor;
}
else if (currentFps < badFpsThreshold)
{
fpsLabel.color = badColor;
}
else
{
fpsLabel.color = warnColor;
}
}
/// <summary>
/// 現在のFPS値を取得したい場合に外部から参照できるプロパティ。
/// (例: 他のデバッグUIと連携したいときなど)
/// </summary>
public float CurrentFps => currentFps;
}
使い方の手順
ここでは、画面右上にFPSを表示するデバッグラベルを例に、セットアップ手順①〜④を説明します。プレイヤーや敵のオブジェクトには一切触らず、「デバッグ用Canvas」にだけ追加していきます。
① Canvas と TextMeshPro の準備
- メニューから
GameObject > UI > Canvasを作成します(既にある場合はそれを利用)。 - 同じくメニューから
GameObject > UI > Text - TextMeshProを作成し、Canvas の子にします。
初回は TextMeshPro のインポートダイアログが出るので、指示に従って導入しておきましょう。 - 作成された Text オブジェクトの名前を
FPSLabelなど、分かりやすい名前に変更します。
② アンカーを画面隅に固定する
FPSLabelを選択し、RectTransformの「Anchor Presets」を開きます。- 右上に固定したい場合は、Alt+Shift を押しながら「右上」のプリセットをクリックします。
(左上・右下・左下にしたい場合は、それぞれの隅のプリセットを選びます) - Position の X / Y を調整して、画面の端から少し内側(例: X = -10, Y = -10)に配置します。
- フォントサイズ・色・フォントアセットなども、見やすいように調整しておきましょう。
③ FPSCounter コンポーネントを追加する
FPSLabelオブジェクトを選択した状態で、Add Componentをクリック。FPSCounterを検索し、追加します。- インスペクター上で、
Fps Labelフィールドに自分自身のTextMeshProUGUIをドラッグ&ドロップします。
(Reset()で自動取得も試みますが、明示的に設定しておくと安心です) - 更新間隔(
Update Interval)や、色分けのしきい値(Good Fps Threshold/Bad Fps Threshold)を好みに合わせて調整します。
④ 実行してFPSを確認する
- Playボタンを押すと、設定した画面隅に「FPS: 60」などのラベルが表示されます。
- ゲーム内でオブジェクト数を増やしたり、ポストエフェクトをオン・オフしてみて、FPSの変化を確認してみましょう。
- ビルド後の実機でも同じラベルが表示されるので、PCとモバイルのパフォーマンス差を簡単に比較できます。
このように、「FPS表示」はあくまで UI専用の小さなコンポーネントとして分離しておくと、プレイヤーの移動ロジックや敵AIとは完全に独立して管理できます。
メリットと応用
FPSCounter をコンポーネントとして独立させることで、以下のようなメリットがあります。
- どのシーンにも簡単に再利用できる
一度プレハブ化しておけば、シーンにドラッグ&ドロップするだけでFPS表示が有効になります。
巨大なGameManagerに手を入れる必要がないので、安全に追加・削除できます。 - レベルデザイン時のパフォーマンス計測が楽になる
ステージに敵やエフェクトを追加しながら、「この辺りでFPSが落ちるな」といった感覚をリアルタイムに確認できます。
重くなりそうなギミック(大量のパーティクル、動く床、NavMeshAgent を使う敵など)を配置するときの指標になります。 - 責務が明確でテストしやすい
「FPS表示だけ」を担当しているので、バグが出たときも原因の切り分けが簡単です。
例えば、「FPSラベルが表示されない」問題は、このコンポーネントかUI設定を見ればよく、ゲームロジック側を疑う必要がありません。
応用として、一定FPSを下回ったときだけ警告を出すといった使い方もできます。例えば、下記のような「警告ログを出す」メソッドを追加すれば、ビルド後のログから重い場面を特定しやすくなります。
/// <summary>
/// FPSがしきい値を下回ったときに、ログで警告を出す例。
/// (UpdateLabel() の最後などから呼び出して使う想定)
/// </summary>
private void LogLowFpsWarning(float threshold)
{
if (currentFps < threshold)
{
Debug.LogWarning(
$"[FPSCounter] FPS が {threshold} を下回りました: {currentFps:F1}",
this
);
}
}
このように、小さなコンポーネントを積み重ねていくと、デバッグ機能やユーティリティ系の機能を安全に追加・削除できるようになります。
FPS表示も「巨大なマネージャーの一部」ではなく、独立した FPSCounter として育てていきましょう。




