【Unity】ECSでComponentGroup内のComponentDataを取得する新API、ToComponentDataArrayとCopyFromComponentDataArray
ComponentDataArray(型)
が非推奨になり、代わりにToComponentDataArray(...)
とCopyFromComponentDataArray()
が追加されました。
これは今までのComponentDataArrayと異なり、NativeArrayを取得します。
ToComponentDataArrayとCopyFromComponentDataArrayはChunkIterationの面倒な記述をスッキリ書ける
このAPIでは、今までComponentDataArrayで取得していたような、ComponentGroup全体が要求するComponentDataの配列を取得できます。
機能は2つで、ComponentGroup.ToComponentDataArray()
でComponentGroupが持つComponentData一覧をNativeArrayにコピーして使用できるようようします。また、ComponentGroup.CopyFromComponentDataArray()
で変更を反映します。
public NativeArray<T> ToComponentDataArray<T>(Allocator allocator, out JobHandle jobhandle) where T : struct,IComponentData {...} public void CopyFromComponentDataArray<T>(NativeArray<T> componentDataArray, out JobHandle jobhandle) where T : struct,IComponentData {...}
内部的にはGatherComponentDataJob<T> : IJobChunk
で全Chunkが入るバッファを1回で作り、中身を一気に埋めていく感じです。チャンクがそれ程多くないなら、殆ど一回のMemcopyで終わるので効率的に見えます。ChunkIterationでやるような面倒くさい記述を割とスッキリとしてくれるので、割とありがたいです。
ForEachで面倒くさい(例えば複数のGroupを参照するような)ケースでは、こちらが便利そうです。
ということで、使い方を見ていきます。
まずは普通にComponentDataのNativeArrayを取得→反映までをメインスレッドのみで
普通にComponentGroupの要求を定義します。
// コンポーネントの準備
playerGroup = GetComponentGroup(
ComponentType.ReadOnly<Position>(),
ComponentType.ReadOnly<Player>(),
ComponentType.Create<HitPoint>());
次にComponentDataを取得します。
ComponentGroup.ToComponentDataArray<T>(Allocator.TempJob);
を使用します。
この時Allocatorを設定するのですが、内部的にコピー作業にジョブを使用しているので、実質TempJob
もしくはPersistent
の二択です。(もしくはTempJob 1択?)
なお内部的にジョブ(&Burst)を使用していますが、即座にCompleteしているので普通にメインスレッド(ComponentSystem)で使って問題ありません。
protected override void OnUpdate() { // チャンクからNativeArrayを取得 var playerPositions = playerGroup.ToComponentDataArray<Position>(Allocator.TempJob); var playerHitpoint = playerGroup.ToComponentDataArray<HitPoint>(Allocator.TempJob); var enemyPositions = enemyGroup.ToComponentDataArray<Position>(Allocator.TempJob);
これで取得したComponentDataのArrayを使用して、色々と演算出来ます。NativeArrayなのでジョブに投げる事も出来ます。
ただし演算に使用するデータはあくまでコピーされたデータなので、もし変更していた場合は設定を反映させる必要があります。
演算結果をComponentGroupに書き戻します。
下記戻す時にはComponentGroup.CopyFromComponentDataArray(...)
を使用します。
// ダメージを反映
playerGroup.CopyFromComponentDataArray(playerHitpoint);
ジョブ対応する
ToComponentDataArrayとCopyFromComponentDataArrayは内部的にはIJobChunkのジョブを使用しています。この時JobHandleを渡してやれば、CompleteのタイミングをoutしてきたJobHandleにまかせてくれます。
少し厄介だったのが、JobHandleを受け渡すのが今までのIJob系と若干異なっていた点です。JobではSchedule時に渡しますが、今回はComponentGroup.AddDependency
経由で渡します。
protected override JobHandle OnUpdate(JobHandle inputDeps) { // チャンクからNativeArrayを取得 playerGroup.AddDependency(inputDeps); var playerPositions = playerGroup.ToComponentDataArray<Position>(Allocator.TempJob, out JobHandle handle1); var playerHitpoint = playerGroup.ToComponentDataArray<HitPoint>(Allocator.TempJob, out JobHandle handle2); var enemyPositions = enemyGroup.ToComponentDataArray<Position>(Allocator.TempJob, out JobHandle handle3); jobhandle = JobHandle.CombineDependencies(handle1, handle2, handle3);
値の反映もジョブ経由です。こちらもAddDependency経由で渡します。正直なんでやねん感はありますが、何故かそうなっています。
// ダメージを反映 playerGroup.AddDependency(jobhandle); playerGroup.CopyFromComponentDataArray(playerHitpoint, out JobHandle handle4);
ジョブを使わない方の実装例
ジョブを使う方の実装例
ハイブリットで使いたい場合
ToComponentDataArray
はGetComponentDataArray(旧)
と異なりコンポーネント(e.g. Transform)を取得出来ません。
そういうのはGetComponentArray
なりGetGameObjectArray
なり、ForEach
でやります。(どうせメインスレッドでしか動きませんし)
感想
またAPIが紛らわしいんだよオラァ!!
低レイヤーに直接アクセスを要求するようなAPIが減ってくれて嬉しい限り