【Unity】ECSで配列を格納する Dynamic Buffers
今回はECSで配列を使用する方法についてです。
ECSで配列を使用する
ECSに格納するComponentDataには配列を格納出来ません。とはいえ、マネージドなメモリも格納出来るSharedComponentDataは大量の種類を生成するとECSの効率が著しく下がるので余り良くありません。
とはいえ、 "接触したキャラクターの一覧"や"NavMeshのパス"など、連続した(可変の)データを必要とするケースはソコソコ多いです。
今回はDynamic Buffersを使用してこの問題を回避します。
下のような感じで特定のEntityが他のEntityを追跡する機能を作成してみました。中央のシリンダーが他の3つのEntityへの参照を保持していて、その参照経由でEntityへの線を引きます。
作ってみる
まず線を引く対象となるEntity群を用意します。コードで配置するのが面倒くさいのでハイブリットです。
GameObjectEntityとPositionComponentを用意します。
Transformの位置とPositionを同期したいのでCopyTransformFromGameObjectでPositionにTransformの座標を毎フレーム登録してもらいます。
今回はこれを青い枠で囲んだオブジェクト全てにセットしています。
線を引くためにEntityに他のEntityを登録するコードを用意します。
最初にバッファを定義します。IBufferElementData
を継承したstructに含めたい要素を登録します。今回は複数のEntityを登録することが目的なのでEntityです。
設定後はInternalBufferCapacity
で配列の長さを設定します。下の図の場合は配列に4つの要素が登録出来ます。
長ければ長いほど、チャンクに格納出来るEntityの数が減ります。オーバーしても良いですが、その場合はヒープメモリとして処理されるらしいです。
あとはバッファをEntityに追加して、要素を登録します。
アーキタイプから作っている場合はComponentTypeで一発で足せるのですが、ハイブリットはザクっと見た感じバッファを最初から持てなかったので、entityManager.AddBuffer
でバッファを追加しています。
追加後はバッファを取得し、中身を埋めていきます。
あとは線を引くシステムを用意します。
まずグループですが、普通にComponentType
が使えます。今回の場合EntityBuffer
を持つEntity一覧を取得しています。
次にグループからバッファを取得します。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の配列と解釈して処理させています。
あとは使用する部分ですが、まんま配列としてアクセス出来ています。
もしパラレルで並列処理したい場合は、ToNativeArray
でバッファをNativeArrayとしてアクセスするのが良いかもしれません。
今回の場合、別スレッドだとEntityから要素を取得できないので、ジョブは使用していません。
サンプル2
Dynamic Bufferに格納した複数の要素を、毎フレーム別スレッドで加算するだけ
関連
Dynamic Buffersの説明
https://github.com/Unity-Technologies/EntityComponentSystemSamples/blob/master/Documentation/reference/dynamic_buffers.mdgithub.com
*1:実験的な機能