Unityでインディゲームを作る!

Unityでのゲーム制作を目指し、それに関わる話題についてのブログ

Memory編 Unity eBook "Optimaize Your Mobile Game Performance" を読み解く

Optimize_Your_Mobile_Game_Performance

 前回(Profiling)に引き続き、"Optimaize Your Mobile Game Performance"のメモリ編について、まとめていきたいと思います。

 

あらすじ

 "Optimaize Your Mobile Game Performance"はUnity公式が発行している無料のeBookでモバイルゲームを最適化するための知識やテクニックをまとめた一冊です。

 各章ごとに重要だと思う個所を抜き出して、翻訳していこう!というのが本企画の趣旨となります。

 

それではメモリ編、スタートです!

 

Memory

 Unityはユーザーが書いたコードやスクリプトに対して、Automatic Memory Management(自動メモリ・マネジメント)を採用しています。小さなデータ片や値タイプのローカル変数はスタック領域に、大きなデータ片や長期的に保持されるものはManaged Heapに割り当てられます。

 

 Garbage Collector(GC)は定期的に、使われていないヒープ領域を特定して、それを開放しています。しかし、ヒープ内の全オブジェクトを自動チェックしている際に、ゲーム重くしたり、ガクガクさせたりする場合があります。

 

 メモリ利用の最適化とは、いつメモリ領域の割り当てと解放を行うのかについて意識する、そして、Garbage Collectionの影響を最小化する、ということです。

マネージド・メモリについての更なる情報

 

Memory Plofilerを使おう

Managed Heapの状態を写し取り、メモリ・リークやフラグ化の問題がどこで起こっているかの特定に役立ちます。Memory Plofilerの公式ドキュメントを読もう。

※Unity 2022.2より正式版が使用可能

 

Garbage Collectionの衝撃を減らす

 UnityのGarbage Collectorは実行されているプログラムを止め、自分の仕事が終わった後にそれを再開させます。

 

 必要のない、特定のheap領域割り当てに注意しましょう。それらはGCスパイクを引き起こします。

 

・不必要な文字列(string)の生成や処理を減らそう。JSONXMLといった文字列を読み込むタイプのデータ形式を避け、ScriptableObjectでデータを保存しよう。

・いくつかの関数呼び出しはHeap Allocationを引き起こすことに注意しよう。配列はあらかじめ、初期化して参照をキャッシュしておこう。ループ文の途中で宣言するとかは止めよう。

・Garbageを出さない特定の関数の利点を活用しよう。GameObject.tagは文字列を生成し、GCが動いてしまうので、GameObject.CompareTagを使う、というように。

・値タイプの変数を参照タイプの変数(オブジェクト)に渡すのはやめよう。これは一時的なオブジェクトを生成し、それは潜在的なGarbageになってしまう。

・コルーチンにおいて、yieldはゴミを出さないが、newキーワードでWaitForSecondsを生成すると発生する。WaitForSecondsを(Start関数などで)キャッシュしておいて、再利用するのが良い。

・パフォーマンスに問題がある場合、LINQ正規表現を避ける。

 

可能なら、Garbage Collectionのタイミングを決める

 Garbage Collectionがゲームをフリーズさせない特定のポイントが明確に分かっているならば、System.GC.CollectでGarbage Collectionを手動で発動できます。

 

Incremental Garbage Collectorを使おう

 一回の長い中断を引き起こす単一のGCとは違い、複数のフレームに渡って作業負荷を分散し、中断時間をより短くできます。

 

 もし、Garbage Collectionがパフォーマンスに影響を与えている場合、Incremental GCのオプションを有効にしてみて、GCスパイクの問題を劇的に減らせるかどうかを試しましょう。Profile Analyzerでその恩恵を確認してください。

(Unity 2021以降ではデフォルトでIncremental GCがオンになっているはずです)

 


 

メモリ編は以上です。細かいですが、役に立っている情報がかなりありました。

 OnTriggerEnterなどで、tagを使ってプレイヤーかどうかをチェックするのに、player.tag == "Player"という風にtagを直接比較していたんですが、これが文字列を無駄に生成する、ということで、全部CompareTagで行うことにしました。

 最適化に神経質になるな!とはよく言われることですが、こういうちょっと気をつけておけば、間違いなく動作が良くなるというテクニックはどんどん使っていきたいですね!

 

次回はAdaptive Performance編です!