今回はUnityの最適化に関する話題で、今までに私が得た最適化の知識を総まとめしておこうという内容の記事です。
私は(執筆時点で)3Dゲームを15作品以上作った経験があるのですが、3Dゲームとなるとどうしても処理が重くなりがちで毎回最適化に関する悩みを抱えてきました。特に私の初期のオープンワールドゲーム等では
といった苦情をたびたび頂いたほどで、3Dゲームを作るからにはきちんとゲームを最適化しないとダメだなということを嫌というほど痛感したものです。しかし、とりわけ初心者の頃は一体どうすればゲームが軽くなるのか全く分からず苦労しました。このような経験から最適化は重要であるにも関わらず「やり方がよく分からない」という理由で放置されがちであり、失敗の原因になりやすいことを実感しました。
そこでこの記事では皆さんが私と同じような苦労をしなくて済むように、過去の最適化の記事でとりあげた内容を復習しつつ、具体的な最適化のやり方を初心者の方でも分かるように説明していきます。ぜひ参考になさってください。
そもそも「最適化」とは?
では具体的な内容に入る前に、「そもそも最適化って何?」という方もいらっしゃるかもしれないのでその点について簡単にお話しておきます。
ゲーム制作における最適化とは、一言でいえば
ことです。
ゲームを作っていると制作サイドの都合でPCに無駄な処理をさせる結果になってしまうことが非常によくある(例えば、作るのが簡単だがPCへの負荷が高い処理になることがある)のですが、ひと手間かけてそういった処理を改善していくのが最適化だといえるでしょう。
最適化のお供「プロファイラー」
それでUnityで最適化を行うにあたって重要なのがゲームの処理負荷を視覚化することです。最適化を行うからには
- どの処理で負荷が掛かっているのか?
- どのタイミングで負荷が掛かっているのか?
といったことが分からないと原因をつぶすことは困難ですよね。そこでUnityには「プロファイラー」という負荷をモニタリングするための機能(※下図)があるのでそれをうまく活用しましょう。
プロファイラーウィンドウの出し方
もしプロファイラーウィンドウが出ていない場合は、メニューバーの「ウィンドウ」→「分析」→「プロファイラー」からプロファイラーウィンドウを開くことができます。適当な位置にドッキングして常駐させておくとすぐにグラフを確認できて便利です。
プロファイラーの基本的な使い方
プロファイラーには様々なグラフが表示されますが、とりあえず初心者の方は一番上の「CPU Usage」のグラフの見方を覚えてください。
このグラフは横軸が時間(単位はフレーム)・縦軸が処理時間(単位はミリ秒)になっています。処理時間は短いほど良いので、基本的にグラフの位置が低いほど良い結果が出ていると思っていただければ差し支えないでしょう。
あとグラフを見てみると色が分かれていると思うのですが、これはそれぞれの色ごとに描画処理とか、物理演算といった処理内容を表しています。色分けの一例を挙げると
- 描画処理:緑
- スクリプト;青
- アニメーション:水色
- 物理演算:オレンジ
といった感じです。このグラフを見れば、どのタイミングでどのカテゴリの処理が重いのかが一目でわかるのでとても便利です。
プロファイラーによる分析はとても奥が深いのでここではほんの少ししか説明できませんが、慣れてくると表示されるグラフから色々なことが分かるようになるので最適化すべき部分の特定には必要不可欠です。興味のある方はぜひググって頂き、さらに詳しい使い方を学んでみてください。
Unityの主な最適化のカテゴリは3つ
さて基本的な部分についてご理解頂いたところで、Unityにおける最適化について具体的にご説明していこうと思います。Unityで主に行える最適化は大きく分けると次の3つのカテゴリがあります。
- グラフィックの最適化
- 物理演算の最適化
- C#スクリプトの最適化
以下でそれぞれについて詳しく見ていきましょう。
グラフィックの最適化
まずはグラフィックの最適化についてです。個人的な経験から言うと3Dゲームの場合はグラフィック関係の処理が最も重くなりやすいので、特に重点的に最適化を行う必要があると思います。
グラフィックの最適化にあたって特に重要な手法は次の通りです。
- URPを使用する
- カメラの設定を見直す
- メッシュを結合する
- マテリアルを統一する
- テクスチャサイズを見直す
- ライトを使い分ける
- Terrainの設定を見直す
- ポストプロセスを使いすぎない
- LODグループを上手く使う
- シェーダーを軽量なものに変更する
- オクルージョンカリングを使う
URPを使用する
まず一つ目の手法は、デフォルトのテンプレートではなくURPを使うことです。デフォルトのテンプレート(=ビルトインレンダリングパイプライン)は旧式のグラフィック描画方式を採用していて遅いので、プロジェクトの作成時にURPを選択することでグラフィック描画を高速化することができます。
デフォルトテンプレートとURPの具体的な違いについては以下の記事でご紹介していますので、併せてご覧いただければと思います。
カメラの設定を見直す
次に二つ目はカメラの設定を見直すことです。Unityでは基本的にカメラに写っているオブジェクトに対して描画処理が走るので、例えばカメラの撮影範囲を狭めることで余計なオブジェクトが映らないようにして処理を削減することができます。
やり方は簡単で、Cameraコンポーネントの「クリップ面」の「ファー」の値を小さくすればOKです(下図)。
また、後述するオクルージョンカリングを行う場合は「オクルージョンカリング」にチェックが入っていることを確認しましょう。
これはすぐにできる最適化なのでぜひ試してみてください。
メッシュを結合する
三つ目はメッシュを結合することです。シーンに別々のメッシュがたくさん存在している場合、1つ1つに対して描画処理が呼び出されるので負荷が高くなりがちです。そこでもし可能であれば、近くにあるメッシュ同士を結合することで描画負荷を軽減することができます。
ただし残念ながらUnityの標準機能ではメッシュを結合できないので、パッケージの「Probuilder」のメッシュ結合機能を使うか、アセットストアで「Easy Mesh Combiner MT」のようなメッシュ結合用のアセットを購入する必要があります。
あと「メッシュを結合するとよい」と書くと勘違いされがちなのですが、何でもかんでも結合すればいいというわけではないので注意してください。原則として近くに密集しているメッシュを一つに結合するのが一番効果的で、逆に広範囲に散っているメッシュを結合してしまうと十分な効果が得られない場合があります(カメラに一部でも映っているメッシュには描画処理が走る点を考慮してもらえばその理由がよく分かると思います)。なのでメッシュを結合する際は粒度を考えて実行するようにしましょう。
マテリアルを統一する
四つ目はできるだけマテリアルを統一することです。メッシュレンダラーに登録されているマテリアルがバラバラだとその分だけ描画コストが増える場合があるので、例えば同じ色のマテリアルが複数あるならそれを同じマテリアルに統一する、といった工夫が必要です。
ただしURPを採用している場合は、「SRP Batcher」という最適化機能がありマテリアルのシェーダーが同じなら描画コストを抑えられる場合もあります。
テクスチャサイズを見直す
さて五つ目はテクスチャサイズを見直すことです。アセットストアで3Dモデル等を購入した場合、高解像度のテクスチャ(4096px四方とか)がそのままインポートされることが多くゲームの容量が激増したりメモリを圧迫したりします。なので特にスマホ用ゲームの場合はテクスチャサイズには十分に気を付ける必要があるでしょう。
テクスチャサイズの変更方法はすごく簡単で、テクスチャ設定の一番下の「最大サイズ」を適当に小さくするだけです。
スマホゲームの場合は大きくても「512」程度で十分でしょう。またPCゲームの場合でもリアルさ重視のゲームでなければ「1024」か、大きくても「2048」のサイズがあれば事足ります。
ライトを使い分ける
六つ目はライトを適切に使い分けることです。ゲームを作っているとポイントライトを乱用しがちですが、ポイントライトは重いのでシーンに置きまくると負荷が掛かってしまいます。そこで場合によってはスポットライト等に置き換えることで描画コストを減らすことができます。
ただしURPを採用している場合は、シーンにたくさんライトを置いても特定のライトしか有効にならないようになっているのであまり気にしなくてもよいかもしれません。
あと、リアルタイムライトとそれによる影は負荷が高いので、なるべくライティングをベイクするようにしておくとベストです。
Terrainの設定を見直す
七つ目はTerrainの設定を見直すことです。3Dゲームでフィールドを作るときによく使われるTerrainは、巷では重いといわれがちですが実は設定次第でかなり負荷を軽くすることができます。
Terrainの最適化については以下の記事で詳しくご紹介しているので、そちらも併せてご覧ください。
ポストプロセスを使いすぎない
八つ目はポストプロセス(URP・HDRPの場合はVolume)を使いすぎないことです。ポストプロセスを使うと良い感じの絵作りをすることができますが、例えばBloomやDepth Of Fieldなどは描画負荷が結構増えるので注意が必要です。本当に必要なものだけを有効化するようにしましょう。
LODグループを上手く使う
九つ目はLODグループを上手く使うことです。LODとは「Level Of Detail」の略で、カメラからの距離に応じてメッシュを雑に表示することで描画負荷を下げる機能のことです。
LODグループについてはUnity公式マニュアルをご覧ください。
シェーダーを軽量なものに変える
10個目はマテリアルのシェーダーを軽量なものに変更することです。例えばURPなら
- Lit
- Complex Lit
- Simple Lit
- Unlit
といったシェーダーが標準で用意されており、デフォルトでは「Lit」が採用されていますが、これを適宜「Simple Lit」や「Unlit」に変更することで高速化が見込めます。特にスマホゲームの場合に効果を実感しやすいと思うのでシェーダーの変更も検討してみてください。
オクルージョンカリングを使う
最後はオクルージョンカリングを使うことです。オクルージョンカリングとは、簡単に言えば隠れていて見えないメッシュを非表示にすることで描画負荷を軽減する手法です。
オクルージョンカリングの使い方については以下の記事で詳しく説明していますので併せてご覧いただければと思います。
物理演算の最適化
次は物理演算の最適化についてです。物理演算の処理を軽くするための主なテクニックとしては次のようなものがあります。
- なるべくメッシュコライダーを使わない
- 固定時間ステップを大きくする
なるべくメッシュコライダーを使わない
まず一つ目はなるべくメッシュコライダーを使わないことです。メッシュコライダーは簡単に複雑な形状の当たり判定を付けられる代わりに、処理が複雑なのでかなり高い負荷が掛かってしまいます。したがって可能であれば
- スフィアコライダー
- カプセルコライダー
- ボックスコライダー
といった単純なコライダーを使うことを検討してみてください。
固定時間ステップを大きくする
次に二つ目は、プロジェクト設定から固定時間ステップの値を大きくすることです。
固定時間ステップの値が小さいほど物理演算が正確になりますがその分処理が重くなります。まあたいていのゲームではデフォルト設定のままでOKですが、スマホゲームなどでは値を大きくして負荷を下げるのも良いかもしれません。
C#スクリプトの最適化
さて最後はC#スクリプトの最適化についてです。これについては以下の記事で詳しくご紹介しているのでそちらをご覧いただければと思います。
一応概要を簡単にまとめておくと、
- 重い関数はUpdate関数などから頻繁に呼び出さないようにする
- なるべく高速な関数を使う
といった感じです。C#スクリプトの書き方が悪いと処理が重くなりがちなので注意しましょう。
おわりに
以上、長くなってしまいましたがUnityで自作ゲームを最適化する方法をまとめてみました。ご紹介したテクニックの中には比較的簡単に最適化できる方法もいくつかあるので、是非それだけでも試して頂ければと思います。
この記事がゲーム開発のお役に立てば幸いです。