テラシュールブログ

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

【Unity】ECSの新しいAPI「Entities.ForEach」について

f:id:tsubaki_t1:20190208232119j:plain

ECSで[Inject]属性が非推奨になり、新しいAPIが追加されました。

https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Documentation%7E/ecs_best_practices.md

個人的に、多少パフォーマンスを犠牲にして簡単になるなら、プロダクトとして妥当*1だと思っていたので若干ガッカリですが、新しいAPI群はInjectよりスッキリしているので好印象です。

ForEachでComponentDataを処理する

メインスレッドを使用するComponentSystemの場合、ForEachを使用して処理を記述出来ます。これはC#の言語的なforeachではなく、ComponentSystem専用に拡張したAPIです。

ForEach( operation );という感じの書式で、operationはEntityDynamicBufferIComponentDataISharedComponentDataや普通のComponentが使用できます。refキーワードを使うのはIComponentDataだけの点に注意です。また、使用できるComponentDataの数は大体5個までです。なおEntityDynamicBufferは要素の先頭というルールがあります。

下のような形で記述出来ます。構造体を用意する必要のあった[Inject]構文と比較して、かなりスッキリした印象です。

gist.github.com

ラムダ式を使ってるならGCが発生するんじゃない?

コードを追ってみた所、ラムダ式の部分はIL2CPPでビルドする際にキャッシュするコードが挿入されており、GCは発生しませんでした。

ただForEach外の値を参照するなら別で、newが走ります。

www.f-sp.com

そういう場合は、ToComponentDataArrayCopyFromComponentDataArrayを使うのが良さそうです。

tsubakit1.hateblo.jp

ComponentGroupで操作対象を予め絞り込む

要素が最大5つなので絞り込みに不安があるところですが、有り難い事にComponentGroupで事前に操作対象を絞り込んでおき、その中から特定のComponentDataにアクセスするといった事が可能になっています。これは「”命令を組み合わせた物を繰り返して計算"するより”命令を繰り返し実行したものを組み合わせた方が良い"」に合わせやすくて良いです。

gist.github.com

なおForEach内ではEntityの追加・削除は出来ません。一旦ForEachの外に出して一気に処理するのが良いです。

InvalidOperationException: EntityManager.AddComponent/RemoveComponent/CreateEntity/DestroyEntity are not allowed during ForEach. Please use PostUpdateCommandBuffer to delay applying those changes until after ForEach.

gist.github.com

IJobProcessComponentDataにもScheduleGroup( ComponentGroup, JobHandle );が追加され、要素の絞り込みが可能になったっぽいです。EntityDebuggerではバグって絞り込み出来ないような表示になりますが、実際の動きではComponentGroupで一旦絞り込みがされた物だけが対象になります。

もうこれRequireComponentTag無くても何とかなるんじゃねと思うんですが、どうなんでしょう。

JobSystem側は、IJobProcessComponentDataとIJobChunkを使う

C# Job System側はと言うと、IJobProcessComponentDataIJobChunkが推奨されています。

それでも十分でないケースでは、ToComponentDataArrayで一覧取得、それも無理ならCreateArchetypeChunkArrayでChunk単位でコントロールするという感じみたいです。

感想

[Inject]やComponentArrayを使わずとも、概ね同じような機能を非常にスッキリとした形で記述出来ました。

…ただ、「ForEach」は別名にしてほしかった感

補足

Unity 2018.3系で新しいECSを使用する場合「"com.unity.test-framework.performance": "0.1.49-preview",」が要ります

関連

tsubakit1.hateblo.jp

*1:逆に、難しいが効率的…というプロダクトは、他と比較して余程のメリットが無ければ大体死ぬ