テラシュールブログ

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

【Unity】物理演算を指定秒数 進める Physics.Simulate

f:id:tsubaki_t1:20180504185846g:plain

今回は「シーンの物理現象をシミュレートする」という、一見分かりにくいAPIPhysics.Simulateおよび Physics2D.Simulateついてです。

 

 

物理演算を指定秒数進めるAPI

このSimulate系のAPIは、簡単に言えば「物理演算を指定秒数、進める」事のできるAPIです。
通常は物理演算は「時間」によって進みますが、コレをスクリプトから超加速出来る訳です。

と言っても凄く癖のある機能で、

  • RigidbodyもしくはRigidbody2Dに動作する
  • FixedUpdateは呼ばれない
  • 1回に進められる時間はFixedDeltaTime(0.0167秒)辺りが限界*1
  • 他のRigidbody等の接触により結果が変わる事がある*2

という、中々に微妙な機能でもあります。

 

物理演算を進める

実際の操作を見てみます。

 

まずSimulate系のAPIですが、PhysicsのautoSimulateがOFFになっていることが前提条件です。ONだと動作しません。
まあ通常はInspectorよりはPhysics.autoSimulationで切り替えます。
コレを無効にすると、ゲームループ内で自動的にPhysics.Simulateが呼ばれなくなります。ただ MonoBehaviour .FixedUpdateは普通に呼ばれます。

f:id:tsubaki_t1:20180504190806j:plain

まずはAutoSimulationを手前でやってみます。
下のコードでは、コンポーネントが有効なときにのみ物理演算のシミュレートが進みます。

 

gist.github.com

f:id:tsubaki_t1:20180504191700g:plain

 

次にn倍速にしてみます。0.1から1.5倍速あたり。これは1回のFixedUpdateで進める時間を調整すれば実現出来ます。

gist.github.com

f:id:tsubaki_t1:20180504192251g:plain

これは所謂Time.Timescaleと異なり、時間軸自体は通常通り動く所が面白いところです。現象だけ遅くする事が出来るので、世界をゆっくりにして云々するのも面白いかもしれません。気分はカブト*3

ただコレは、移動も物理演算に頼っている場合は、少し面倒なことになるかもしれません。単純に時間を弄る系はTime.TimeScale弄ったほうが最終的に楽できます。

f:id:tsubaki_t1:20180504192949g:plain

 

物理演算を指定秒数進める・巻き戻す

さて、次は物理演算を指定秒数進めてみます。
時間指定はそれ程難しくはありません。指定時間に到達するまで秒数分ぶん回すだけです。パーティクルのPrewarmと同じようなものです。
強いて問題を上げるとすれば、高い負荷を計上する事があるという点ですが、まぁ。*4

gist.github.com

では巻き戻しは? …実は出来ません。
なので、「0秒の状態を保持しておいて、毎回0秒から指定秒まで演算を進める」という超力技で実現します。

下の2つは、このアプローチで巻き戻しを実現しています。

 


ただ、このときの動作が「他の物理演算の有無」により結構誤差が出ます。また負荷も半端ないので、実際にゲームで使う際にはTransform単位でキャプチャするほうが現実的かもしれません。

 

未来予測

最後に未来予測です。これも基本的には「巻き戻し」と同じ考え方です。
要するに、指定時間まで秒を進めて結果を回収する…という物です。

gist.github.com

f:id:tsubaki_t1:20180504200110g:plain

もっと軽くても良いよ!という場合は、下の設定にすると、大分負荷が減ります。
ただし計算精度も雑になります。まぁゲームのちょっとした弾道予想ではコチラの方が都合が良いかもしれません。

  • float deltaTime = Time.fixedDeltaTime を float deltaTime = Time.fixedDeltaTime * 10; に変更
    (計算制度を雑にする)
  • 予測したいRigidbodyのCollision DetectionをContinuous Dynamicに設定

f:id:tsubaki_t1:20180504200931j:plain

感想

中々に楽しい機能ではあるのですが、大体において「重い」です。複数フレームでやるような処理を1フレームでやってるので、仕方がないとも言えます。

 

個人的には、用途としては「弾道予想」と「エディターで物理演算をレコードする時に使う(GameObjectRecorder)」くらいかなと思ってます。

 

今回は3Dの紹介でしたが、2Dでも同じAPIがあります。動作も同じです

関連

普通に高校で習う内容で落下位置を予測しようぜ!という話。
複雑な地形や複数のオブジェクト間の接触、それの最適化等を考えるとしんどい

tsubakit1.hateblo.jp

たぶん見るであろう「壁貫通」について

tsubakit1.hateblo.jp

TImeScaleで行うスローモーション

tsubakit1.hateblo.jp

*1:マトモに演算結果を受けたければ

*2:ゲーム向け物理演算の限界

*3:クロックアップ

*4:パーティクルのPrewarmも同様に、モジュールにより高い負荷を計上することがある

【Unity】Unityでプレゼンテーション資料を作る

f:id:tsubaki_t1:20180503001307j:plain

今回はUnityでKeynotePowerpointにあるようなプレゼン資料を作るプロジェクトの紹介です。

 

 

もはやPowerPointに切り替える必要はない…

説明会や勉強会等で資料を作成すると、よく「KeynotePowerPoint)とデモ用の何か(自分の場合はUnityエディター)の切り替え」を行います。
これが中々の曲者で、一旦Keynoteをプレゼンモードから戻すと現在進行中の時間がリセットされ、のこり時間を見失う事があります。発表中で上がっている時や、時間がズレてる時は特に。

 

他にもPowerPointとUnityエディターを行ったり来たりすることで、誰かが何かの問題を起こすかもしれません。これはとても難しい問題です。

そこでvalyard氏は考えました。

 

Q:UnityエディターとPowerPointの切り替えで起こるトラブルを回避するには?

A:UnityがPowerPointになればいい

 

そうだ、Unityでプレゼン資料を作ろう

このブッ飛んだ考えで作られているのが、Presentation Project (Unity)です。
キャッチコピーはNo need to switch to Power Point anymore.

f:id:tsubaki_t1:20180502233205g:plain

github.com

考え方は単純で、UGUIのレイアウト機能を使えば資料ぐらい作れるよね!という物です。1シーンを1スライドと捉えて、それを切り替えることでプレゼン資料を実現しています。
このプロジェクトで提供されている物は、この「1スライド1シーン」を順番に切り替える&ロードする仕組みです。

f:id:tsubaki_t1:20180503000046g:plain

一応ビルドすることも出来るので、WebGLとかでも動くんじゃないですかね(多分

 

使い方を見てみる

とりあえず試しに使ってみました。

f:id:tsubaki_t1:20180503000140g:plain

 

Window > Presentationでメニューを開きます。

f:id:tsubaki_t1:20180502235358j:plain

あとは+でプレゼン資料のページを増やして、シーンを登録していきます。シーンは自分で普通のエディター操作で作ります。
何か最初から色々とセットされていたら、左上のNewボタンとか押しておけばOKです。

f:id:tsubaki_t1:20180502235436j:plain

あとは右上の>Bボタンを押せばプレビューが出来ますし、Stopボタンでプレビューを停止できます。
「再生」が入っていると、その資料を開くときにエディターがシーンを再生してしまうので、文字だけのページの場合は外しておくと良いかもしれません。

f:id:tsubaki_t1:20180502233928j:plain


発表者ノートは?プレゼンの残り時間の確認は?

ノーコメント。

 

その他

・・・確か自分が知ってる範囲で2件ほど同じ様な事をやってるプロジェクトがあった気がするけど思い出せない…

動かしながらプレゼン出来るのは面白いかも

【Unity】独自に作成したスレッドの処理時間をCPU Profiler(Timeline)で確認する

まんまタイトルの通りで、.NETで作成したスレッド上の処理時間をProfilerに乗せる方法についてです。

 

 

Profilerで負荷のかかっている場所を見つける

処理負荷や処理時間を知るのに、Profilerは非常に有用なツールです。
パレートの法則で言われるように、大抵の負荷は2割がボトルネックになっている事が多く、Profilerはその2割を見つけるのに有効です。

f:id:tsubaki_t1:20180430232723j:plain

さて、Profilerに処理時間を乗せたい訳ですが、特に何も設定しないならばMonoBehaviour.UpdateやECSのSystemといった単位でCPU Profilerに計上されます。
これでは「◯◯コンポーネントが遅い」事は解っても、何が問題とはハッキリ出来ません。

そこでProfiler.CustomSamplerで特定の処理時間をピックアップします。
サンプルとしてVector3を最後にApplyするだけのモノと、transformを取得してユニットを動かす場合、transformをキャッシュして行う場合の処理時間を見てみます。

gist.github.com

結果はProfilerのTimelineで確認できます。確認すると、Vector3だけ取り出しておいて最後に反映させるモノが、Transformに毎回数値を入れてるモノよりとても速い事が確認出来ます。
transformのposition等を上書きするだけでもソレナリのコストが発生していることです。あとgameObject.transformが内部的にキャッシュされるというのは迷信だと予想出来ます。

f:id:tsubaki_t1:20180501132033j:plain

ちなみにBeginSampleではなくCustomSamplerを使用しているのは、こちらのほうがオーバーヘッドが少なく、正確な値が取れるからです。

 

非同期処理とProfiler

負荷を計上するProfilerですが、実はスレッドが関わってくると途端に面倒になってきます。
C# Job SystemであればC# Job System側である程度の処理時間がProfilerのTimelineに出力されるので、そこまで致命的な問題にならないのですが、.NETの機能で作成したThreadはUnityのProfilerに計上されない為、どの程度の時間がかかっているのかが把握しにくくなっています。
またBeginSampleはメインスレッド上でしか動作しません。

 

この問題を解決するのが、Profiler.BeginThreadProfilingです。このAPIで囲った範囲のsampler.Begin~sampler.Endの範囲はProfilerのTimelineに乗るようになります。
例えば下のようなコードを記述すると、Profilerから処理時間が確認できます。

gist.github.com

f:id:tsubaki_t1:20180501135619j:plain

またsampler.Beginの引数にgameObjectをセットすれば、Profiler Timelineに計上される処理時間とHierarchy上のGameObjectを紐付ける事が出来ます。

 

複数フレームを跨げる

この処理時間は複数のフレームを跨いでもOKで、特にコルーチンのような遅延処理に対してThreadを使用する場合には有用そうです。
ただバージョンによっては載らない事もあるかもしれません。以前はsampler.Endまで到達した処理が無ければ載らなかった気がするので(Unity 2018.1f1で確認)

f:id:tsubaki_t1:20180501220611j:plain

逆にCPU ProfilerのHierarchyに処理時間が載らない点ので、その勘違いは気をつける必要があります。

 

関連

async/awaitのコードはココのを真似させてもらいました。

satoshi-maemoto.hatenablog.com

samplerについての説明。BeginSamplerは止めよう。
どちらかと言えばProfilerで見るような物をゲーム画面に乗せられる方が重要

tsubakit1.hateblo.jp

Profiler便利便利。

tsubakit1.hateblo.jp

【Unity】ECSを使用した大規模なデモプロジェクト "Unite Austin Technical Presentation"

f:id:tsubaki_t1:20180427001949j:plain

簡単なサンプルではなく、ECSを大規模に使用した技術デモ「Unite Austin Technical Presentation」の紹介です。

 

 

Unite Austin Technical Presentation

Unite Austin Technical Presentationは、Nordeus社が作成したゲームの一部プレビューです。内容は大量のミリオン同士の戦い…という感じです。

実際、数万体*1のミリオンがワールドの中央付近で戦闘を繰り広げています。

f:id:tsubaki_t1:20180427010109g:plain

また1キーや2キーでワールドの中身に介入することも出来ます。
例えばマウスの位置に隕石?を落下する攻撃を打ち込むと、大量のミリオンが一斉に吹き飛ぶのが確認出来ます。

プロジェクトは以下のGithubから入手出来ます。サイズがちょっと大きくて、3.3GBくらいあるので、モバイルWifiから落とす場合には注意が必要です。帯域制限を喰らいます(体験談

github.com

 

ユニットの動作や攻撃にはECSが使用されている

このゲームですが、キャラクターの移動や攻撃、キャラクター表現等、多くの部分にECSが使用されています。

キャラクター毎にEntityが割り当てられ、キャラクターの一体一体が各々で動いています。ミリオンだけでなく、弓の攻撃も一本一本がEntityです。

f:id:tsubaki_t1:20180427012516j:plain

ステージとの連携は、ふっ飛ばされた時などの物理挙動にはRaycastCommandで地面との相対距離等が求められていますが、移動はもっぱらNavMeshQueryでパスを取得してパスの通りに移動するというものみたいです。

 

逆に、判定やレンダリングなど、ユニットの動作に関する部分は多くの部分が自作コードになっていました。これは単純に用意されてないというのもありそうですが、幾つかは特別な最適化を施したいというのも理由の一つかもしれません。
例えば以下のような感じ

  • 当たり判定(判定の精度を下げたい)
  • レンダリング(DrawMeshIndirectで一括表示したい)
  • キャラクターのAI

f:id:tsubaki_t1:20180427010640j:plain

これらの処理は大まかな部分をSystemはJobSystemに移譲しており、ワーカースレッドはソレナリの範囲を埋めています。

特に長いのはMoveJobで、中でNavMeshQueryでパス計算とかしてます。

f:id:tsubaki_t1:20180427011010j:plain

 

キャラクターアニメーションは頂点シェーダー

さて、このプロジェクトは数万体という大量のユニットを同時に表現するためにDrawMeshIndirectでキャラクターを描画しています。
とは言え、DrawMeshIndirectで描画するものは基本的にアニメーションを使用できません。その辺り、このプロジェクトでは、頂点シェーダーでポリゴンを動かすことで実現しています。

f:id:tsubaki_t1:20180427015251g:plain

基本的には Animation Texture Baker と同じ系統の技術のようですが、テクスチャをベイクするというよりはバッファを云々してるように見えます。(ちゃんとコードを読んでいない)。このバッファを塗るのに、UnsafeUtilityもガンガン使うという中々に面白くないコードになっています。

 

 なお、何故かTextureAnimationSystemはComponentフォルダ以下にあります。多分ミスです。Systemフォルダに無かったので焦りました。

f:id:tsubaki_t1:20180427020546j:plain

 

状態の切り替えはComponentDataの追加で

ユニットには複数の状態が設定されています。例えば「移動中」とか「攻撃中」とか、あとは「死亡中」とか。

この状態の切替は、ComponentDataの抜き差しで行っていました。

f:id:tsubaki_t1:20180427025041p:plain

ComponentDataの抜き差し(AddComponent/RemoveComponent)は、ComponentDataが所属するチャンクを移動する事になるので余り効率的では無いように思ってたんですが、各動作毎に要求するデータが大きく違うので、その辺りを嫌がったのかもしれません。

まあチャンク間の移動といっても、数箇所の要素を塗り直すだけですし(新しくチャンクが作られなければ)案外問題は無いのかもしれません。

f:id:tsubaki_t1:20180427030732j:plain

 

ユニット情報はPrefabから取得

ECSを使用しているので殆どがコードで書かれるように思っていましたが、ユニット情報等はECSハイブリットの構造を使用して取得していました。
Prefab(GameObject)をInstantiateするのではなく、PrefabからEntityを作ってEntityをInstantiateする感じです。

これでコードを弄る事なく、複数パターンのEnittyを作成しています。

f:id:tsubaki_t1:20180427022246j:plain

このコードを見てEntityManagerのInstantiateにGameObject割り当てられる事に気づきました。

確かにこの使い方は理にかなってるように感じます。まぁ最終的にはScriptableObjectかEntityをシリアライズしたものに纏めてほしい気もします。

 

ユニットの生成位置はMonobehaviourから取得

ハイブリットと言えば、ユニットの生成位置等の情報はMonoBehaviourを継承したコンポーネントから取得していました。
出現位置などの情報はGameObjectでシーン上に配置(Gizmoでプレビュー)し、実際にUnitを生成するのはComponentSystemで行っている感じです。

f:id:tsubaki_t1:20180427032356j:plain

 

関連

このプロジェクトの紹介です

www.youtube.com

ECSとGameObjectのハイブリット

tsubakit1.hateblo.jpECSについて

tsubakit1.hateblo.jp

このプロジェクトのフォーラム

https://forum.unity.com/threads/unite-austin-technical-presentation.522628/

*1:ゲーム開始時は1万くらい、放置すると2万3万と増えていく