Unity Profiler 入門:GetComponent / Debug.Log の計測と最新の実践法
Unity でパフォーマンスを意識したコーディングを行うには、Profiler を用いた計測が必須です。この記事では、以下を解説します。
- Profiler の起動と基本操作
- GetComponent を毎回呼ぶ場合とキャッシュする場合の差
- Debug.Log の重さ
- 最新の推奨 API(ProfilerMarker / ProfilerRecorder) の紹介
- Editor 実行と Player 実行の違い に関する注意点
Profiler の使い方
Profiler ウィンドウを開く
メニューから Window → Analysis → Profiler を選択します。
Profiler ウィンドウは CPU、GPU、Memory、Rendering、など複数のカテゴリを確認できます。
計測対象の明示
コード内で負荷を囲みたい場合、従来は以下のように書いていました。
using UnityEngine.Profiling;
Profiler.BeginSample("負荷テスト");
// 計測したい処理
Profiler.EndSample();
ただし、Unity 2020以降は ProfilerMarker が推奨です。
ProfilerMarker の利用
BeginSample/EndSample は書き忘れリスクやオーバーヘッドが残るため、以下のように ProfilerMarker を使います。
using UnityEngine;
using Unity.Profiling;
public class ProfileTest : MonoBehaviour
{
static readonly ProfilerMarker s_Marker = new ProfilerMarker("負荷テスト");
Renderer _renderer;
void Start()
{
_renderer = GetComponent<Renderer>(); // キャッシュ
}
void Update()
{
using (s_Marker.Auto())
{
for (int i = 0; i < 100000; i++)
{
// 計測対象コード
var tmp = _renderer.enabled;
}
}
}
}
- using (s_Marker.Auto()) によって、範囲の開始・終了が確実に処理されます。
- Release ビルドではオーバーヘッドはほぼゼロです。
GetComponent の比較
毎回呼ぶ場合
void Update()
{
for (int i = 0; i < 100000; i++)
{
var rend = GetComponent<Renderer>();
rend.enabled = true;
}
}
キャッシュする場合
Renderer _renderer;
void Start() => _renderer = GetComponent<Renderer>();
void Update()
{
for (int i = 0; i < 100000; i++)
{
_renderer.enabled = true;
}
}
Profiler で比較すると、キャッシュした方が圧倒的に軽量であることが確認できます。
なお、実務では「すべてをキャッシュ」ではなく、頻繁に呼ぶもの・ホットパスのみキャッシュで十分です。
Debug.Log のコスト
void Update()
{
for (int i = 0; i < 1000; i++)
{
Debug.Log("テスト");
}
}
- Profiler で確認すると、Debug.Log は非常に重い処理であることがわかります。
- 大量出力はフレーム落ちや GC 負荷の原因になります。
実務的な対策
- #if UNITY_EDITOR で Editor 実行時のみ有効化
- 出力をバッファリング/まとめて書き出す
- 必要なログは Analytics や独自カウンタに送る
Unity では #if UNITY_EDITOR という 条件付きコンパイルディレクティブ を使うことで、Editor 実行時のみ有効になるコードを書くことができます。
基本の使い方
using UnityEngine;
public class DebugExample : MonoBehaviour
{
void Update()
{
// Editor 上でのみログを出したい場合
#if UNITY_EDITOR
Debug.Log("Editor 実行時のみ表示されます");
#endif
// 実機ビルドでは常に動かしたい処理
transform.Rotate(Vector3.up * Time.deltaTime * 10f);
}
}
動作
- Unity Editor 内(Play 実行時) → Debug.Log が表示される
- ビルドしたアプリ(Windows, Android, iOS, etc.) → Debug.Log の部分はコンパイル対象から外れる(完全に消える)
つまり「実機に余計なログ処理が入らない」ため、パフォーマンス低下や無駄な出力を防げるわけです。
よく使うパターン
1. ログ出力を Editor 限定にする
#if UNITY_EDITOR
Debug.Log($"座標: {transform.position}");
#endif
2. エディタ拡張用のコード
#if UNITY_EDITOR
using UnityEditor; // Editor 名前空間はビルドに含められない
[CustomEditor(typeof(MyComponent))]
public class MyComponentEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
}
}
#endif
UnityEditor 名前空間はビルドするとエラーになるため、必ず #if UNITY_EDITOR … #endif で囲みます。
3. 実行環境ごとに分ける
Unity には他にも多くの条件付きシンボルがあります。
- UNITY_EDITOR : Editor 上で動作しているとき
- UNITY_STANDALONE : Windows/Mac/Linux プレイヤー
- UNITY_IOS, UNITY_ANDROID : モバイル
- DEVELOPMENT_BUILD : 「Development Build」でビルドしたとき
組み合わせれば「Editor のときだけログ」「モバイルのときだけ処理変更」といった分岐が可能です。
注意点
- #if UNITY_EDITOR で囲んだコードは ビルド後のアプリには一切含まれません
- 「開発中だけ欲しいログ」「エディタ拡張専用コード」を入れるときに便利
- 本番環境で必要な処理(例:エラーログ送信)は UNITY_EDITOR ではなく Debug.isDebugBuild や DEVELOPMENT_BUILD を使った方が安全
まとめ
- #if UNITY_EDITOR は Editor 専用コード を安全に書くための仕組み
- Debug.Log や UnityEditor 名前空間の利用に必須
- 実機ビルドには含まれないため、パフォーマンスや安全性に優れる
これを使えば、わざわざ毎回 #if UNITY_EDITOR を書かなくても済みます。
EditorLog.cs(簡易ラッパークラス)
using UnityEngine;
public static class EditorLog
{
/// <summary>
/// Editor 実行時のみログを出力します
/// </summary>
public static void Log(object message)
{
#if UNITY_EDITOR
Debug.Log(message);
#endif
}
/// <summary>
/// Editor 実行時のみ警告ログを出力します
/// </summary>
public static void LogWarning(object message)
{
#if UNITY_EDITOR
Debug.LogWarning(message);
#endif
}
/// <summary>
/// Editor 実行時のみエラーログを出力します
/// </summary>
public static void LogError(object message)
{
#if UNITY_EDITOR
Debug.LogError(message);
#endif
}
}
使い方
using UnityEngine;
public class Sample : MonoBehaviour
{
void Start()
{
// Editor でのみ表示される
EditorLog.Log("Editor でだけ出力されます");
EditorLog.LogWarning("警告です");
EditorLog.LogError("エラーです");
}
}
- Unity Editor の Play 実行 → ログが表示される
- ビルドした実機アプリ → ログ処理自体がコンパイルから除外される
利点
- #if UNITY_EDITOR を毎回書かなくていい
- コードがすっきりして読みやすくなる
- 実機ビルドには一切影響を与えない
ProfilerRecorder によるスクリプト計測
Unity 2020.2 以降、ProfilerRecorder を使って スクリプトから統計データを取得できます。これにより、Profiler ウィンドウを開かなくても HUD 表示が可能です。
using Unity.Profiling;
public class GCAllocMonitor : MonoBehaviour
{
ProfilerRecorder _gcRecorder;
void OnEnable()
{
_gcRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Memory, "GC Allocated In Frame");
}
void Update()
{
Debug.Log($"GC Alloc: {_gcRecorder.LastValue / 1024f} KB");
}
void OnDisable() => _gcRecorder.Dispose();
}
Editor 実行と Player 実行の違い
Profiler の結果は Editor 実行では正確ではない場合があります。
Editor のオーバーヘッドが「Other」などに大きく乗るため、実際より重く見えることもあります。
注意点
- 開発中の目安は Editor Profiler
- 最終的な判断は Development / Release Player ビルドで確認すること
Deep Profiling の注意
Deep Profiling は関数ごとの詳細な計測が可能ですが、極端なオーバーヘッドを生むため結果が歪みやすいです。
利用は「特定のボトルネック調査の一時的な有効化」に留めましょう。
まとめ
- Profiler を使って実際に計測して判断することが重要。
- GetComponent はキャッシュが基本。
- Debug.Log は極めて重いので乱用禁止。
- ProfilerMarker / ProfilerRecorder を使えば、最新の Unity で安全かつ効率的な計測が可能。
- Editor と Player で結果が違う点を必ず意識する。
これらを踏まえて、日常的に「なんとなくではなく数値で判断する」習慣をつけていきましょう。
ディスカッション
コメント一覧
まだ、コメントがありません