普通のノートパソコンで3Dのアクションゲームが作りたくて最適化を頑張ってみた【Unity】

#初めに
「普通のノートパソコンで敵がわらわら出てくるようなゾンビゲームを作りたい！でも、すぐにかくかくになってしまう」
ということでUnityおよびC#を用いた3Dゲーム制作でいろいろな文献をあさって最適化を頑張ったのでまとめてみました。
というか備忘録です。
私のノートはGPU積んでいないので基本的にはCPUについての話になるかと思います。
また、大部分をUnity.2017で作成しましたのでUnity.2018では違うということがありましたら教えていただけるとありがたいです。

※ここに書いてあることは**全部検証とかしたわけでなく**、Uniteの動画見たりブログなどに書いてあったことオンリーです

#Unityでできること
##オクルージョンカリング
見えないところの描画を消してくれる機能で私は消す部分をだいぶ大きめにしました。
試した中で、一番効果があったかと思います。
##当たり判定
MOBのプレイヤー探索を円コライダーで行っていたんですがphysicsの割合がかなり大きく、何回もコールしまくりでかなり重いという状況でした
前回と同じ検索結果ならすぐreturnするとかタグではじくとかやってみたけどいまいちの結果でした
解消できた方法はlayer physicsの設定でプレイヤーのレイヤー以外の判定をなくすことでした。
もし、探すものが一つしかないなら最初にFindとかしておくというのも手かなとは思います。

##オブジェクトプーリング
InstantiateとかDestroyというのはかなり負荷がかかる処理らしいです。実際にプロファイラ見てみても時間かかってたりGCが発生してたりします。
ので、あるタイミングで全部作っておく&消すときは見た目とデータだけ消して次に作るときに使いまわすという考えです。

私の場合だと最初に長いロード時間ができてしまいましたが生成のたびにFPSが下がるというのはなくなりました。

##アニメーション
Optimaize Game Object という階層の多いボーンをまとめる機能を使いました。
あとは、　アニメーションによる移動 > Transformによる移動　なので単純回転なんかはTransformでやったほうが良いらしいです。

あとは私はやってないことですが

```csharp

Animator anim = /*どうにかして中身入れる*/;
anim.SetBool("hoge",true);
```

みたいに書くと思いますが状態の変更もしないのに呼び続けるとやはり無駄な処理になるみたいです。

##Canvas
CanvasなどのUIを変更すると変更されていない部分も含めて作り直すという処理がされているらしいです
なので一回データを入れたら不変のもの(私の場合だとアイコンや名前など)とリアルタイムに動くHPなどのようなものを別のCanvasに変更しました。
これは変更後の実感はありませんでした。


~~あとは、ここに限った話ではありませんがPanelの半透明やめました。~~
2017/7/5追記
確かに半透明やめただけではコール数は不変でした。

##親子関係
アニメーションのところの話と被るのですが子や孫といった下の階層のものを動かすと子がないものを動かしたときよりもだいぶ重たい処理になるようです。
あまり意識してやっていませんがリリースの時には必要ない分のまとめる用のGameObjectなんかは親子関係を解消しておいたほうが良いようです。

##Debug.Log
変数の確認とかに使うと思いますがいっぱい出すとすごいパフォーマンス落ちます。ので、確認や修正が終わったら速やかに消すorコメントアウトしましょう。Assetのサンプルシーンとかがいっぱい出してるケースもあるようです。

##コンポーネント
上にある奴ほど参照のスピードが速いようです。なのでよく呼び出すものは上に一回しか呼ばないようなものは下にやってしまいましょう。
(これも試しては見ましたが実感ないです)

#スクリプト
##String結合対策
String型の文字列結合はかなり時間かかる+GC生むということで私もStringbuilderを使用してみました。あとは、UIの呼び出し回数削減も少しやってみました。
それによりGCはかなり減らせることができました。

```csharp
void OnTriggerEnter(Collider coll){
 if( coll.gameobject.tag("hogehoge") ){
  Destroy(gameObject);
 }
}
```

とか、やると思うのですがtag("hogehoge")もGCの原因らしいのでcomparetagを使うべきだそうです。(例、下図)

```csharp
String hoge = "hogehoge";

void OnTriggerEnter(Collider coll){
 if( coll.gameobject.CompareTag(hoge) ){
  Destroy(gameObject);
 }
}
```

##Getcomponentについて
Getcomponentはかなり重い処理に入るそうです。なのでキャッシュなんかを利用して極力しないほうが良いとありました。
(私もあまり数を減らせてるとは言えないと思います。)

###Transformについて
***2018/7/5追記コメントによると今は違うようです。***
Qiiitaの作法がよくわからないのですが間違えたから消すというのも違うと思うので打消し線入れたうえで置いておきます。
(自分でも調べてみてましたがだいぶ混在してるようです)


~~transform.position = new Vector3.zero;~~


~~というのはつまり、~~



~~Getcomponent<Transform>().position = new Vector3.zero;~~



~~に置き換えることができます。つまり、Transformを呼びまくるとあっという間にGetcomponentだらけになってしまうとのことなので気を付けるとよいそうです。~~


###Find
Findもかなり重い処理なので極力使わないほうが良いみたい。(これはみんな知ってると思うが)
私は

```csharp
[SerializeField]
private GameObject;
```

SerializeFieldを使って極力回避できるように実装しました。

###new
Unityだとあんまり見ることはないとは思いますがnewするとGCを生むとのことなので極力しない+したら使い倒すが原則みたいです
(Unityの搭載してるやつが古いって聞きました)
ここで「あっ」ってなったところがコルーチンの

```csharp
yield return new WaitForSeconds(秒数);
```

です。
これは


```csharp
Var num = WaitForSeconds(秒数);
//とループ外で宣言してから
yield return num;
```

とすると回避できるようです。

###その他

   - Vector3やMathfもあんまり呼ぶとよくない
   - List と Array は配列のほうが軽いので配列で実装
   - foreachもだめらしい(使わないのでよくわからないです)
   
(リスト大好きでしたが何とかしました。)

#モデル
- マテリアルの数を極力減らす(同じマテリアルをたくさん使うのが〇)
- 頂点数も少ないほうが当然良いらしいとのことですが私はあまり気にしませんでした。
- テクスチャも小さいほうがいいらしい
    - さらに、テクスチャのなかでも軽い奴と重い奴があるみたい


#最後に
そもそも、Monobehaviourがかなり重いらしいです・・・
理想は一つのMonobehaviourですべてのオブジェクトを動かすというのが正しいようですが私にはその方法がわからないです。(Getcomponentとかどうするの?)

ゲームのほうですが結果としてGPUなくてもすごいぬるぬる動くようになりました。 ^^ v

Uniteの動画でも言われてることですが最適化やるならprofiler見たほうがいいです。(私も使いこなせてないうえに一部機能使おうとすると固まりますが)
あとは、キーワードさえ知っていればググることができて、この記事の元となった資料に行きつけると思うので皆さんも軽いゲームを作りましょう。