Unityで始める！負荷削減

グレンジ Advent Calendar 2017 9日目担当の、flankids と申します。
グレンジでUnityによる新規ゲームアプリ開発のクライアントエンジニアをしております。

今回は、Unityでスマートフォン向けアプリを開発する上での負荷削減についてのお話です。よろしくお願い致します！

#はじめに
スマートフォンのスペックは年々高くなっていますが、それに合わせてゲームアプリのクオリティも高いものがどんどんリリースされており、 **ビジュアルはもちろん、FPSを担保して操作感も快適なものにすることが要求されています。** （重くてカクカクなゲームはやりたくないし、かといって見た目がしょぼい3Dモデルやエフェクトのものもちょっと・・・）

 **高いクオリティのクリエイティブを使って、贅沢な画面表現を使いつつ、動作は軽くしたい！** 

そこで、

- 負荷削減にあたって、どこから決めればいいのか？
- 測定はどうやって実施して、何を見ればいいのか？
- 動作が重いときは、何をしたらいいのか？

という観点で、「負荷に気を使った開発」についてまとめました。

 **前提　：Unityによるスマートフォン向け3Dゲームの開発** 
 **ゴール：負荷削減を迷わず無駄なく実施！** 

# 端末選定
はじめに、開発するゲームの **動作を保証する最低スペックのスマートフォン** を選びます。
iOSと、Androidでそれぞれ具体的な型番を決められると良いでしょう。

選ぶ基準はいろいろありますが、 **何年前くらいの端末までサポートするか** で大まかに決めて、そこからその時代の代表的なAndroid端末をチョイスする※ ことを推奨します。
※iOS端末は基本的に同世代Android端末よりスペックが高いので、まずAndroid端末を選定しましょう

例えば、アプリのリリース時期を2017年を予定していて、「携帯の機種変更のスパンは長くても2～3年」と仮定し、そこから逆算して3年前くらいの端末を選ぶ場合、代表的な機種は下記のものになります。

- Xperia Z2 SO-03F
- Xperia ZL2 SOL25
- ARROWS NX F-02G
- AQUOS ZETA SH-04F
- AQUOS ZETA SH-01G
- Xperia Z4(Z3+) SO-03G

「スマートフォン ○年 モデル」と検索すると、その時期リリースされた端末の情報が見つかると思うので、そこから探っていきましょう。

▼参考
・[2014年冬春モデル登場！Androidスマホ全機種をチェック](https://andronavi.com/2014/10/334946)
・[ドコモ、2014年冬～2015年春モデルを発表](https://k-tai.watch.impress.co.jp/docs/news/668915.html)

## スペック情報、比較
 **iOS端末は選択したAndroidの最低保障スペック端末と同程度の機種を選ぶ** と良いでしょう。
比較基準はさまざまですが、大まかな比較は **Geekbench** のようなベンチマークアプリによるスコアで比較し、 **詳細はCPU、GPU、RAMで比べる** と良いと思われます。

Geekbench（Android）：http://browser.geekbench.com/android-benchmarks/
Geekbench（iOS）：http://browser.geekbench.com/ios-benchmarks/

<img width="687" alt="score.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/184294/29f767a9-185d-88f1-2e64-7002e26c1a42.jpeg">

例えば、Xperia Z2を最低保障スペックとした場合、スコアが一番近い **iPhone5S** あたりがiOSの最低保障スペックとなると思われます

CPU、GPU、RAMの詳細は各機種の公式ページから確認できると思われますが、 [HYPERでんち](http://dench.flatlib.jp/opengl/devicelist) に、非常に細かにまとまっているので、こちらの情報がオススメです。

## TIPS・端末のシェア率
<img width="754" alt="MOBILE_HARDWARE_STATS.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/184294/2872ccd2-4de6-2b78-f723-342fbd52d67a.jpeg">

Unityが公開している [MOBILE HARDWARE STATS](https://hwstats.unity3d.com/mobile/device.html) という情報がありますが、これは世界的な統計なので、日本中心にリリースするアプリでこの情報を参考にするのはベターではなさそうです。
国内では、Xperiaが圧倒的に多く、その次にGALAXY、AQUOS、ちらほらとNexus、というようなイメージです。ご参考まで。
参考：https://webrage.jp/techblog/sp_share/

# 負荷測定
端末が決まったら、実際にその端末でどのくらいのFPSになるかゲームを動かして確認してみましょう。（[FPS表示方法の参考](http://tasogare-games.hatenablog.jp/entry/20150609/1433779228)。実機で動作させるため、Statisticsは使用できないので注意）
ゲームを通して目標FPSを満たしていたら、 **万々歳！** もう少しクリエイティブや表現をリッチにしてみてもいいかもしれませんね！

　
　
　
　
　
　

<div align="center"><img width="342" alt="fps.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/184294/82657f78-54e5-7ae7-43bf-0e6812d1bcb4.jpeg"></div>


…と、そう上手くいけば、この記事にはたどり着かないと思うので、順に改善していきましょう。

## 負荷の原因のジャンル分け
負荷は、大きく分けて **CPU負荷** と **GPU負荷** の２つに分けられます。

 **▼CPU負荷** 
主にスクリプトの実行にかかる負荷。
不要な処理を消したり、アルゴリズムや設計を改善して重い処理が呼ばれる回数を減らすことで改善できます。

 **▼GPU負荷** 
主に描画の実行にかかる負荷。
表示するオブジェクトの数を減らしたり、3Dモデルのポリゴン数や、画像の解像度を減らしたり、ポストエフェクトやシェーダーを計算効率の良いものにすることで改善できます。

## 負荷ジャンルの特定

Xcodeでの測定を推奨します。細かな実施方法は、[こちらの記事](http://tsubakit1.hateblo.jp/entry/2014/08/03/050628) がオススメです。
（必ずiOS端末をつないで実行してください）

ゲームを動作してみて、動作が重くなるところで Frame Time をチェックしましょう。

![frame_time.jpg](https://qiita-image-store.s3.amazonaws.com/0/184294/ead6bade-4f43-c62c-9a22-c24d56a53edf.jpeg)

- CPU：27.4 ms
- GPU：13.5 ms

これは、それぞれ **1フレームあたりの処理にかけた時間** です。
例えば、このような結果になっていた場合、描画回りの処理は60FPSは出せるポテンシャルがあるにも関わらず、CPU負荷が原因で30FPS程度しか出せないということになります。（FPS = 1 / FrameTime [s]）

ここから60FPSを目指したい場合は、CPU負荷を減らす必要があり、逆に30FPSで充分ならGPU負荷に余裕があるため、表現のクオリティアップを試みるのも良いかもしれません。

# 負荷削減
では、Xcodeの結果から、CPU負荷、GPU負荷のどちらが原因か（あるいは両方か？）が分かったので、実際に負荷の原因を追って潰していきましょう。

## CPU負荷削減

<img width="800" alt="profiler.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/184294/7556b22b-037b-2903-7f0c-4fe80acbbcf8.jpeg">

[UnityのCPUプロファイラ](http://tsubakit1.hateblo.jp/entry/2016/05/09/073000)の **Deep Profile** を使えば、具体的に **どのクラスのどのメソッドが重いのか** が確認できます。
こちらもXcodeでの負荷計測と同じく、実機で測定すべきですが、大まかな部分はUnity上で確認してからでも良いかもしれません。

処理時間が長い場所を特定したら、

・重いメソッドやメンバアクセスを減らす
　┗ GetComponent、Distance(magnitude)、transform...
・ガベージコレクション対策
・Raycast関連
　┗ 実装のRaycastや、キャンバスのタッチ判定（CanvasRaycaster）...
・Animator関連
　┗ SetBoolやTriggerを呼ぶ頻度、モデルのOptimize...

など、原因に合わせて行うべきアプローチが変わります。
細かくは別記事にまとめたので、よろしければご覧ください！

<a href="https://qiita.com/flankids/items/aaef052162dacaef3dc2"><img width="500" alt="CPU負荷.png" src="https://qiita-image-store.s3.amazonaws.com/0/184294/2b2c2cf4-12be-1a3f-c909-8c03b31f09ff.png">


## GPU負荷削減
GPU負荷の原因はいろんな要素が原因に考えられます。
一般的にドローコールやモデルのポリゴン数を減らすことで負荷を下げていきますが、 **先にそもそもの設定を見直すことを推奨** します。
設定の難易度と効果を加味して、おすすめの対処順に紹介していきます。

 **1. 解像度を下げる
2. ライトを減らすorなくす
3. 半透明のモデルを減らすorなくす
4. ドローコール（SetPassCall）を減らす
5. ポリゴン数を減らす** 

それぞれの詳細は別記事にまとめたので、よろしければご覧ください！

<a href="https://qiita.com/flankids/items/823f9bc19c669912237b"><img width="500" alt="GPU負荷.png" src="https://qiita-image-store.s3.amazonaws.com/0/184294/c1e91033-d926-350c-53b9-e0d454bf2f89.png">

なお、このときに必ず、 **量産するクリエイティブに関するスペックの制限** を決めておきましょう。（3Dモデルなら、ポリゴン数、ボーン数、マテリアル数、テクスチャサイズなど）
量産してから「ボーン数が多すぎた！」となると取り返しがつかなくなるので…！

## それでも目標FPSに満たない場合
CPU負荷もできる限り抑えた、GPU負荷もできる限り工夫した、それでもFPSが目標に満たない場合、下記のようなアプローチがあります。

 **▼目標FPSを落とす** 
60FPSをターゲットに調整をしている場合、30FPSを通常動作、ハイスペック端末は設定で60FPSで動作できるようにする、というようなアプローチです。
サポートする最低スペックの端末と最新端末でスペックに開きが出るときは有効で、最近のスマホゲームでこの手法を取っているサービスもよく見かけます。

ただし、30FPSに満たないかつ、操作にアクション性が求められるものの場合、快適さが著しく低くなるため、この方針を適用するのは非推奨です。

 **▼仕様調整** 
負荷にかかわる処理や表示を制限するアプローチです。
 **一度に表示するオブジェクト数を制限したり** 、 **画面外のオブジェクトのUpdateを止めたり** 、という方法が考えられます。
前者はプレイヤーが見られる情報が減るため、 **間接的にゲームバランスに影響** し、後者はゲームの動作結果自体が変わるため、 **直接的にゲームバランスに影響** します。
プランナー、デザイナーとよく相談して、妥協点を見つけましょう。

 **▼最低スペック端末の基準を上げる** 
記事の序盤に設定した、サポートする「最低スペック端末」の世代上げるアプローチです。
 **古い世代の端末のユーザーを足切りすることになる** ため、できれば避けたい方法ですが、プロジェクトの目指すゲームのクオリティと天秤にかけ、判断しないといけないこともあるかもしれません。
ディレクターやプロジェクトマネージャーと相談して、選択しましょう。

# おわりに
対応の流れのまとめです。

 **1. 端末を決める
2. 目標FPSを決める
3. 負荷を測る
4. 負荷を減らす
5. 最終相談（目標FPS、仕様、端末スペック）** 

負荷削減はとても地道な作業で、デザイナーやプランナーなど、他の職種と折り合いをつけるのも非常に大変ですが、プロジェクト一丸となれば、きっとユーザーが快適に遊べるゲームを作れます！
指先の操作感に全力を！

長くなりましたが、ここまでご覧いただきありがとうございました。

もしよければ、いいねを押して頂けると嬉しいです！