テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】Nested PrefabやPrefabエディターが追加、新しいPrefabワークフローを触ってみた

f:id:tsubaki_t1:20180621224234j:plain

Uniteベルリンにて、Nested Prefabが可能になるプレビュービルドが公開されたので触ってみました。

追記:Unity 2018.3にて搭載されています

 

 

Prefabの階層化が出来るようになった

UnityではPrefabの中にPrefabを配置すると、親のPrefabに統合されるという問題がありました。

例えばPrefab AとPrefab Bという異なるキャラクターにPrefab Cという共通の武器を持たせた場合、反映するとPrefab Cという情報は失われ統合されます。結果、Prefab Cの内容を書き換えようと思ったらPrefab A・Bを探して書き換えるといった手間が発生することになります。

f:id:tsubaki_t1:20180621232109j:plain

 

新しいPrefabのワークフローではこのPrefabのネスト問題が解消し、良い感じに出来るようになりました。

Prefabの親とPrefabの子は明確に分けて設計出来るようになっていて、Prefabの親を変更した場合でもPrefabの子のユニークさは維持されるようになっているみたいです。

f:id:tsubaki_t1:20180621232135j:plain

f:id:tsubaki_t1:20180621230043j:plain

 

例えば2つのPrefabの中に共通のテキストプレハブを配置して、中身を書き換えてみます。Prefabが異なりますが、色を変えればちゃんと反映されています

f:id:tsubaki_t1:20180621231445j:plain

f:id:tsubaki_t1:20180621231217g:plain

 

またPrefabをGameObject側で変更した場合、何がどう変更したのかといった差分が見れるようになったみたいです。

f:id:tsubaki_t1:20180621231207j:plain

 

Prefabのバリアント

Prefabは基本的に他のPrefabとは異なるものとして扱われました。例えばImageのプレハブを複製した場合、元となるPrefabを更新しても他のPrefabには反映しません。

 

新しいPrefabワークフローではPrefabバリアントという機能が追加されました。これはPrefab毎にユニークな値を持ちつつも、変更していない内容は共有する…といった物みたいです。
例えば下のUIは上がオリジナル、下がバリアントなPrefabです。2つは独立した異なるPrefabなのですが、オリジナルを変更するとバリアントのPrefabも反映されています。

f:id:tsubaki_t1:20180621233532j:plain

f:id:tsubaki_t1:20180621233357g:plain

これは、例えばオリジナルなユニットを作った後バリアントでユニットを量産しておけば、オリジナルのユニットを変えれば全てのユニットに反映する…といった感じで使えそうです。

 

Prefabエディターの追加

通常Prefabを操作する場合は、編集用シーンに配置して操作等をしていた訳ですが、Prefab用エディターが新しく追加されました。

Open PrefabやPrefabボタンのダブルクリックでシーンとは独立したPrefabエディターが開き、編集出来るようになっているみたいです。

f:id:tsubaki_t1:20180621234140g:plain

 

プレビューのダウンロード

下のページよりダウンロード出来ます。

unity3d.com

【Unity】Unity 2018.2で「C# プロジェクト "Assembly-Csharp"は、このコンピューターにインストールされていない」が出たので、その対処

f:id:tsubaki_t1:20180617025211j:plain

Unity 2018.2にてVisual Studio経由でソースコードを編集する際、C#プロジェクト"Assembly-CSharp"はこのコンピューターにいんすとーるされていない。".NET Framework、Version=4.7.1"を対象にしています。」といった感じのダイアログが出ました。
↑のダイアログが出ると、ソースコードを追加する度にダイアログが出てきて、例え「今後この操作中にメッセージを表示しない」しても何度でも出てきます。

それの対処法についてです。

 

対処法

対処法は簡単で、.NET Framework Version=4.7.1のターゲットパックをVisual Studioに入れれば良いです。

 

ということで、パックを入れていきます。

f:id:tsubaki_t1:20180617025634j:plain

まず、ツール>ツールと機能を取得(T)を選択します。
すると暫くしてワークロード一覧が出るので「個別のコンポーネント」タブを選択します。
一覧から.NET Framework 4.7.1 Target Pack」を選択し、「変更」ボタンを押します

この後、実行中の処理が云々とか言われるかもしれませんが、無視して続けても問題なかったです。

f:id:tsubaki_t1:20180617025439j:plain

これでダイアログが出なくなりました。

 

Macの場合

Macの場合はこちら

tsubakit1.hateblo.jp

【Unity】C# Job SystemからECSのEntityやComponentDataを追加・削除・変更する指示を出す

今回はECSのComponentDataの追加や削除・変更といった動作をJobSystem上から指示する方法についてです。

 

 

C# Job SystemからComponentDataの追加・削除等を指示する

ECSは処理を並列処理しやすいように作られています。ただしEntityの追加や削除、ComponentDataの付け替えといった動作はメインスレッド上で行われなければいけません。
ただしEntityの追加や削除の判定は非同期でも問題ないので、そういった判定自体は非同期で行いEntityの削除等はメインスレッドで行う…をやってみます。

考え方的には、非同期でEntityに対するタスクを溜め込んでおき、後で一気に処理するといった感じです。

 

試しに作ったのは下のような感じで、マウスの近くにあるEntityを削除するだけの簡単なモノです。ただしチェックは総当たりしているのでEntityの数によっては非常に長い時間がかかりそうな物です。
(例えば40万個のEntityで試した所、毎秒80msのチェックコストが発生しました)

f:id:tsubaki_t1:20180614223808g:plain

BarrierSystem

この部分をバックグラウンドに持っていくため、BarrierSystemを使用します。BarrierSystemでEntityCommandBufferを作成し、EntityCommandBufferにコマンドを詰め込んでおけば後で実行してくれます。

EntityCommandBufferを実行するタイミングですが、BarrierSystemの実行タイミングに依存するみたいです。例えばBarrierSystemを継承したクラスを用意してUpdateAfter等をすれば好きなタイミングで実行できるといった感じ。

f:id:tsubaki_t1:20180614230529j:plain

面倒ならば、事前に3つのタイミングのBarrierSystemがあるので、そのタイミングで処するのが良さそうです。できるだけSyncPointは少ない方が良いという感じ。

  • EndFrameBarrier
  • MeshCullingBarrier
  • TransformInputBarrier

なおEndFrameBarrierはフレームの最後ではなく最初です(どちらも同じと言えば同じですが)

f:id:tsubaki_t1:20180614225954j:plain

 

サンプルコード

 

gist.github.com

IjobParallelFotやIJobProcessComponentDataで使用するには?

使用できません。基本的にIJobのみ使用できるみたいです。

https://forum.unity.com/threads/how-to-get-an-entity-in-ijobprocesscomponentdata.532573/#post-3505943

使えるようになりました

tsubakit1.hateblo.jp

【Unity】実践的なパフォーマンス分析と最適化

f:id:tsubaki_t1:20180609202944j:plain

今回はUnite Tokyo 2018で紹介された「実践的なパフォーマンス分析と最適化」のセッションの動画から得られた物をメモしていこうと思います。

なおセッションの音声をそのまま文字にするのではなく、メモを元に自分なりの理解を文章に起こしています。内容の順番も幾つか変更しています。

f:id:tsubaki_t1:20180609234258g:plain

なおセッションタイトルは「実践的なパフォーマンス分析と最適化」ですが原題は「Real world performance analysis and optimisation」です。

内容は、ローエンドなモバイル向けのパフォーマンスの最適化についてです。

 

 

はじめに

この話は、エンタープライズサポートでよく言われる問題、例えばメモリ使用率、ロード時間、CPU負荷について、どのように改善するのかといった事を話します。

f:id:tsubaki_t1:20180609203416j:plain

ただ最初はプロファイリングの話をします。
何故かと言えば、プロファイリングはパフォーマンスを話す上で迚も大事な要素だからです。実際アドバイスでは多くの場合プロファイリングについて話します。

プロファイリング

まずはプロファイリングを行い原因の抽出を行うことは非常に大事なことです。

f:id:tsubaki_t1:20180609204108j:plain

プロファイリングでよくある改善ポイントの一つは、リアルな環境でプロファイリングするという事です。言い換えればエディターの中でプロファイルしないという事です。
エディターのパフォーマンスは特定プラットフォームの動作と異なる場合があります。例えばエディターではアンロードされないリソースによってメモリ使用率が変わったり、スクリプトの動作が変わったり。

では何処でプロファイリングするべきかと言えば、想定するハードウェアやプラットフォーム上でプロファイリングします。古いモバイルなら古いモバイル、コンソールならコンソール。
特にCPUやファイルIOの特性も異なるので、出来る限りそれぞれの環境でやるべきです。スケーリングする場合は、頑張ってください。

次にゲーム全体の状況をプロファイリングするのも重要です。これは2つ理由があって、単体の動作確認では不十分である場合があります。

  • ゲームの原因を把握してると思ったら違った…というケースを発見する
  • リソースが複数の場所で共有している事がある

あと基本ですがプロファイリングは変更前と変更後で行う事も大事です。

 

いくつかプロファイリングの機能と新機能の紹介です。

プロファイラーのCPUプロファイラーウィンドウ。これはCPUの使用率であったりGCによるスパイクの把握等に役立ちます。
最近は以前は未対応だったスレッドのサポートや、ビルド済みStandaloneプレイヤーのDeepProfilingが追加されました。

f:id:tsubaki_t1:20180609205329j:plain

フレームデバッガは描画のコマンドを確認出来ます。
これはどのような順番で描画コマンドが発行されたかや、何故バッチされなかったのかといった情報を確認出来ます。

f:id:tsubaki_t1:20180609205646j:plain

メモリプロファイラという機能もあります。
これはUnityの使用しているメモリのスナップショットを取り確認できる機能で、Unity 2017.3からIL2CPP環境外でも使えるようになりました。

この機能ではメモリを使用しているものの名前と参照が分かります。例えば大きなテクスチャがメモリを専有しているとき、そのテクスチャは何という名前か、そして誰が参照しているのか…といった情報がわかる訳です。

tsubakit1.hateblo.jpUnityのプラットフォーム機能も紹介しましたが、プラットフォームのプロファイラも重要です。OSレベルでより高度な、全体的なパフォーマンスの把握に使用できます。

f:id:tsubaki_t1:20180609210227j:plain

 

最適化

ここから、よくある問題と解決方法についてです。

 

アセットの設定はルール付けして自動処理

アセット(Texture/Mesh/Animation/AudioClip等)はアセットの設定次第で容量が大きく異なります。アセットを後から調整しない為には、アセットのルールが必要です。

アセットのルールがあるのならば、AssetPostProcessor等で自動的にルールを適応するのが良いです。
そんなの面倒くせえと言う貴方の為に、エンタープライズチームが作成したアセットインポートの自動化ツールがありますGithubからダウンロード可能!

https://github.com/MarkUnity/AssetAuditor

f:id:tsubaki_t1:20180609211043j:plain

過剰な高解像度、無用な設定を排除

アセットでよくあるのが、過剰に高解像度なアセットです。例えば超高解像度テクスチャとか超細かいポリゴンとか。
この辺り殆どの場合ゲームにメリットはありません
得られる表現の内容と比較して、ランタイムのメモリサイズやアセットのサイズ、ローディング時間やGPUアップロードコストがバランス取れているのか考える必要があります。

またよくあるのはReadWriteが有効というケースです。例えばReadWriteが有効なテクスチャは約二倍くらいのメモリを消費します
これはGPUにアップロードしたテクスチャに加えてCPUのRAMにもテクスチャの情報が乗る為です。
これは主にCPUがテクスチャのピクセル情報を読み込んだり書き込んだりする為に使用します。これらが不要な場合はReadWriteを外せばRAMのコピーが無くなります。

生成したTexture2DはReadWriteが最初から有効です(自分でCPUからアクセスするので当然と言えば当然)。これはApplyでGPUにアップロードするときにRAMの情報を破棄することが出来ます

f:id:tsubaki_t1:20180609212107j:plain

これはメッシュも同様で、メッシュのReadWriteを有効にすると頂点情報がRAMに常駐します。
ReadWriteは以下の条件が当てはまる場合は外すとマズイですが、それ以外なら外しても良さそうです。

  • コードの中でMeshにアクセスしている
  • MeshColliderを使用していて、Transformがマイナススケールを持っている
  • MeshColliderを使用していて、Transformが斜めになっている場合

下2つは親オブジェクトが不均一にスケーリングしたり回転してる場合に起こるみたいです。この状況ではPhysXが正しく形状を取得するためにメッシュの情報が必要です。

多分こういう感じです。オブジェクト毎のスケールと回転がめちゃくちゃの時、Cubeが変な形状になります。

f:id:tsubaki_t1:20180609212938g:plain

コードで作られたメッシュも当然ReadWriteが有効です。これはUploadMeshDataメソッドでメッシュを開放できます。

f:id:tsubaki_t1:20180609213150j:plain

なおテクスチャにMipmapという項目があります。
これは画面の表示対してテクスチャのサイズが大きい時に描画効率が上がるというものですが、サイズが33%増えます。
画面に対して大きさが一致する、つまりカメラとの距離が変わらないなら無効にするのが良いです。

 

メッシュの圧縮

メッシュサイズを減らす方法として、Vertex Comporessionというオプションがあります。これはPlayerSettingsの設定です。
これは対象のチャンネルの精度を半分にしてランタイムメモリとディスクサイズを稼ぎます

なおSkinnedMeshRendererには非対応でしたが、Unity 2018.2からTextureの座標(UV?)だけ対応するようになります。

f:id:tsubaki_t1:20180609213846j:plain

ただし、ファイルで圧縮の設定が上書きされている場合は例外です。
Meshの設定に同様にMeshCompressionの設定があるのですが、これを有効にしているとVertex Compressionは無効になります
またReadWriteが有効の場合も無効になります。この辺りも含めてアセットのルールはよく考えるのが良いです。

 

アニメーションの最適化

次にアニメーションとその周辺です。
アニメーションの圧縮設定を変えることでメモリが改善します。
この辺り、Compression Error設定で微調整が可能です。見た目とランタイムの負荷で良いポイントを探すのが良さそうです(当然プロファイリングしながら!)

なおアニメーション圧縮は初期設定では無効になっています。これを有効にすることでクリップサイズはかなり小さくなります。

f:id:tsubaki_t1:20180609214610j:plain

Legacyの場合はKeyframe Reductionで冗長なキーフレームを削除します。GenericはKeyframe reductionかOptimalを選べます。

 

さてアニメーションシステムは大きく分けて3つあります。LegacyとHumanoidとGenericです。ドレを使うべきかというのを考えてみます。
CPU負荷で見てみます。
iPhone 4Sという非常に古い(コアが少ない)端末で12カーブという小さな要素を持つアニメーションを100個再生してみます。結果はLegacyのほうがGenericより高速です。

f:id:tsubaki_t1:20180609215243j:plain

逆に640という沢山のカーブを持つアニメーションの場合、LegacyよりもGenericの方が高速になります。

この内容から考えるに、カーブの数でLegacyかGenericか決めるのが良さそうです。
分岐点は大体300カーブぐらいで、その辺りを超えるとGenericのほうがよく、それ以下(例えばUIアニメーション等)だとLegacyの方が良いという感じです。ただし、この負荷はコア数によって変わってきます
(Mecanimはマルチスレッドで動作するが、Legacyはシングルスレッド)

f:id:tsubaki_t1:20180609215406j:plain

では、この一番重いHumanoidは何時使うべきでしょう。
Humanoidは同じクリップでもリターゲット出来ることがメリットです。またIKなどにも対応しています。
逆を言えば、IKやリターゲットが必要ならばHumanoid、使わないならばGenericという形で良さそうです。

f:id:tsubaki_t1:20180609215843j:plain

またアニメーションの動作において知っておいたほうが良いのがCullingModeという機能です。
Always Animateは例え画面外でも常にアニメーションとステートマシンを実行します。最も負荷が一定で高いです。
一方CullComplete設定では画面外にいるキャラクターはアニメーションしないようにします。これにはステートも含みます。つまり画面外だとステートマシンが止まります
CullUpdateTransformsはステートをアップデートするがIK等は画面外では動作しないという設定です。

 

Animatorで喜ばしい重要なアップデートがあります。
Animatorはデータバッファーやステート情報をアクティブ時に保持しており、非アクティブになると破棄していました。つまりAnimatorを持つオブジェクトをプーリングすると、バッファーの破棄や再取得、ステートの再設定が高いCPUスパイクにつながっていました。

Unity 2018.1からKeepAnimationControllerOnDisableという設定が追加され、データバッファとバインディングを保持できるようになります
なおこの設定はスクリプトでしか設定できません。

f:id:tsubaki_t1:20180609220540j:plain

 

オーディオの最適化

オーディオでもメモリをセーブしたいと思います。

オーディオクリップのロードタイプですが、ファイルの大きさが1MB以上の場合はStreamにするとメモリ負荷が少なくて済みます

一方ファイルの大きさが200KB~1MB程度の場合はCompressed Memory200KB未満ならばDecompress on loadが推奨されます。

この区分は、ファイルの展開に200KBのバッファーを使用しているので、それ以下のファイルサイズの場合バッファーサイズがランタイムサイズを上回ってしまうためです。

f:id:tsubaki_t1:20180609230437j:plain

圧縮率及びCPU負荷も見てみましょう。
これはモバイル向けの設定で、どの程度圧縮されるのかと言ったものを見ています。

クォリティは高ければ高いほど圧縮効率は悪いです。最も悪いのはVorbisをクォリティ100%で圧縮した場合で31%まで圧縮されます。一方MP3ならば22%までと、比較すると高圧縮です。
つまりクォリティが高い場合はMP3の方が良い結果に見えます
なおクォリティが50%の場合は同じくらいの圧縮率になります。

f:id:tsubaki_t1:20180609230811j:plain

次はロードにかかる負荷を見てみます。
iPhone4SでCPUに掛かる負荷を比較しています。
最も負荷が低いのはADPCMで、1.4%と非常に低く解凍も早いです。
Vorbis100%は負荷も高くMP3と比較すると倍ぐらいコストがかかっています。こちらも50%にすると大体同じぐらいの負荷になります。

f:id:tsubaki_t1:20180609231129j:plain

このことから、圧縮率がそれほど重要ではないものはADPCMが良さそうです。長い場合はVorbisですが使えるならMP3の方が良さそうという結果になりました。

 

起動時間の改善

起動のコストですが、一つの関数がすごい負荷を計上していました。
GetScriptingClassという関数で、アプリケーションの起動時にクラスタイプのためのアセンブリを探しにいくAPIです。
元々は文字列で色々やっており、特にIL2CPPで高い負荷を形状していました。これを改善することでゲームによっては起動時間が5秒も改善したとか何とか。

これは既に修正済で、現在Unity 2017~の全てのバージョンにパッチが適応されています。(Fixは2018.2のタイミングなので、たぶん最近のパッチです)
要するにパッチバージョンを使えば治るという感じでしょう。

f:id:tsubaki_t1:20180609231902j:plain

 

Crunch圧縮

もう一つ、UnithはCrunch圧縮をサポートしました。
これはDXTでしか使えなかったものがETCやETC2でも対応したというもので、Unity 2017.3から使用できるようになりました。

Crunchは圧縮テクスチャを追加で圧縮する手法で、GPUへアップロードする直前に解凍するというものです。
これをAssetBundleに格納して比較した所、ファイルサイズは大幅に小さくなりました。但しロード時間が劇的に伸びてしまっています。
この辺りは現在調査中です(普通に伸びると思うのですが、発表者的には短くなってほしかったのか…?)

f:id:tsubaki_t1:20180609232144j:plain

 

感想

来週の月曜日(6/11)から5日間にかけて「1日5時間×5日間連続 全25時間放送」というUniteの動画をチャットしつつみんなで見直そうぜ的なイベントがあるので、先駆けて少し見たセッションを文章にしてみました。

connect.unity.com

内容に関しては、正直モニョる部分もあったりしますが、かなり有益かなという印象があります。
まぁこの内容を鵜呑みにせず”分析”して”最適化”するのが良さそうです。

 

関連

資料:【Unite Tokyo 2018】実践的なパフォーマンス分析と最適化

動画:【Unite Tokyo 2018】実践的なパフォーマンス分析と最適化 - YouTube

 

最近の個人的に思う有益な動画と言えばコレ

tsubakit1.hateblo.jpUnite tokyo 2018の動画は全て公開されました

tsubakit1.hateblo.jp