unity-performance について
Unity ゲーム開発者向けに、性能の計測と最適化を解説するリファレンスです。CPU、GPU、メモリ、描画、プラットフォーム別の対策を、計測と比較を繰り返す手順とともに扱います。
主な機能
- 01Unity のプロファイリング手順
- 02CPU とガベージコレクションの最適化
- 03GPU と描画の最適化手法
- 04メモリとアセット読み込みのガイド
- 05プラットフォーム別の性能上の注意
活用シーン
- 01対象デバイスでフレーム時間のボトルネックを調査
- 02ゲーム中の割り当てやオブジェクト生成を削減
- 03複雑なシーンの描画を最適化
- 04各プラットフォームのメモリとアセット読み込みを管理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ josiahsiegel/ claude-plugin-marketplace --skill unity-performanceインストール後、Claude Code で/unity-performanceを入力して読み込まれたか確認します。
詳細
Unity のパフォーマンス最適化
概要
Unity ゲームのプロファイリングと最適化を体系的に行う方法です。プロファイリングツール、CPU/GPU の最適化、メモリ管理、レンダリングの最適化、プラットフォーム固有の考慮事項を扱います。
プロファイリングツール
| ツール | 表示される内容 | 使用する場面 |
|---|---|---|
| Unity Profiler | フレームごとの CPU、GPU、メモリ、オーディオ、物理演算 | パフォーマンスの問題が発生した際の最初の調査 |
| Frame Debugger | ドローコールの内訳、シェーダー/マテリアルの状態 | レンダリングのボトルネック |
| Memory Profiler | ヒープスナップショット、テクスチャ/メッシュのメモリ使用量 | メモリリーク、メモリの肥大化 |
| Profile Analyzer | キャプチャの比較、統計分析 | 最適化の前後の比較 |
| Physics Debugger | コライダーの可視化、接触点 | 物理演算のパフォーマンス |
Profiler のワークフロー
- Development Build と Autoconnect Profiler を有効にしてビルドします
- 対象デバイス上でプロファイリングします(Editor 上ではなく、Editor のオーバーヘッドによって結果が歪むため)
- ボトルネックのカテゴリを特定します:CPU バウンド、GPU バウンド、またはメモリ不足
- 問題の原因となっているシステムを詳しく調べます
- 最適化し、再度プロファイリングして比較します
Profiler の読み方
If frame time > 16.6ms (60 FPS target):
CPU timeline > GPU timeline -> CPU-bound
GPU timeline > CPU timeline -> GPU-bound
GC.Alloc column shows per-frame allocations -> GC pressure
スパイク(単発の遅いフレーム)と、高い時間が継続する状態(ベースラインが重すぎる状態)を確認します。
CPU の最適化
フレームごとの割り当て(GC)を減らす
| アンチパターン | 修正方法 |
|---|---|
Update 内での string + string | StringBuilder を使用するか、キャッシュします |
フレームごとの new List<T>() | 一度だけ割り当て、Clear() して再利用します |
| ホットパスでの LINQ | 手動のループに置き換えます |
フレームごとの GetComponent<T>() | Awake/Start でキャッシュします |
フレームごとの GameObject.Find() | 参照をキャッシュするか、イベントを使用します |
ジェネリックではないコレクションに対する foreach | for ループまたはジェネリックコレクションを使用します |
SendMessage() / BroadcastMessage() | 直接呼び出し、イベント、またはインターフェースを使用します |
| 値型のボックス化 | ジェネリックコレクションを使用し、object へのキャストを避けます |
オブジェクトプーリング
public class ObjectPool<T> where T : Component
{
readonly Queue<T> _pool = new();
readonly T _prefab;
readonly Transform _parent;
public ObjectPool(T prefab, int preWarm, Transform parent = null)
{
_prefab = prefab;
_parent = parent;
for (int i = 0; i < preWarm; i++)
_pool.Enqueue(CreateInstance());
}
public T Get(Vector3 position, Quaternion rotation)
{
var obj = _pool.Count > 0 ? _pool.Dequeue() : CreateInstance();
obj.transform.SetPositionAndRotation(position, rotation);
obj.gameObject.SetActive(true);
return obj;
}
public void Return(T obj)
{
obj.gameObject.SetActive(false);
_pool.Enqueue(obj);
}
T CreateInstance()
{
var obj = Object.Instantiate(_prefab, _parent);
obj.gameObject.SetActive(false);
return obj;
}
}
頻繁に生成/破棄される弾丸、パーティクル、敵、UI 要素など、あらゆるものをプールします。Unity 2021+ には UnityEngine.Pool.ObjectPool<T> も組み込まれています。
Update の最適化
| 手法 | 説明 |
|---|---|
| 更新を分散する | すべての AI を毎フレーム更新せず、ティックグループを使用します |
| 距離ベースの LOD | 遠くのオブジェクトの更新頻度を下げます |
| イベント駆動 | 可能な場合はポーリングをイベントに置き換えます |
| 未使用のスクリプトを無効にする | 画面外のコンポーネントで enabled = false にします |
InvokeRepeating を使用する | 定期的なチェックに使用します(コルーチンで待機するより低コストです) |
GPU/レンダリングの最適化
ドローコールを減らす
| 手法 | 方法 | 削減効果 |
|---|---|---|
| Static Batching | 移動しないオブジェクトを Static に設定します | ビルド時にメッシュを結合します |
| Dynamic Batching | 小さなメッシュ(<300 verts)に対して自動で行われます | URP/Built-in のみ |
| GPU Instancing | 繰り返し表示するオブジェクトのマテリアルで有効にします | 木、草、岩 |
| SRP Batcher | URP/HDRP でデフォルトで有効です | SetPass 呼び出しを減らします |
| テクスチャアトラス化 | テクスチャをアトラスにまとめます | マテリアルの切り替えを減らします |
| メッシュの結合 | 実行時に CombineMeshes() を使用します | カスタムバッチング |
LOD(詳細度)
LOD Group Setup:
LOD 0 (0-30%): Full-detail mesh (5000 tris)
LOD 1 (30-60%): Medium mesh (2000 tris)
LOD 2 (60-90%): Low mesh (500 tris)
Culled (90%+): Not rendered
メッシュに LOD を使用するだけでなく、遠距離ではスクリプトの複雑さ、パーティクル数、物理演算も減らします。
オクルージョンカリング
屋内や複雑なシーンではオクルージョンデータをベイクします。壁や床など、大きな静的オクルーダーを設定します。シーンのスケールに合わせてセルサイズを設定します。Occlusion Culling ウィンドウを使用して可視化し、テストします。
シェーダーの最適化
| 問題 | 解決策 |
|---|---|
| 複雑なフラグメントシェーダー | テクスチャサンプルを減らし、計算を簡略化します |
| オーバードロー | 透明オブジェクトを最小限にし、可能な場合は不透明にします |
| バリアントが多すぎる | ビルド設定で未使用のシェーダーバリアントを除外します |
| 高コストなポストプロセッシング | モバイルではエフェクトを無効にし、より低コストな代替手段を使用します |
メモリ管理
メモリに関するよくある問題
| 問題 | 症状 | 対処方法 |
|---|---|---|
| テクスチャの肥大化 | メモリ使用量が多く、読み込みに時間がかかる | テクスチャを圧縮し、プラットフォームごとに最大サイズを小さくします |
| アンロード済みシーンが参照を保持 | 時間とともにメモリ使用量が増加する | シーン遷移後に Resources.UnloadUnusedAssets() を使用します |
| Addressables が解放されない | バンドルがメモリに残る | Addressables.Release(handle) を呼び出します |
| オーディオクリップが非圧縮 | メモリ使用量が非常に大きい | 音楽はメモリ上で圧縮し、効果音は読み込み時に展開します |
| メッシュの読み取り/書き込みが有効 | メッシュごとにメモリ使用量が倍増する | 実行時に不要であれば Read/Write を無効にします |
プラットフォームごとのテクスチャ圧縮
| プラットフォーム | 形式 | 備考 |
|---|---|---|
| PC/Console | BC7 (DXT) | 品質とサイズの比率が最適です |
| Android | ASTC 6x6 | 汎用的で、品質を調整できます |
| iOS | ASTC 6x6 | Android と同じです |
| WebGL | ETC2 / DXT | 対象 GPU によって異なります |
テクスチャのインポート設定で「Override for [Platform]」を使用します。最大テクスチャサイズは必要最小限に設定します(UI アイコンは 512、プロップは 1024、ヒーローアセットは 2048)。
プラットフォーム固有の考慮事項
| プラットフォーム | 主な制約 |
|---|---|
| Mobile | サーマルスロットリング、限られたメモリ、バッテリー消費、フィルレートの制限 |
| WebGL | スレッド非対応(Unity 6 より前)、ダウンロードサイズが大きい、コンピュートシェーダー非対応 |
| Console | 認定要件、固定されたハードウェア、メモリ予算 |
| VR/XR | 最低 72~90 FPS、ステレオレンダリングの負荷、フレームレート低下による乗り物酔い |
Addressables とアセットの読み込み
読み込み時のカクつきを避けるため、非同期アセット読み込みには Addressables を使用します。
// Preload during loading screen
var handle = Addressables.LoadAssetAsync<GameObject>("enemy_boss");
await handle;
// Release when done
Addressables.Release(handle);
Addressable グループを使用して、バンドルの粒度を制御します。使用頻度の低いアセットはリモート/オンデマンドとして設定します。Addressables Event Viewer でバンドルのメモリ使用量をプロファイリングします。
最適化のクイックチェックリスト
- Editor ではなく、対象デバイスでプロファイリングする
- ゲームプレイコードでフレームごとの GC 割り当てをゼロにする
- 頻繁に生成されるすべてのオブジェクトにオブジェクトプーリングを使用する
- 動かないオブジェクトでスタティックバッチングを有効にする
- 距離に応じて表示されるすべての 3D モデルに LOD グループを設定する
- プラットフォームに応じた適切な最大サイズでテクスチャを圧縮する
- 実行時に変更しないメッシュとテクスチャでは Read/Write を無効にする
- オーディオクリップに適切な圧縮設定を使用する
- 屋内や複雑なシーンではオクルージョンカリングをベイクする
- ビルド設定でシェーダーバリアントを除外する
追加リソース
参考ファイル
references/profiling-deep-dive.md-- Profiler の高度な使用方法、メモリプロファイラーのスナップショット、フレームごとの分析、カスタムプロファイラーマーカー、自動パフォーマンステスト、ビルドサイズ分析、ECS/DOTS のパフォーマンスパターン