概要
PCやコンソール向けのゲーム開発と同じ感覚でモバイルゲームを開発すると、多くの場合、深刻なパフォーマンス問題に直面します。モバイルデバイスは、PCに比べてCPUやGPUの性能が低く、メモリ容量も限られています。さらに、熱 (Thermal Throttling) とバッテリー消費という、PC開発ではあまり意識しない独自の制約も抱えています。
高い負荷がかかり続けると、デバイスは熱暴走を防ぐために強制的にCPU/GPUのクロック周波数を下げ(サーマルスロ ットリング)、パフォーマンスがガクンと落ちてしまいます。また、無駄な処理はバッテリーを急速に消耗させ、プレイヤーの満足度を低下させます。
快適なモバイルゲーム体験を提供するためには、開発の初期段階からモバイル特有の制約を意識した最適化が不可欠です。この記事では、モバイルゲーム開発における基本的な最適化のポイントを解説します。
1. レンダリング負荷の削減 (GPU)
GPU負荷は、バッテリー消費に最も大きな影響を与える要素の一つです。描画処理をいかに軽くするかが鍵となります。
ドローコールの削減
ドローコール(描画命令)は、CPUがGPUに対して「このメッシュを、このマテリアルで、この位置に描画して」と指示する命令です。この命令自体がCPUにとってコストであり、ドローコールが多すぎるとCPUがボトルネックとなり、GPUが遊んでしまう状態になります。
- バッチング (Batching): 同じマテリアルを使用している複数のメッシュを、一つのドローコールにまとめて処理する技術です。
Project Settings > Player > Other SettingsでStatic BatchingとDynamic Batchingを有効にしましょう。静的なオブジェクトはStaticに設定し、動的なオブジェクトでも頂点数が少ないものは自動でバッチングされます。 - テクスチャアトラス (Texture Atlas): 複数のテクスチャを一枚の大きなテクスチャにまとめる手法です。こ れにより、異なるオブジェクトでも同じマテリアルを共有できるようになり、バッチングが効きやすくなります。
- SRP Batcher: URP (Universal Render Pipeline) や HDRP を使用している場合、
SRP Batcherを有効にすることで、マテリアルのプロパティが異なっていても、同じシェーダーであればバッチングが効くようになり、ドローコールを劇的に削減できます。
ポリゴン数とオーバードローの管理
- ポリゴン数 (Polygon Count): モバイルデバイスで一度に表示するポリゴン数は、数万〜数十万程度に抑えるのが一般的です。LOD (Level of Detail) を活用し、カメラから遠いオブジェクトはより単純なモデルに切り替えるようにしましょう。
- オーバードロー (Overdraw): 同じピクセルをフレーム内で何度も塗りつぶしてしまうことです。特に、半透明のエフェクト(パーティクルなど)が重なると発生しやすく、GPUに大きな負荷をかけます。
Sceneビューの描画モードをOverdrawに切り替えて、画面が真っ赤になっている箇所を特定し、不要なエフェクトを削減したり、不透明な部分が多いテクスチャを使ったりするなどの対策を取りましょう。
2. CPU負荷の削減
CPUは、ゲームロジック、物理演算、アニメーション、そしてGPUへの描画命令など、多くの処理を担当します。
物理演算の最適化
FixedUpdate内で実行される物理演算 は、CPU負荷の大きな原因となり得ます。Project Settings > TimeのFixed Timestepの値を大きくする(例:0.02->0.033)と、物理演算の更新頻度が下がり、負荷が軽減されますが、動きがカクついて見える可能性もあります。- 不要な
RigidbodyやColliderは削除しましょう。特に、動かない背景オブジェクトにRigidbodyが付いているのは無駄です。 Project Settings > Physics(またはPhysics 2D) のLayer Collision Matrixを編集し、互いに衝突する必要のないレイヤー同士の判定をオフにしましょう。
C#コードの最適化
- GCアロケーションの削減:
Update内での文字列結合、newによるクラスインスタンス化、LINQの使用などを避け、ガベージコレクションの発生頻度を抑えましょう。オブジェクトプーリングは必須のテクニックです。(詳細は「ガベージコレクション最適化」の記事を参照) GetComponentのキャッシュ:Update内でGetComponentを呼び出すのは非常に高コストです。StartやAwakeで一度だけ呼び出し、結果を変数にキャッシュしておきましょう。
3. メモリ管理
メモリ使用量が多いと、OSによってアプリが強制終了させられる原因となります。
- テクスチャの最適化: メモリ使用量の大部分を占めるのがテクスチャです。テクスチャインポータで、プラットフォームごとに適切な最大解像度(
Max Size)と圧縮形式(Format)を設定しましょう。ASTCやETC2は、多くのモダンなデバイスでサポートされている効率的な圧縮形式です。 - オーディオの最適化: BGMなどの長いオーディオクリップは、
Load TypeをStreamingに設定すると、メモリを圧迫せずに再生できます。SEはDecompress On Load、頻繁に使う短い音はCompressed In Memoryが良いでしょう。 - Addressablesの活用:
Resourcesフォルダを使わず、Addressable Asset Systemを導入して、必要なアセットを必要な時にだけ動的にロード・アンロードするようにしましょう。
4. モバイル特有の対応
- タッチ入力:
Input.GetMouseButtonDown(0)はタッチ入力にも反応しますが、マルチタッチやジェスチャーに対応するにはInput.GetTouch()を使います。より高度な制御には、新しいInput Systemパッケージの導入を検討しましょう。 - セーフエリア対応: iPhone X以降のノッチ(切り欠き)や、Androidのパンチホールなど、画面の表示が欠ける領域を避けてUIを配置する必要があります。
Screen.safeAreaプロパティを使って、UIのルートCanvasの範囲を調整します。
まとめ
モバイル最適化は、単一の解決策があるわけではなく、地道な改善の積み重ねです。
- レンダリング: ドローコールを削減し(バッチング、SRP Batcher)、ポリゴン数を抑え(LOD)、オーバードローを避ける。
- CPU: 物理演算の負荷を軽減し、GCアロケーションを徹底的に削減する。
- メモリ: テクスチャとオーディオの圧縮・ロード設定を最適化し、Addressablesで動的なメモリ管理を行う。
開発の早い段階からProfilerを使い、実機でパフォーマンスを計測しながら、これらの最適化を継続的に行っていくことが、成功するモバイルゲーム開発の鍵となります。