テラシュールブログ

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

【Unity】ECSで配列を格納する Dynamic Buffers

今回はECSで配列を使用する方法についてです。

ECSで配列を使用する

ECSに格納するComponentDataには配列を格納出来ません。とはいえ、マネージドなメモリも格納出来るSharedComponentDataは大量の種類を生成するとECSの効率が著しく下がるので余り良くありません。
とはいえ、 "接触したキャラクターの一覧"や"NavMeshのパス"など、連続した(可変の)データを必要とするケースはソコソコ多いです。

今回はDynamic Buffersを使用してこの問題を回避します。
下のような感じで特定のEntityが他のEntityを追跡する機能を作成してみました。中央のシリンダーが他の3つのEntityへの参照を保持していて、その参照経由でEntityへの線を引きます。

f:id:tsubaki_t1:20181107223223g:plain

作ってみる

まず線を引く対象となるEntity群を用意します。コードで配置するのが面倒くさいのでハイブリットです。

GameObjectEntityとPositionComponentを用意します。 Transformの位置とPositionを同期したいのでCopyTransformFromGameObjectでPositionにTransformの座標を毎フレーム登録してもらいます。
今回はこれを青い枠で囲んだオブジェクト全てにセットしています。

f:id:tsubaki_t1:20181107224104j:plain

f:id:tsubaki_t1:20181107224627j:plain

線を引くためにEntityに他のEntityを登録するコードを用意します。

最初にバッファを定義します。IBufferElementDataを継承したstructに含めたい要素を登録します。今回は複数のEntityを登録することが目的なのでEntityです。
設定後はInternalBufferCapacityで配列の長さを設定します。下の図の場合は配列に4つの要素が登録出来ます。
長ければ長いほど、チャンクに格納出来るEntityの数が減ります。オーバーしても良いですが、その場合はヒープメモリとして処理されるらしいです。

f:id:tsubaki_t1:20181107231523j:plain

f:id:tsubaki_t1:20181107231651j:plain

あとはバッファをEntityに追加して、要素を登録します。

アーキタイプから作っている場合はComponentTypeで一発で足せるのですが、ハイブリットはザクっと見た感じバッファを最初から持てなかったので、entityManager.AddBuffer(entity)でバッファを追加しています。
追加後はバッファを取得し、中身を埋めていきます。

f:id:tsubaki_t1:20181107232215j:plain

gist.github.com

あとは線を引くシステムを用意します。

まずグループですが、普通にComponentTypeが使えます。今回の場合EntityBufferを持つEntity一覧を取得しています。

f:id:tsubaki_t1:20181107233217j:plain

次にグループからバッファを取得します。GetBufferArrayで取得するわけですが、Entity単位ではなくグループ単位で持ってきます。
例えばEntityBufferを持つEntityが2つある場合、下のように取得します。

buffer[0] { entity[0], entity[1], entity[2], entity[3] },
buffer[1] { entity[0], entity[1], entity[2], entity[3] },

Reinterpret()は、取得する要素を別の型へ解釈するという機能です*1。内容物が同じレイアウトなら別の型として使用することが出来ます。今回の場合、中身がEntityのみなのでEntityの配列と解釈して処理させています。

f:id:tsubaki_t1:20181107233942j:plain

あとは使用する部分ですが、まんま配列としてアクセス出来ています。 もしパラレルで並列処理したい場合は、ToNativeArrayでバッファをNativeArrayとしてアクセスするのが良いかもしれません。
今回の場合、別スレッドだとEntityから要素を取得できないので、ジョブは使用していません。

f:id:tsubaki_t1:20181107234010j:plain

gist.github.com

サンプル2

Dynamic Bufferに格納した複数の要素を、毎フレーム別スレッドで加算するだけ

gist.github.com

関連

Dynamic Buffersの説明

https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Documentation/reference/dynamic_buffers.mdgithub.com

*1:実験的な機能