Unity環境でAndroidアプリを最適化しよう!

Unity
スポンサーリンク

今回は開発中ゲームでAndroidで起動させることにしたのですが、最初は重すぎてまともに操作できるような状態ではありませんでした。
Androidアプリの高速化・最適化について記事にしてみましたので動かしてみたけど処理がすごい重い、画面がカクカクって方はぜひ読んでみてくださいね。

最適化とは

最適化というのは簡単に言うと文字通り「最適な形にするということです。
例えば処理が重いというのは最適な形になっておらず無駄な機能があるから重いということです。

基本的にその端末に合った処理形式に近づけていき、動作を軽くしていくという意味となります。

 

最適化をした経緯

なぜ最適化しようかと思ったかと言うと

「そろそろ一旦実機で確認しておいた方がいいかも…汗」

というプログラマーの直感が働いたからです。

そして急いで下記を対応してAndroidで確認することにしました。

  • タッチ画面での操作UIを実装
  • Android用のアプリ出力

そしてワクワクしながらゲームを動かした矢先、画面確認すると「1秒で1フレームぐらいしか動かない遅さ」だったのです…

これまで簡単なカジュアルゲームしか作ったことなかったので、シェーダーもデフォルトの軽いものしか使っていませんでした。そして今回もUnity様がいい感じに軽くしてくれるんじゃないかと甘い期待を持っていたんですが、さすがに世の中甘くはないということですね(笑)

 

元から重かった可能性

元から重かったんじゃないの?

と言われそうですが、パソコン版ではまったく問題なかったんです。

今はスマートフォンが普及していて、それでYouTubeも見れるし、アプリも遊べるし、動画の編集なんか出来てしまいます。なのでパソコンと出来ることとが変わらないという勘違いも生まれてしまいがちですが、パソコンのゲーム動かす性能はスマートフォンとは比べ物にならないということですね。

 

何が重いかを調査する

以前紹介した記事でプロファイルという機能があります。

こんな画面のやつです。

まずはこの機能を使って基本的に何が重いか目星をつけることにしました。
私の場合は「Graphics.PresentAndSync」というものが1フレームで大多数を占めていたため名前の通り「Graphics(グラフィック)」という名前から描画周りの最適化を試みました

 

重い原因

今回のケースだと2Dゲームの癖に地形やオブジェクトにノーマルマップ(凸凹感を出す画像)やライティング処理が入っている少し重いマテリアルを使っていました。

ちなみにマテリアルというのはシェーダとほぼ同じ意味で
ゲーム上でオブジェクトがどのような見た目にするかを決定付ける要素です。

そして、それが原因だと怪しんだ私は
そのマテリアルすべてをデフォルトのスプライトのマテリアルに戻してみました。

するとどうでしょう!
完全スムーズとまでは行きませんがギリギリ操作できるまでは軽くすることができました!

基本的に最適化ではこのように怪しいところを外してみたりするのが基本です。
怪しいなと思ったところはぜひ一旦外してみてくださいね。

 

最適化をしよう!

ここからが実際取り組んだ最適化方法です。
私の場合は描画が重かったのでその対応がほとんどとなります。
ではレッツ最適化!

 

最適化案:動かないオブジェクトをStaticにする

動かないオブジェクトやプレハブを選択して「Inspector」タブに下記のような「Static(赤丸)」という項目があります。これを設定するだけで軽くなるので便利です。

基本的にシーンで動かないオブジェクトに対して設定してください

内部の処理的にはカリング(画面外の見えないものを表示しない機能)したり、マテリアルが同じモノをまとめて描画して手順を間引いたりして高速化を実現しているようですね。

ただ非常に簡単に対応できるのでぜひ対応してみたい項目ですね。

 

最適化案:テクスチャをまとめる

テクスチャをまとめるというのは要するに複数の画像が1つの画像にまとめることを意味します。

開発中のゲームではこんな感じです。

テクスチャをまとめて処理が軽減出来る理由

なぜ軽くなるかと言うと一般的な描画エンジンではポリゴン(CGの大本になっている三角形)を描画するには基本的に下記のような手順を踏みます。

  • 頂点を設定
  • マテリアル(シェーダ)を設定
  • テクスチャ(画像)を設定

そしてこの要素が一つでも変更があるとまた設定し直す必要があります。
例えばポリゴンの「頂点を設定」部分はオブジェクトごとに場所が違うのでオブジェクト毎に更新されます

ではそれ以外はどうでしょうか?
例えば今回の「テクスチャ(画像)を設定」は1つ前に描画されたポリゴンと画像が違う場合、再設定が必要になってしまうんです。

ただ、そんな時
もし1枚の画像にまとめられていれば画像の差し替えの手間が省けるので
処理を軽くすることができるということなのです!

 

 

Unityのテクスチャをまとめる機能を使う

そしてUnityでは簡単にテクスチャをまとめる機能があります。
めちゃくちゃ便利です。

一昔前は手作業でまとめたり、コンバータを実装してWindowsコマンドなんかでまとめたりしてましたから。うーん、本当に時代は変わりましたね。

そして、そのまとめるファイル形式が「Sprite Atlas」、機能としての名前は「Sprite Packer」と呼ぶようですね。

Sprite Atlasファイルを作る

では作っていきたいと思いますが実際のファイルはこんな感じです。

そしてその作り方はProjectタブの中で右クリック→Create→Sprite Atlas(赤丸のところ)から作ることができます。

Sprite Atlasにスプライトまとめる

ファイルできたので早速画像をまとめてみます。今回のケースではステージごとに読み分けないので画像をすべてまとめている「Sprites」フォルダの画像を全部まとめています。

「Objects for Packing」という項目の+ボタンを押して、Spriteをまとめているフォルダをドラッグアンドドロップするだけです。

 

仕上げにSprite Packerを有効にする

では最後にまとめた画像が実際アプリで使われるように設定します。

「Project Setting」ウィンドウを開いて、「Sprite Packer→Mode」の項目を「Enable for Builds」に変更します。

これで無事Androidでテクスチャまとめられるようになりました!

「Always Enable」にするとエディタ実行でも「Sprite Packer」が使われるようになります。ただ変換などに若干時間を要すため、あくまで動作確認用として利用しましょう。

これらの設定でSprite Packerの機能が使われるようになったはずです。
実機やエディタで確認して問題なければOKです。

 

Sprite Packerで問題が起こった

今回初めて使ってみたのですが
実際SpritePackerを有効にしてAndroid動かしてみると下記のような問題がおきました。

  1. UIの端に別のテクスチャが見える
  2. エミッシブと呼ばれる画面の一定以上の明るいところに
    発光したようなエフェクトを掛けるのが弱くなった

ということが起きました。

1.は単純に「Sprite Atlas」のInspectorタブの設定にある、「Allow Rotation(回転許容)」、「Tight Packing(間隔キツめにまとめる)」を無効にして、「Padding(他の画像との余白)」を8に変更して他の画像との間隔がおかしくならないように修正しました。

2.が非常に厄介で元々Unity社が提供する2DGameKitというものを利用していたため、そのサンプルで用意されていたシェーダー(.Shader)が引き起こしていたようです。色々設定を変えても全く原因がわからず、結局使っていない機能も多かったので必要な機能だけを抜き取って再実装したものを使ったら正常に戻りました

要らない機能を削ってシェーダ自体がシンプルになり以前より処理が軽くなる事も考えられたのでそういう意図で削減したのをもありますね~

 

最適化案:マテリアルを最適化、種類をへらす

さきほどのテクスチャを一つにまとめる項目と同じ原理です。
マテリアルの種類が多いとポリゴンを設定する前の手順「マテリアル(シェーダ)を設定」が増えてしまうことで処理負荷が上がってしまいます。
それを回避するため利用するシェーダの数は減らしましょう。

私の場合は2Dゲームでマテリアルは2つぐらいしかないので大きな対応は入れていませんが
処理が大きく変わるシェーダ以外は一つにまとめるなどすると効果的だと思われます。

例えばノーマルマップ(凸凹感用のテクスチャ)しか使わないマテリアルとエミッション(発光用テクスチャ)しか使わないシェーダを分けている場合なんかは、意外に実際の描画処理よりもシェーダー差し替え「マテリアル(シェーダ)を設定」の方が重く無駄な処理になっているケースがあります。

もちろん描画数やその順番にもよるのですが、処理の流れをシンプルにするという意味でもマテリアルをできるだけ減らすというのは非常に重要です

 

最適化案:VSyncをOFF

モニターや様々な家電の画面というのは画面にキレイに出るように工夫をされています。
その機能がVSyncと呼ばれるものとなります。

「ProjectSettingタブ→Quality→Other→VSync Count」を
「Don’t Sync(同期しない)」に変更します。

設定画面はこんな感じです(赤丸のところ)。

VSync(ブイシンク、垂直同期)はSync(同期、待ち合わせ)というぐらいなので実は画面がキレイに出るようにタイミングを待っているのです。

そしてその機能を容赦なくOFFにします!

一見処理がキレイに出なくなりそうですが、ただ画面表示の切り替えタイミングがずれるだけなので大きく見た目に影響はないと思います。

 

ゲーム開発のVSyncとは

こちらは余談ですが
ゲーム開発ではグラフィックとゲーム処理というのは別の実行ライン(スレッド)で動いていることが多く、おそらくこのVSyncはゲーム側の処理待つことを意味しています。

当然ゲーム側の処理を待ってしまうと待ち合わせが必要になるためグラフィック側も次の作業に入れないため処理が遅くなってしまいます。
そのゲーム処理を待つのをなくしてしまうのが今回の設定だと思われます。

複数の処理が同時に実行されるマルチスレッドプログラミングなんかでは基本的に同期は最低限しないのがベストなのでそういったものと同じような考え方ですね。

 

最適化案:解像度を下げる

単純にこちらはゲームの解像度を下げます。
クオリティに対してハードの性能が追いついていないので当然ですね^^;

対応方法

他にも良い方法があるかもしれませんが下記のコードを
ゲーム開始時に呼び出せば解像度を下げることが出来ます。

private void Awake()
{
	Screen.SetResolution(800, 450, false);
}

 

解像度を下げると軽くなる理由

こちら描画の流れを知っているとわかりやすいのですが

一般的な描画エンジンでは「ポリゴンの形状確定(前述のポリゴン描画)」→「画面のピクセル(画面の画素の描画)」で成り立っています。

この「ピクセルの描画」というのがフラグメントシェーダ、ピクセルシェーダと呼ばれ画面の解像度だけ実行される処理になります。

たとえばHD解像度では画面全体に処理した場合、「1280×720=921600」、92万回以上もの処理をすることになります。
そして私のAndroidは不釣り合いにもフルHDと呼ばれる「1920×1080=2073600」、207万回以上の処理をしていたことになります。

実はこれが処理が重くなる主な原因だったんですよね(泣)
みなさんも注意してくださいね!

 

解像度を下げて画面は汚くならないの?

残念ながら当然画質は落ちます…
ただゲームがカクカクで操作できないというのはゲームとしては論外です。

いくらキレイでも操作できなければただの絵画になってしまいますのでゲームである以上どこが一番重要か見極める力も重要です。

私の場合は一旦ざっくり削って800×450(16:9)まで落としちゃいました。

ちなみにPS4の最新ゲームなども

実は最新PS4ゲームなんかも実は1280×720なんかで実際は動いていたりします。
なので携帯ゲームで同程度以上使うなんでおこがましいと感じてしまうのが本音ですね…

なぜ1280×720でも最新タイトルがキレイかというと
完成した画面を高画質化する「チェッカーボードレンダリング」といった技術を使って4K画面なんかも対応していたりするようですね。

番外編:将来はWEBベースだから最適化は要らない説

「これからはウェブベースのゲーム(Amazon Web Serviceなどを用いた)が処理するから最適化なんていらないんじゃないの?」って言う方もいるかも知れません。

ただ自分はそうは思えません。

その理由はサーバー側が処理するため、その分サーバーの月額費用が上がることが容易に想像できるためです。

一つのアプリが重いということは「1アプリの重さ×ユーザー数=サーバー負荷」となるため物によってはサーバー利用ができないことやサーバーの月額費用が莫大な費用が必要になると思われます。

少しの手間でサーバー運用費をガクッ下げられるならウハウハですね。
まあただの妄想ですが…
軽いアプリに越したことはないので興味のある方はぜひ最適化してみてくださいね。

 

開発ゲームのPR

また今回Androidで最適化するきっかけとなったゲームを現在絶賛開発中です。
YouTubeで開発ブログもしていますのでご興味のある方はぜひ見てみてくださいね。

 

終わりに

いかがでしたでしょうか。
こちらを使って皆さんのアプリも軽く慣れば幸いです。

私は今までUnityを使って本格的な開発をしたことがなかったので
この問題のおかげでシェーダーの書き方、プロファイルの使い方、最適化の方法まで知ることができかなり有益な試練でした(笑)

時間がかかったのも事実ですが、その分成長も出来てよかったです(^o^)

では長くなってしまいましたがここまでお読みいただきありがとうございました。

コメント

タイトルとURLをコピーしました