しゅみぷろ

プログラミングとか

Unite 2016 Tokyo まとめ(1日目)

はじめに

Uniteで聴講させていただいたものをとりあえず整理してまとめておきます。

2日分まとめて書こうと思ったんですがめちゃくちゃ長くなりそうなので分割することにしました。2日目はまた後日。本記事では1日目の内容を書いていきます。

個人的に興味深かった講演については後日もっと深く掘り下げようと思っています。今回は大雑把にどんなことをお話しされていたか等を残しておきます。

講演の資料はこちらからDLできます。

モバイル端末向けのUnityアプリケーションの最適化実践テクニック

モバイル端末のアプリケーション最適化テクニックについての講演でした。 この講演で強調されていたのは「良いデータを取ること」でした。 ここで言う良いデータとは、プロファイラ等を用いて計測されるパフォーマンス測定データのことで、これらのデータを正確に取得し、リソースを喰い潰している原因を特定することが最適化の第一歩となります。

プロファイラ

特定のプロファイラによるネイティブコードのトレースのセクションでは、PlayerLoop(Unityのメインループ部分)、BaseBehaviourManager::CommonUpdate(Update系メソッド)でテンポラリを用いることによる害や、String Manipulation、Boxing によるパフォーマンスの低下について説明されていました。 それぞれの解決策は

  • テンポラリをUpdate等で用いない
  • StringはHash値を求め、それを利用する
  • Boxingを回避する(is -> cast, 値型参照型変換, boxingが発生するコードを記述しない)

といった感じで、普段からこういったことを意識してコードがかけるかどうかが重要そうです。

ファイルインポート設定

また、外部からインポートするファイルについても注意が必要だと説明していました。たとえばMeshデータやテクスチャのインポート設定では、Read/Write enabledは(必要がなければ)falseにしておくべきです。アセットのインポートについてはAssetPostProcessorを継承することで独自に拡張することができます。 また、UI等で利用しているテクスチャ以外は基本的にMipmapを生成すべきです。

GCとManagedHeap

ManagedHeapとGCについても説明されていました。.NETで開発している時と注意点としてはあまり変わらない部分もあるかなと思いましたが、気をつけておくべきことについて書いておこうと思います。ManagedHeapは最初、ある程度の領域を確保しておき、その領域からオブジェクト生成の際のメモリを割り当てます。もし割り当てるメモリ領域がなくなってしまった場合、このHeap領域が整理され、フラグメンテーションが解消されます。しかしそれでも確保するべきメモリが足りない場合、Heap領域が拡張されます。Heap領域が大きければ大きいほど1回のGC.Collectによるレイテンシも大きくなります。ゲームにとっては致命傷になりかねません。UnityのProfilerで確認できる、GC Allocの項目は全て無くすくらいの勢いで最適化に臨むべきです。最適化の方法は

  • Stringの利用を避ける
  • Boxingを避ける
  • 再利用可能なコレクションを利用する(新しいコレクションをどんどん生成しないようにする)

といったものが挙げられます。

テキストパースによるロード時間

JsonXMLのパースには時間がかかる(内部でリフレクションを使っていることが多い)ため、ロードに時間がかかります。UnityのBuiltin型のJsonUtilityクラスを使うことで高速化できます。また、テキストファイルをScriptableObject等にしてしまうことで読み込み時間を少なくすることができます。UnityのデータタイプはWorkerスレッド上ではシリアライズできないので、別途Threadを生成してUnityのデータをデシリアライズし、高速化を図る、というようなことが出来ません。注意が必要です。ResourcesフォルダからAssetを排除し、AssetBundleを使うようにすることでも高速化を図れます。もうResourcesのことは忘れましょう!ローカルなデータもAssetBundleでロードするようにします。データの扱いがコード上でも一本化されて外部リソースを使う際にも煩わしさが軽減されて個人的には嬉しいことです。

ランタイムCPU

例えばマテリアルのシェーダープロパティのアクセスによくStringを用いますが、Hashでのアクセスの方が望ましいです。何回もこのStringで呼び出す場合に、一回だけHash値を求めて以後はそれを使ってプロパティにアクセスするわけです。String Manipulation、BoxingはメモリだけでなくCPU時間をも喰らいます。また、特定の文字列操作はかなり低速です(RegExps,String.Start(End)With,etc..)。既存のプロジェクトでは、CPUが処理に時間をかけている部分を見つけ出し、そこから改善していくことが大切です。

Unityグラフィックス最新機能ガイド

Unityの最新のグラフィックスについての講演でした。5以降のUnityでは「絵作り」がかなり楽しくなってきました。その際に利用できるImageEffectの紹介や、Unity5.4からの新機能などが盛り込まれた非常に有意義な講演でした。

Multi Thread Rendering

5.4からの新機能で、MainThreadだけでなくWorkerThreadでもレンダリングさせるようにしたそうです。スライドを見るとグラフが載っていますが、かなりパフォーマンスが改善しています。

Cinematic Image Effects

オープンソースで使えるImageEffectです。確認はしていませんが、DeferredShadingを行っている時のみ効果があるものとかもあるのかな?紹介されたエフェクトは

  • Screen Space Reflections
    • スクリーンスペースで行う反射を表現
  • Ambient Occlusion
    • ご存知AO
  • Bloom
    • ご存知Bloom
  • Depth of Field
  • Tone Mapping / Color Grading
    • カラー補正
  • Antialiasing
  • Lens Aberrations
    • レンズで覗き見るようなスクリーン効果を表現し、画面周囲にぼやっとした感じのなにがしを書く

といった感じでした。

Progressive Light Mapper

DynamicなGIが不要な場合にかなり使える表現になりそうです。ビルド時にライトマップを生成し、そのテクスチャを貼り付けてしまおうというものです。

ライトマップの生成はRaytraceによって行われていて、ライトからRayを飛ばし、ヒットした場合にテクスチャにライトをベイクします。

ハードウェア性能を引き出して60fpsを実現するプログラミング・テクニック

個人的にかなり好きな内容でした。Vitaで60fps出るようなゲームを実際に作って、工夫した点を簡潔にまとめてくださっていました。ゲームはこちらで公開されています。

後日公開されているプロジェクトを詳細に見ていこうと思っていますが、講演で述べられていたことだけさらっとまとめておこうと思います。理解が間違っている部分があるかもしれませんのでご了承ください。

ゲームロジックを別スレッドで行わせる

MainThreadにかかる負荷を減らすために、WorkerThreadを生成し、このThreadでゲームロジックを処理しているようです。WorkerThreadの問題点として、ほとんどのUnityのAPIが使えないことが挙げられます。PhysicsやColliderも使えないため「なければ作ればいい!」の精神でこれを実装していったそうです。まぁ全て完璧に実装する必要はなく、ゲームで必要な見せ方ができる程度に適当に作ればいいのでそこまで重く考える必要もないはずです。

MainThreadに情報を渡す経路には、ダブルバッファを使っています。WorkerThreadでは、ゲーム処理をUpdateする部分とこのバッファを更新するRenderUpdateなる2つのフェーズから成り立っています。MainThreadはUpdateされたバッファを参照し、必要なUnityAPIをCallする仕組みです(見てないので恐らくですが)。

GPUを多用する

エフェクトはGPUを多用することでCPUになるべく負荷をかけないようにし、パフォーマンスを発揮できるようにする試みです。ImageEffectはもちろんですが、Meshのverticesを更新してパーティクル効果を出す「GPUパーティクル」について紹介されていました。

動的なメモリを確保しない

structを多用し、オブジェクトは事前確保、使い回しすることでGCのCollectを避けたそうです。Heap領域はなるべく使わないようにし、Heapの拡張が起こらないようにしています。

学校では教えてくれないアセットバンドルのしくみ

みんな大好きAssetBundleの仕組みまでしゃぶらせてくれる夢のような講演でした。今後はResourcesも使わなくなり、AssetBundleに一本化していくことになりそうなのでゲームを個人で開発していてABはあまり触ったことがない・・・というような方でも今後触れる機会が増えていくのではないでしょうか。

アセットバンドルおさらい

アセットを含めたコンテナで、これを後から配信してゲーム内で展開し、利用することができます。Unityのランタイムで利用するデータはほとんどがアセットバンドルにできます。シリアライズされた複数のアセットを内部に含むことができますが、1つに含まれるファイル数が増えれば検索に時間がかかるようになってしまうので注意が必要です。

アセットバンドルには圧縮・非圧縮形式があり、それぞれデータ容量/読み込み速度 の観点から一長一短、といった感じでした。が、この2つの長所を持つハイブリッドな圧縮フォーマットによって、今後は更に使い勝手が良くなりそうです。

Unity5.3からの変更点

先にも少しあげましたが、圧縮フォーマットがLZ4に変更されます。以前のABでは圧縮フォーマットはLZMAで、伸張処理にファイル全体が必要だったのですが、チャンクが揃ったオブジェクトから順次アクセスすることが可能になりました。

UnityWebRequest

WWWを使うとManagedのHeapを拡張してしまう可能性がありました。これを回避するには、WWWを使わずにWebClientとかを使ってアセットをダウンロードしてどっかに保存する仕組みを作る必要があったのですが、この用途にUnityWebRequestが使えます。このクラスは直接WWWに置き換わる機能を提供してくれるそうです。

Unity上級者を目指すなら知っておくべきデバッグテクニック集

テラシュールブログでおなじみの方の講演でした(そうとは知らずに講演に来たのでビックリしました・・・)。内容的には開発者の方々ならごく当然の内容ではあったのですが 、バグへの向き合い方などを再度確認し、早期発見のための基本的な手順をおさらいできる、とても楽しい内容でした。MacでVS使いたいですね。仮想環境でVSを動かすこともできますがセットアップが面倒な上にVS用slnの構築を行うエディタ拡張等を用意する必要があったりデバッガを利用する場合リモートデバッガを使う必要があったりと怠いです。VS使うならWindowsで開発していくのが一番という結論。。

デバッグの手順

  1. バグを正しく定義する
  2. バグを再現する
  3. バグが発生する箇所を見つけ出す
  4. 問題を修正する

といった手順で行っていきます。バグではなく仕様であれば修正の必要はないのでみなさんCSのバグ対応には最大限ゴネてみることが大切です。

バグ確認の流れ

  1. 走査方法、デバイスの接続等の確認(入力の問題)
  2. パラメーターの差し替え(データの問題)
  3. プログラムを確認(ソースコードの問題)
  4. OSやプラットフォームの問題?(ルールの問題)

といった流れでバグを確認していきます。大抵パラメーターやらプログラムのロジックが間違ってるのでここまできたら観念して修正にあたりましょう。

絞り込みの手順

バグ発生箇所の絞り込みについてです。

  1. 問題周辺コードが呼ばれているか
    • Debug.Logを呼び出してみる。ログを表示したくない場合は、 Debug.logger.logenabled=falseで一括非表示にできる。
  2. パラメータは意図したとおりのものが入力されているか
    • Assertを使う。IsNotNullでもインスタンスがない場合があるので注意。Assert はリリースビルドでは完全に取り除かれるので、コード上に残しておいても大丈 夫!素晴らし! また、TestRunnerを書いて実行しちゃうといい。テストが実行されるのは現在の シーンらしい。 また、シーン上で距離などのパラメーターをギズモを使って表示させることで、 デバッグを容易にする。
  3. 問題発生時の値は正しいか
    • Debug.LogやAssertで値をみる。リッチテキストで色とかを変えて見やすく するといいかも。また、デバッガで処理を止めて確認したりとか。IL2CPPではデ バッガを利用できない(?)って言ってた。
  4. 処理の実行順は意図したとおりか
  5. 式は正しいか

というような感じで絞っていくといいっぽいです。