UnityでIOS/Androidアプリを開発していたら、ユーザーからこんなクレームがきました。
・10分ほどプレイしていたら強制終了した!手に入れたアイテムどうしてくれる!
・画面をスクロールしたらカクカクして重い!
・ロードが1分以上かかってなかなかゲームが始まらないよ
そこで、パフォーマンスのチューニング(主にCPU,メモリ)をする必要が出てきました。今回の記事は、ゲーム開発するうえで参考になったものを記載します。
ゲームエンジニア養成読本より
ゲームエンジニア養成読本という本を見つけた。
その中で参考になったところのメモ
ゲーム設計において
1ミリ秒でも早く
ゲームシステムの場合、1ミリ秒でも処理が軽くなるように実装すること。これは、ゲーム以外のシステムは1ミリ秒の遅さは誤差だが、ゲームになると1ミリ秒の遅れが積もれば山になり、CPUのパフォーマンスの低下につながる。
たとえば、弾幕系のシューティングゲームです。弾幕系のゲームは画面いっぱいに弾が広がり、弾それぞれがアニメーションしたり座標移動して重い処理をしている。このようなときに、無駄な処理をしていたら遅くなってしまうのである。
UnityでいればUpdate関数のように1フレームごとに呼ばれる関数内で、重い処理(1万以上の大量のオブジェクト処理、ファイル読み込み処理など)を書かないようにする。
特に検索系の処理は、できるかぎり時間短くする工夫が必要。線形探索で時間がかかるようであれば、二分探索のようなアルゴリズムを使う。
Unityの関数にFindがあるが、処理速度が遅い。Findは検索系のためできる限り使わない。
実際、クレームが多い部分を調べてみたらFind系の処理が多いことがわかった。
Findしなくても結果を予めキャッシュできるものは、キャッシュするなどで対処したら改善した。
何フレームで動くか決めること
何フレームで動くアプリか決めること。
大体のアプリは、30f,60fで動くことが多い。
1f以内に終わらない処理は、別スレッド、もしくは非同期処理で対応する。
処理はジョブ単位に分ける
1フレームの処理は、「ジョブ1 ジョブ2 ジョブ3」とジョブの連続で動くようにする。
ジョブとは以下のようなものを示す
・プレイヤーからの入力処理
・プレイヤーの移動
・プレイヤーのアニメーション
・テクスチャーなどの更新
ファイル読み込み、通信処理のように1フレーム内で終わらない処理は、ジョブを書くスレッドとは、別スレッド、もしくは非同期に処理をさせる。
その他のノウハウ
ゲーム開発初期にやること
画面の上などにfps,メモリ容量を常に可視化できるようにしておく。
Unityではプロファイラーをつなげれば、CPU負荷、使用メモリ、fpsなど確認できるが、プロファイラーを有効にしてビルドするなど意外に面倒な作業があるため、つい忘れがちである。
だからこそ、パフォーマンスを常に確認できるようにfps,メモリ容量などを常に画面に表示しておく。
これはゲーム開発に入るときから、やっておくこと。
スクリプトの最適化
https://docs.unity3d.com/ja/current/Manual/MobileOptimizationPracticalScriptingOptimizations.html
最適化
https://docs.unity3d.com/jp/560/Manual/MobileOptimisation.html
Unityのメモリ管理で参考になる資料・ツールなど
■Unityのメモリ管理について
以下の資料はUnityがどのようにメモリ管理してあるか書いてあるマニュアルです。
趣味でカジュアルゲームを作るときは、メモリを意識しなくても、アプリを作れることが多い。スクエニなどの大手ゲーム会社が作る大規模アプリになってきたら、メモリを意識しないと強制終了するなど大きな不具合に繋がってしまう。
自動メモリ管理を理解する
https://docs.unity3d.com/jp/current/Manual/UnderstandingAutomaticMemoryManagement.html
■その他Unityで開発する上で便利なデバッグツール
・MainLoopProfilingSamplez
Unity 2018.1から導入された PlayerLoopを利用して実機上での簡易プロファイリングツールです。スクリプトやアニメーションなどの処理にどのくらいの時間を使っているか視覚的にわかります。
https://github.com/wotakuro/MainLoopProfilingSample
・MemoryProfiler
メモリの確保状況がより視覚的に見やすくなります。
https://bitbucket.org/Unity-Technologies/memoryprofiler
■端末からの物理メモリを取得するサンプルコード
IOS/Android端末で使用されているメモリ容量を取得するサンプルコードです。
Unityの関数でメモリ容量を取ることも可能だが、プロファイラーをONにしていないと取れなかったりと、意外に面倒なので、プロファイラーを使わなくても端末側(ネイティブ側)から取得できる処理がオススメです。
標準Profilerだけに頼らないUnityのプロファイリングに挑んでみる
http://klabgames.tech.blog.jp.klab.com/archives/1046552735.html
まとめ
以上、CPU・メモリ最適化のメモでした。
CPU・メモリ最適化は、最初は意識しなくてもアプリを作れてしまいますが、ある程度、開発が進んだり、コンテンツを追加していくと、必ずといって良いほどパフォーマンスの壁にぶつかります。
開発の初期のうちに、CPU負荷、メモリ容量を見えるようにしておきましょう、という話しでした。