【Unity,ECS】他のEntityが持つComponentDataを追跡する - ComponentDataFromEntity
今回もECSの話です。
ゲームでは特定のオブジェクトを追跡するというのはよくある話です。例えばカーソルやユニット、アイテムやパスなど。
ECSでも同様にそういった機構が必要になる事は多々あります。今回はEntityから特定のキャラクターを追跡させる方法についてです。
キャラクターを追跡する
キャラクターを追跡する…となると、MonoBehaviour
を使用するならば非常に簡単に実現が可能です。下のようなコードを書けば良いです。
ただしECSでは、こんな簡単に実現出来ません。ECSが使用するオブジェクトEntityは基本的に参照型ではなく、またポインター等でアドレスを取得してもComponentData
等の増減やSharedComponentData
の値変更等の理由により、アドレスが変化する可能性があります。
なのでComponentDataFromEntity
を使用して、Entityから対象の座標やパラメーターを追跡します。
ComponentDataFromEntityでEntityの持つComponentDataを参照する
ComponentDataFromEntity
は、全てのComponentData
の内から、特定のEntity
が持つComponentData
を取得するAPIです。
特定のEntityの方を向くシステムを作ってみます。
最初に特定のEntity
からPosition
のComponentData
を取得します。
[ReadOnly] public ComponentDataFromEntity<Position> positionFromEntity;
値を取得するには、下のように記述します。 なお対象のEntityがPositionを持っていない可能性もあるので、一応でEntityが無ければ処理をスキップします。
if (!positionFromEntity.Exists(target.Value)) return; var targetPos = positionFromEntity[target.Value];
ComponentDataFromEntity
は[ReadOnly]
なら並列でもアクセス出来るので、楽で良いです。
なお、ComponentDataFromEntityやポインタをキャッシュして云々は、基本的にランダムアクセスになります。ECSは連続したメモリアクセスによる高速化(事前にソートしたデータへ連続してアクセスし、同じ処理を繰り返し実行することによる高速化)なので、ランダムアクセスは出来れば避けたい所です。
ComponentDataFromEntityと同じ種類のComponentDataにアクセスする場合
厄介なのが、ジョブ上でComponentDataFromEntity
と同種のComponentData
にアクセスする場合です。
例えば、ComponentDataFromEntity
が取得するPosition
の情報をを元に、Position
を更新する…といった場合です。
ということで、キャラクターがシリンダーを追跡するコードを作ってみます。
まず、下のコードはエラーになります。 InvalidOperationException: The writable NativeArray LookTargetJob.Iterator is the same NativeArray as LookTargetJob.Data.positionFromEntity, two NativeArrays may not be the same (aliasing).
一旦EntityFromPosition
の情報をキャッシュしてから使用するように変更してみました。
ポインタを使用して直接アクセスするのも悪くなさそうですが、このアプローチならリニアなメモリアクセスが維持できるので良いんじゃないかなとコッソリ思っています。
(キャッシュミスしまくるComponentDataFromEntity
を事前に叩いておき、参照するデータを連続したメモリに格納、あとは参照先の値を使用した処理はシーケンシャルに処理する)
コピーのコストがあるので最適解かは微妙なところですが、まぁ一応こんな感じで回避出来るという事で。
また、一応EntityCommandBuffer.Concurrent
で一旦バッファに格納することでも回避が可能といえば可能です。
追記
一つしか存在しないようなデータ(例えばプレイヤー)の場合、System
にフィールド持たせてキャッシュさせるのはアリです。
System
は世界で一つであることが保証されているので、シングルトン的な感じでデータをキャッシュしたり出来ます。
ただ、ComonentGroup
がなければSystem
停止はしてくれないので、動作を安全にするためにSystem
が勝手に動かないようにOnUpdateを即リターンするなり、System
の生成を運用するなり工夫する必要はあります。