テラシュールブログ

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

【Unity】既存のプロジェクトにLightweight RenderPipelineを導入する

今回はLight Weight Render Pipelineを既存のプロジェクトに導入してみます。

 

 

Lightweight render pipeline

Light Weight Render Pipeline(LWRP)は、Scriptable Render Pipelineで提供するテンプレートの一つです。
主にモバイルやモバイルVRといったパフォーマンスがリッチではない端末に焦点を当てているパイプラインで、多くの端末で動作しつつも見た目はそれ程貧弱にならないといったモノを目指しているみたいです。

f:id:tsubaki_t1:20180907184402j:plain

それなりに詳しい説明は、下のブログで確認できます。

blogs.unity3d.com

 

Light Weight Render Pipelineを使う

LWRPを使用してみます。

まずPackage Managerからパッケージを導入します。

  1. メニュー > Window > Package Managerを開く
  2. Allタブを選択
  3. Render Pieplines.Lightweightを選択
  4. Installを押す

f:id:tsubaki_t1:20180907184552j:plain

これでパッケージが導入されます。次にLWRPの適当です。

  1. Asset > Create > Rendering > Lightweight Pipeline Asset
    でアセットを作成
  2. Editor > Project Settings > Graphicsを開く
  3. Script Render Pipelineに先程作ったアセットを指定

f:id:tsubaki_t1:20180907185618j:plain

f:id:tsubaki_t1:20180907185646j:plain

画面がまっピングになります。
これはLWRPのライティングが今までのSurface Shaderに対応していないからです。
これを一つ一つアップデートするのは非常に骨なので、一括でアップデートしてしまいます。

  1. Editor > Render Piepeline > Update Project Materials To Lightweight Materials
  2. Processedを押す

これで一定の法則に従い、シェーダーがアップデートされます。
この法則については、Upgradable Shadersに書いてあります。

要するにStandard ShaderはStandard (Physically Based)、MobileやLegacy ShaderはStandard (Simple Lighting)になります。
ではそれ以外…例えばParticleのShader等はといえば、ライティングシステムを使用してないならそのまま使えます。Unlit等のシェーダーも同様です。

f:id:tsubaki_t1:20180907184716j:plain

 

 これでシェーダーがUpgradable Shadersの法則に従い差し替えられて、概ね見れるようになります。いくつかのシェーダー(Unityの光表現を使用しつつ差し替え対象から外れてるシェーダー・・・例えばReflectiveやGlass Shalder等)はShaderが動作せずピンク色になるので、手動で別のシェーダーを割り当ててやる必要があります。
光があり特殊な動きをするシェーダーに関しては、ShaderGraphに丸投げという感じみたいです。

f:id:tsubaki_t1:20180907213947p:plain

それができたらクォリティの調整です。
LWRPを適応するとQuality Settingsで変更していたライティング関連の項目が殆どLightweight Asset(先程作成したファイル)に移行しますので、プロジェクトにあった適当な値を設定する必要があります。

今までの設定に加えてさり気なくシャドウの解像度が設定できるのは嬉しいですね。

【Unity】リアルタイムな影がカクカクになる問題の対処 - テラシュールブログ

f:id:tsubaki_t1:20180907214958j:plain

最後にライトの設定も行います。というのも、LWRPは現在subtractive(減法)以外のライトマップに対応していません。なので、ライトは再度焼き直したほうが良いです。リアルタイムGIも無くなっています。

一応、Unity 19.1で他のいくつかのライトマップ(Shadow MaskとBaked Indirect)が実装されるみたいなので多少は変わるかもしれません。

Pipeline Feature Comparison
f:id:tsubaki_t1:20180907220221j:plain

 

【Unity】ECSのComponentSystem実行順をコントロールする

今回はECSのComponentSystemも実行順番をコントロールする方法についてです。

 

 

ComponentSystemの実行順番は非常に重要

ECSは無数に存在するComponentDataをComponentSystemで一括制御する事で実現しています。ソレが単なる技術デモでComponentDataとComponentSystemが一対一の関係なら大した問題にはなりません。

問題になるのは複数のComponentSystemで複数のComponentDataを制御する場合で、その場合ComponentSystemの実行順によって結果が変わってきます

例えば「Entity生成」「Entityの向きをターゲットへ変更」「Entityの向きに従って前方をチェック」「当たっているならEntityを生成」といったシステムを考えてみます。

f:id:tsubaki_t1:20180904191205j:plain

これが順番通り動くなら問題はないのですが、「Entityの向きをターゲットへ変更」の前に「Entityの向きに従って前方をチェック」が入ると、最初に向いていた方へチェックが入る事になり、思ったとおりの結果を得られなくなります。

f:id:tsubaki_t1:20180904191241j:plain

 

実行順の確認方法

まず実行順の確認方法です。
使っていて気づいたのですが、EntityDebuggerのSystem並び順は実行順で並ぶみたいです。
例えば下の場合、SampleSystem1~3を用意したのですが、実行してみると実行順はSampleSystem 2、SampleSystem 3、SampleSystm1の順でした。

f:id:tsubaki_t1:20180904175105j:plain

f:id:tsubaki_t1:20180904175211j:plain

まぁ一番確実なのはProfilerで順番を見る事ですが、処理時間が短いと探すのが大変なので、多分大丈夫だろう程度で使えそうです。

 

特定のComponentSystemの前に実行する、後に実行する

ComponentSystemはScript Execution Orderのような機能はありません。その代り、UpdateAfterUpdateBeforeというAttributeが使えます。

 このAPIComponentSystemを指定したシステムより前もしくは後に実行するように指定するというものです。PlayerLoopに登録する時、依存関係を見て実行順を作ってくれるとか云々。
下のコードの場合、System1よりSystem2が先に実行されるようになります。

gist.github.com

 なお、実はこのUpdateAfterUpdateBeforePlayerLoopのAPIにも使用できます。例えば下のように記述すると、Updateの前後に処理を挟むことも出来ます。

gist.github.comf:id:tsubaki_t1:20180904180713j:plain

なお実行のタイミングによってEntityDebuggerの表示される場所が微妙に変わる事があります。例えば下のコードでは[UpdateBefore(typeof( UnityEngine.Experimental.PlayerLoop.Initialization))]と記述した所、所属するグループがInitializationに変化しました。

f:id:tsubaki_t1:20180904180951p:plain

 

UpdateInGroupで処理のタイミングを大まかに纏める

次にUpdateAfterを特定のシステムではなく、システムをまとめた「グループ」を指定してみます。これは複数人で開発する時に重要な要素です。

例えば[A][B][C]という3つのシステムがあり、この処理後に何かをしたい場合です。この場合はUpdateAfter(c)で良いです。
では[A][B][C]に追加で[D]というシステムが増え、処理結果は[D]以降に取らなくてはいけなくなった場合、チーム全体にDというシステムが増えた事を共有する必要が出てきます。

f:id:tsubaki_t1:20180904183546j:plain

そこでグループを使用します
[A][B][C]を纏めてグループ:[G]に登録しておきます。そうすると他の人はABCの最後のシステムを把握せずともUpdateAfter(G)で良くなります。逆に「前」で実行する場合もUpdateBefore(G)で良くなります。

f:id:tsubaki_t1:20180904191658j:plain

gist.github.com上のコードを実行すると、ABCの前にX1、後にX2が実行されます。新しいシステムを追加しても、グループで囲ってる範囲に入り込むことはありません。
なお、Xをグループに紛れ込ませるとバグります。

f:id:tsubaki_t1:20180904185243j:plain

関連

Documentation for system update order · GitHub

【Unity】エディター操作でシーン間参照を実現する guid-based-reference

Unityでは複数のシーンを同時に編集することは可能ですが、シーン間の参照は出来ません。今回はソレをGUIDベースでなんとかするアプローチについてです。

シーン間参照(Multi Scene Reference)が出来ない

基本的にUnityのSceneというシステムは、別のシーンへのアクセスは出来ません。そのため、スクリプトを利用して参照先を取得します。例えば

  • 現在読込中のシーン全体にFindを実行
  • Staticなフィールドに格納
  • 特定のオブジェクトに登録し、マッチング
  • ユニークなアセットに格納

といった感じです。実際、様々なフレームワークは存在しますが、大抵の場合は上のどれかに行き着くという認識です。

 

シーン間参照がやりたくなるケース

ここまでして得たい他シーンの情報とは何でしょう。それが○○マネージャーといったオブジェクトを制御するユニークな存在であれば、staticなフィールドに突っ込んでも、まぁ何とかなります。

問題になるのが、シーンから別のシーンにアクセスするようなギミックの場合です。例えばScene1のライトをScene2で制御しようと思うと、これが非常に面倒くさい。
Staticのようにユニークなオブジェクトとして格納する訳にはいきません。

f:id:tsubaki_t1:20180904010641j:plain

こういった場合に強力な解決策になりそうなのが、Guid Based Referenceです。

 

GUID BASED REFERENCE

guid-based-referenceはオブジェクトのシーン間参照を実現するために便利なアセットです。Unity Companion Licenseで提供されています。

github.com

これは、任意のオブジェクトにGUID(Globally Unique Identifier:ユニークなID)を発行し、GUIDを基点にオブジェクトの参照を行うというアプローチです。インスタンスIDはちょくちょく変わるので、変更しないIDを追加で割り振る事で実現しています。
なんでFileIDじゃ駄目だったんでしょう。

ID管理はコンテンツカタログのように全IDを事前に保存しているタイプではなく、オブジェクトが生成されたタイミングで共有スペースに書き込むタイプです。
つまりロードされていないオブジェクトは参照出来ませんが、参照先と参照元のオブジェクトが揃えば利用可能になります。

f:id:tsubaki_t1:20180904013958g:plain

なおお察しの通り、この機能は「GUID Manager(シングルトン)に全部の参照を突っ込む」パターンです。

 

使い方

参照される対象となるGameObjectにGuidComponentを追加します。
これでオブジェクトにユニークなIDが割り振られます。

f:id:tsubaki_t1:20180904014448j:plain

参照する側のコードを用意します。今回はオブジェクトが接触したらアクティブになる…的なやつです。
同じシーンなら[SerializeField]GameObject target;と書く所ですが、[SerializeField] GuidReference target;と記述します。

gist.github.com

後はGuid Componentを追加したオブジェクトをドラッグ&ドロップしてやれば、オブジェクトが登録されてアクセス出来るようになります。
また設定時「どのシーンのオブジェクトを参照しているのか」が表示されているので、多少混乱を抑えられます。

f:id:tsubaki_t1:20180904015224g:plain

なお、取得できるのはGameObjectだけです。ジェネリックも使えないので、対象がその点に注意です。
また内部的にGameObjectをキャッシュしてくれてるのでGUIDで毎回検索する訳ではありませんが、取れるのがGameObjectのため必然的にGetComponent連打することになります。毎フレーム動かすような物はキャッシュしておくと色々と良いです。

 

 

関連

この機能を作るに当たって得られた幾つかのTipsが紹介されています。

blogs.unity3d.com似たような違う機能

tsubakit1.hateblo.jp共有するのがスコアの場合

tsubakit1.hateblo.jpオブジェクトへ直接参照するんじゃなくて、一旦ScriptableObjectを通せば…というアプローチ。

tsubakit1.hateblo.jp

baba-s.hatenablog.com

【Unity】Cinemachineの挙動を拡張してみる

 今回はCinemachineの挙動を拡張してみました。

 

 

Cinemachineの挙動を拡張

CinemachineはLookAtとFollow、カメラの向きと位置を各々の要素が補完してカメラワークを作ります。
この挙動は基本的な機能として存在する幾つかの項目(例えばComposerやTransposer)だけでなく、自分で拡張することも出来ます。
なので、今回はとあるついでに拡張してみました。

f:id:tsubaki_t1:20180901231001j:plain

 

 対象をX軸に追跡するだけのカメラ

カメラを横に振るだけのカメラを作ってみました。
Composerのように特定の位置にキャラクターを配置する等は出来ませんが、オフセットで角度を持って対処を追跡することは出来るようにしました。

gist.github.com

特にパッケージを改造する必要などは無く、CinemachineComponentBaseを継承したクラスをプロジェクト内に配置しておけば、自動的に一覧に組み込まれます。
その際、CinemachineCore.Stage Stageプロパティに何を指定するかで、BodyかAIMのどちらに指定するのかが変わります。

 

内容は割と単純で、MutateCameraStateでカメラの動作を指定しているだけです。最終的な位置に向けてRawPositionRawOrientationを設定すれば、よしなにやってくれます。場合によってはPositionCorrection等もどうぞ。
まぁ動けばいいやクォリティなので、実際間違いはあるかもしれませんが、自分の欲しい動きはしたので、まあいいやっていう。

Struct CameraState | Package Manager UI website

 

サンプルコードはPackageManagerにある

コレを実装する上で何をどう書いて良いのか分からんとなるので、コードを色々と参考にさせてもらいました。PackageManagerにあるやつです。

f:id:tsubaki_t1:20180903004534j:plain

PackagerManagerで取得したパッケージはProjectビューから確認することが出来るので、中身を色々と参考にさせてもらいました。
幾つかのパッケージ(例えばECS)はテストも付随してるので「この機能をどうやって使えば良いの?」や「どんな想定の機能なの?」といった疑問には、コレが結構役に立ちます。

ちなみにCinemachineにテストはありません。マア、モトモト販売シテタAssetダシネ

tsubakit1.hateblo.jp