テラシュールブログ

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

【Unity】Progressive LightmapperのGPU accelerationでライトマップのベイクが爆速に

f:id:tsubaki_t1:20180928225255j:plain

Unity 2018.3b3からProgressive LightmapperのベイクにGPU accelerationが使用できるようになりました(Windowsのみ)

ので実際に使ってみました。

www.youtube.com

 

使い方

特に難しいこともなく、ライトマッパーをProgressive GPU(Preview)に変更するだけです。後はベイクすればライトマップが焼けます。

f:id:tsubaki_t1:20180928230951j:plain

ベイク時間がかなり短縮

実際にSetalthのステージをベイクして速度を比較した所、かなりの差が出ました。

 

最初はどちらも45分と出るのですが、Progressive GPU は一気に処時間が小さくなっていき、気づいたら終了という感じです。
ライトマップをベイクする為の準備は同程度の手間が発生するので、事前準備の割合が大きいステージ…例えば小さいステージで膨大な量のマテリアルとメッシュを使ってるステージ等(CADでよくある)はそこまで速度を体感できないかもしれません(未検証です。実際には問題ないかもしれません)

 

現状の制約

ただPreviewなだけあり、現状幾つか制約があります。

まずShadowMaskやOnlyIndirectが使えません。完全にライトマップをベイクするせっていのみの扱いとなります。

現状Windowsのみの機能です。MacLinuxは2019.1から

【Unity】Cinemachineで複数のカメラを使用する

f:id:tsubaki_t1:20180923231727g:plain

今回はCinemachineで複数のカメラを使用する方法についてです。

 

 

複数のカメラを使う

ゲームを作成している時、複数のカメラを使用したいというケースが稀に存在します。例えば画面分割2Pプレイダンスシーンの背景カメラなど。

f:id:tsubaki_t1:20180923222302j:plain

普通はCameraを複数持てば良い話です。ただしCinemachineを使用する場合に少し考える必要があります。
というのもCinemachineはCinemachine Brainは最も優先度の高いVirtual Cameraを追跡する仕組みの為、複数のCinemachineBrainを用意すると全て同じVirtual Cameraを追跡してしまいます。

 

今回はその解決法についてです。

 

Timelineを使用している場合、複数のTrackを用意する

それがTimelineを使用している場合、複数のVirtual Cameraを使用するのは非常に単純、TimelineのTrackを複数用意すれば良いです。

f:id:tsubaki_t1:20180923223852j:plain

 

Timelineを使用しない場合

Timelineを使用しない場合、非常に面倒な手間が必要です。

  1. 同時に使用するCameraの数だけレイヤーを用意
  2. VirtualCameraのレイヤーを設定
    使用するレイヤーはカメラ毎にユニークな物を使用する
  3. CameraのCullingMaskでVirtualCameraが持っているレイヤーを指定
  4. 2と3をカメラの数だけ繰り返す

f:id:tsubaki_t1:20180923224614j:plain

f:id:tsubaki_t1:20180923224848j:plain

正直な所、この制御のためだけに31個しか使えないレイヤーを使うのはナンセンスな印象があります。多分開発者的には「映す対象とVirtualCameraが一致する」的な考えなんじゃないかなーと予想してますが…うーん?

 

なおCan I use 2 camera with Cinemachine Brain?にてCinemachineBrain.SetCameraOverride() を使用して独自にCinemachineBrainのアクセス先をオーバーライドする方法が紹介されていますが、APIがInternalなので使えません。
使えたとしても追加でカメラを制御するコードを記述する必要があります。正直これならCinemachineBrainのCullingMask使っている所を書き換えた方が楽かなという印象です。

【Unity】ECSで複数のWorldを運用する方法

「デモとしてのECS」が欲しいなら使いませんが「ゲームとしてのECS」が必要なら知っておくと良い、複数のWorld運用についてです。

 

 

ECSを”ゲーム”にするためには

ECSをヘビーに使用する時に気になってくるのが「特定の状態で不要になるSystem」の存在です。

デモであればSystemを止める事は不要です。始まりも終わりもなく単純に続けるシステムです、なんにも問題はありません。何故なら全て必要なSystemのみが動作しているからです
ですが「ゲーム」ならば、Systemが動きっぱなしというのは少し面倒な問題です。大抵の場合、不要なSystemが登場します
例えばシューティングゲームに必要なPositionの更新やHitcheckといったSystemはゲーム以外の状態…例えば結果画面やタイトル、ポーズ、会話といったシーンでは不要でしょう。

ここでのSystemを止めるアイディアは3つあります。

  • 複数のWorldを運用し、Worldを切り替えて運用する
  • ComponentSystemを取得して積極的にOFFにする
  • Systemが要求する全てのEntityを削除する

ある意味どれでも良いと思いますが、今回は複数のWorldで運用する方法について考えてみます。

f:id:tsubaki_t1:20180923022709p:plain

 

複数のWorldを管理する

複数のWorldで運用するのは、個人的には悪くないアイディアのように考えています。

  • 有効・無効を大きな単位で行える
  • 生成・破棄を裏側で行える
  • オブジェクトを維持したまま処理だけを止められる

まずWorldという単位で処理の有効化・無効化が行えるので、そのWorldでどのような処理が行われているのかを把握せず大きなWorld単位でON/OFFが実行できます
これは「Game World」や「Menu World」のような単位で区切っておけば、メニュー中にゲームを簡単に止められる事を意味します。

オブジェクトの生成・破棄をバックスレッドで実行できる点も魅力的です。同じスレッドだと同期ポイントが必要になるのですが、Worldを分割しておくことで現在有効ではないWorldならスレッドから操作するといったアイディアが出てきます。

またSceneのように読込-破棄を選択するのではなく、Entityを維持したまま処理のみを停止出来るのも魅力的です。例えばゲーム中にメニュー画面を開いても、ゲームの内容は維持されていますが、ゲームの進行は停止するといった事ができそうです。

f:id:tsubaki_t1:20180923024530p:plain

tsubakit1.hateblo.jp

 

複数のWorldは動かせなくなりました。

なおWorldは複数同時に動かす事も可能です。
下のGifアニメでは、赤いコインを作成したWorldと白いコインを作成したWorldを作成して、各Worldを有効・無効を切り替えています。

 

Worldを作成する

まずは独自のWorldを作成します。

Worldはnew World("World Name");で作成できます。
なお独自に作成したWorldには一切のSystemが登録されていないので、world.CreateManager<ComponentSystem>();で必要なシステムは独自に登録する必要があります。

gist.github.com 

動かすWorldを切り替える

次に複数作成したWorldを切り替えます。
正確にはWorldを有効化するという方が動作に沿っています。

ScriptBehaviourUpdateOrder.UpdatePlayerLoopを使用すると、指定したWorldのみが有効化します。逆を言えば、指定していないWorldの持つ全てのSystemは動作を停止します。

gist.github.com

Tips

World.Active

WorldにはActiveというAPIがあります。
これは特別な意味はない単なるプロパティです。そのため、ココにWorldをセットする事に意味はそれ程ありません。

強いて言うならSceneのActiveと同じような扱いになりそうです。つまりGameObjectを作成したらActiveなシーンに生成されるように、アクティブなシーンに対して色々と行う…という感じ。

Worldの運用によっては、メインのゲームとなるWorldは常に一つということも考えられるので(Worldを跨いだEntityのやりとりが出来ないので、メインとなるWorldへ追加したEntityをマージしていく)、こういった設計も有りかなという感じです。

 

Default Worldを生成しない方法

コレを使っていると気になるのが、最初から用意されているWorldです。
最初から用意されているWorldは基本的に全てのSystemが最初から登録されています。処理の一貫性を保つ為には、コレは余計なお世話です。

Default Worldを作成しないようにするには、Scripting Define Symbolsに UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAPを定義しておきます。

f:id:tsubaki_t1:20180923024034j:plain

初期化を防止するコードはAutomaticWorldBoostrap(PackageManagerから参照)にて確認出来ます。
Worldの破棄はWorld.DisposeAllWorlds();でも可能ですが、シンボルで事前に作成を抑制したほうが正しい印象です。

f:id:tsubaki_t1:20180923023831j:plain

Default Worldは「全てのSystemを登録」してしまいます。ちゃんとInjectで注入する形で設計し、注入対象のEntityが無ければそこまでは問題にはならないと思います。
ですがInjectを行わないタイプ(GetEntity等)のSystemや、Systemを別のアプローチで動かす(RXやAsync等)場合には注意が必要かもしれません。

 

EntityはWorldを跨がず、SystemはWorld毎に作成

Worldを複数作成した時に気になるポイントは、別のWorldからEntityへアクセスすることが出来ないという点です。つまり、異世界転生(World間の移動)は可能ですが、異世界通信は不可能という話。

f:id:tsubaki_t1:20180923031357p:plain

ただしSystem間の場合は普通にアクセスできそうです。流石にInjectは使えませんが、CreateManagerしてるなら普通にイベント登録するなり色々と。
ECSのSystemは普通にオブジェクト指向です。

 

Systemの停止処理をはさみたい場合は注意

なお、今回の方法で止まるのはOnUpdate等の処理です。
OnStopRunningOnStartRunning等のコールバックが呼ばれる訳ではない点に注意です。

またOnUpdateで駆動してない場合もあります。
例えばMeshInstanceRendererSystemとかはRenderPipeline.beginCameraRenderingCamera.onPreCull側にイベント登録して呼び出しを行っているので、Worldを止めても無効化されません。

 明確に停止したい場合は、World.GetExistingManagerでSystemを取得して停止するのが良さそうです。

 

関連

ECSで移動するビルボード風スプライトを同時に114514体表示する

【Unity】Entity Component System入門(その1)【2018.2】

【Unity】ECS + JobSystemでライフゲームを実装してみた

【Unity】Unity 2018.3でTimelineの座標系が少し分かりやすくなった

UnityのTimelineは座標系に謎の挙動がありましたが、Unity2018.3でもう少し納得できる感じに修正されました。

 

 

 

Unity 2018.3以前のTimelineの座標系

Timelineの座標ですが、Unity2018.3より前はAnimatoinControllerの有無で挙動が変わるという、非常に面倒くさい仕様でした。
AnimationTrackで操作する場合、AnimationControllerが無い場合は親オブジェクトからの相対座標ですが、有る場合は現在座標を開始地点とした相対距離を移動します。

https://cdn-ak.f.st-hatena.com/images/fotolife/t/tsubaki_t1/20180514/20180514230903.gif

これがAnimationControllerの有無で変わる為に無駄な混乱が有りました。
例えばゲーム演出としてのカットシーンを使用したい場合、AnimationControllerを外さないと現在地からの相対移動でスタートすることになり座標が狂います。かといって座標をTimeline開始点に合わせて調整するのは余りにも無駄な操作です。

tsubakit1.hateblo.jpそれだけでも意味不明ですが、この挙動はゲーム再生時のみのためゲーム再生とTimeline再生で動作が異なります。

そんな感じで、Timelineは結構面倒な機能になっていました。

 

変化したTimelineの座標系

 Unity 2018.3からAnimationTrackに3つのオプションが追加されました。

  • ApplyTransformOffset
  • ApplySceneOffset
  • Auto

この設定により座標の設定が若干異なることになります。

f:id:tsubaki_t1:20180914233322p:plain

ApplyTransformOffsetはカットシーン向け

ApplyTransformOffsetはキャラクターの現在の座標を無視して、親GameObjectからの相対距離で移動します。
親オブジェクトの座標に従う為、座標は常に期待通りの位置で終わります。逆に、その場でアニメーションさせたい場合には親オブジェクトを用意して実行という無駄な手間が発生します。
f:id:tsubaki_t1:20180914225131j:plain

f:id:tsubaki_t1:20180914232327g:plain


開始点を少しずらしたい場合はオフセットで位置や回転を調整して、アニメーションをうまい感じに調整していきます。

ApplySceneOffset

ApplySceneOffsetの場合、Timeline開始時の座標を基準としてアニメーションを開始します。

f:id:tsubaki_t1:20180914232733j:plain

f:id:tsubaki_t1:20180914232924g:plain

なお、この状態でアニメーションを編集すると挙動がかなりおかしくなります。基本的にアニメーションを編集する際にはTransformOffsetにするのが良さそうです。

 

Auto

AnimationControllerの有無で動作が変わります。
Uniy 2018.3以前の動作との互換性用です。

 

私が本当に欲しかったもの

 PlayableDirectorとの相対距離で作ってほしかった私です。
まぁ全オブジェクトをPlayableDirectorの子にして、TransformOffsetで再生すれば良い話ではあるんですが、親子関係は色々な動作に強烈に関わるからできるだけ動かしたくないっていう

 

tsubakit1.hateblo.jp

何にしても、余計なお世話が消えて大分使いやすくなりました。