テラシュールブログ

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

【Unity】ECSでSystemの有効化・無効化についてのまとめ

ECSの基本はSystemです。システムというWorld毎にユニークなオブジェクトが多くのEntityを操作することでゲーム表現を実現しています。

ここで気になるのはSystemを動かす方法、及び止める方法についてです。今回はその辺りについて書いてみます。

 

 

Default WorldのSystemは自動的に登録される

まずECSのSystemですが、特に何も指定しなければ自動的に生成されるDefault Worldには全てのSystemがゲームに登録されます。

開発者は動作を確認するというフェーズではSystemの運用を特に気にせず機能と動作の開発に集中出来る感じです。
f:id:tsubaki_t1:20180929202848p:plain

 

自動的に登録したくない場合

ただし特定のシステムを最初から登録したくないといったケースもあります。そういった場合には[DisableAutoCreation]をシステムに設定しておくとSystemが自動的に生成されなくなります

 このアプローチは一つのWorldでゲームを作る場合に作りやすくなるかなと思います。

f:id:tsubaki_t1:20180929203121j:plain

一方、複数のWorldで運用が前提になってくるとDefault Worldの存在が少し厄介です。新しいWorldには全てのSystemが自動的に登録されていないので、自分でSystemを登録する必要が出てきます。そうすると、登録の要不要で一貫性がなくなります。

そういった場合、UNITY_DISABLE_AUTOMATIC_SYSTEM_BOOTSTRAPを使用することで、初期のWorld生成を抑制し、実装に一貫性を持てます。え?逆に全てのWorldに全Systemを登録すれば良い?それはDefaultWorldInitializationのアクセスが取れないから面倒かなぁ…

tsubakit1.hateblo.jp

Systemの明示的な作成

Systemは World.Active.GetOrCreateManager<EntityManager>()で明示的に作成できます。 World.Active.CreateManager<EntityManager>()もありますが、既にEntityが存在すると問題になるので、個人的には「明示的な作成」のような生成タイミングがフワっとしてるものにはオススメしません。(Worldの初期化時に作るんだ!!!等ならCreateManagerでも)

 生成時にはOnCreateManager、削除時にはOnDestroyManagerのコールバックが呼ばれます。NativeArrayを作る時はココでやるのが良さそうな感じがします。
コンストラクタも使えますが引数を持っている場合、World.Active.CreateManager以外のタイミングで生成されると破綻するので余り良い選択肢ではない印象です。(個人で作ってるなら問題ないでしょうが…)

f:id:tsubaki_t1:20180929210857j:plain

作成後はScriptBehaviourUpdateOrder.UpdatePlayerLoop(World.Active);を呼び出します。これをしないとOnUpdateは動作しません。削除後も同様で、忘れると下のようなエラーが出続けます。

 NullReferenceException: Object reference not set to an instance of an object
Unity.Entities.ComponentSystemBase.BeforeUpdateVersioning () (at Library/PackageCache/com.unity.entities@0.0.12-preview.16/Unity.Entities/ComponentSystem.cs:158)
Unity.Entities.ComponentSystem.BeforeOnUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.16/Unity.Entities/ComponentSystem.cs:315)
Unity.Entities.ComponentSystem.InternalUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.16/Unity.Entities/ComponentSystem.cs:355)
Unity.Entities.ScriptBehaviourManager.Update () (at Library/PackageCache/com.unity.entities@0.0.12-preview.16/Unity.Entities/ScriptBehaviourManager.cs:77)
Unity.Entities.ScriptBehaviourUpdateOrder+DummyDelagateWrapper.TriggerUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.16/Unity.Entities/ScriptBehaviourUpdateOrder.cs:703)

また[Inject]で特定のシステムを参照した場合も自動的に生成されるみたいです。こちらはScriptBehaviourUpdateOrder.UpdatePlayerLoopを実行しなくても自動的に登録されます。

 

Systemの明示的な有効化・無効化

Systemを明示的に有効化・もしくは無効化します。
やり方は単純で、WorldからGetExistingManagerでSystemを取得して、Enabledtrueもしくはfalseにするだけです。

なおMonobehaviourではなくComponentSystemから取得する場合、World.GetExistingManager<System名>()で同じWorldのシステムを取得出来ます。まぁソレ以前に[Inject]System名 sys;で呼べるんですが

f:id:tsubaki_t1:20180929211751j:plain

このアプローチで有効化・無効化を設定した場合、Entity Debuggerでチェックボックスが外れます。また有効化・無効化時にOnStartRunningOnStopRunningのコールバックが呼ばれます。

f:id:tsubaki_t1:20180929211907j:plain

 

アクセスするEntityが無いSystemは動作しない

小まめにSystemをON/OFFしたいところですが、それ程頑張らなくても良いという意見もあります。というのも、Systemは要求するGroupが存在しない場合は動作が停止し、OnUpdate等で呼ばれる処理は呼ばれなくなります。

f:id:tsubaki_t1:20180929203542j:plain

 なお動作しないといってもSystemの数だけ呼び出し処理は走ります(OnUpdateが呼ばれる訳ではありません)。一つ一つは本当に0ms~と殆ど負荷にならないと思いますが、量が増えてくると負荷になるかもしれません。ただコレに目を向けるのは早すぎる最適化な感じがします。

常に動かしたい場合

少々厄介なのがこのSystemが複数のGroupを要求した場合の判定はORなので、どれか一つでも条件が揃えば呼ばれてしまう点です。Systemが2つのGroupを要求していた場合、どちらかが揃えばSystemは動作してしまいます。

またSystemが何も要求していない場合も、自動的に動作するようになっています。
下のコードは配置しておけば常にDo it!!!と叫ぶ迷惑なコードです。

f:id:tsubaki_t1:20180929204328j:plain

Groupが何らかコンポーネントを要求しつつも常に動かしたい…という場合にはAlwaysUpdateSystemを使用します。当然要求したグループが空の可能性もあるので、そこは自分でなんとかする必要があります。

f:id:tsubaki_t1:20180929205112j:plain