Unity DOTS 入門 (12) - ECB
「ECB」を使って、「エンティティの作成」などJob処理中に実行できない命令を実行する方法を説明します。
・Unity 2019.3.14.f1
・Entities 0.11.1
1. ECB
「ECB」(Entity Command Buffer)は、「エンティティの作成」など、Job処理中に実行できない命令を、Job完了後にメインスレッドで実行するために保持するバッファです。
チャンク構造の変更が必要な命令は全て、Job処理中に実行できない命令になります。
2. ECBシステム
デフォルトワールドの3つの実行タイミングには、3つのシステムグループがあります。各システムグループ内の最初と最後には、「ECBシステム」(Entity Command Buffer System)があります。
「システム」はこの「ECBシステム」から「ECB」を取得し、そこにJob処理中に実行できない命令を追加します。
3. ECBで実行可能な命令
ECBで実行可能な命令は、EntityCommandBufferのメソッドに定義されています。
・AddBuffer<T>(Entity) : バッファの追加。
・AddComponent<T>(Entity, T) : コンポーネントの追加。
・AddComponent(EntityQuery, ComponentType) : コンポーネントの追加。
・AddSharedComponent<T>(Entity, T) : 共有コンポーネントの追加。
・AddSharedComponent<T>(EntityQuery, T) : 共有コンポーネントの追加。
・CreateEntity(EntityArchetype) : エンティティの生成。
・DestroyEntity(Entity) : エンティティの破棄。
・DestroyEntity(EntityQuery) : エンティティの破棄。
・RemoveComponent<T>(Entity) : コンポーネントの削除。
・RemoveComponent(Entity, ComponentType) : コンポーネントの削除。
・RemoveComponent(EntityQuery, ComponentType) : コンポーネントの削除。
・SetBuffer<T>(Entity) : バッファの更新。
・SetComponent<T>(Entity, T) : コンポーネントの更新。
・SetSharedComponent<T>(Entity, T) : 共有コンポーネントの更新。
・Instantiate(Entity) : インスタンスの生成。
・ToConcurrent() : 並列ECBに変換。
・Playback(EntityManager) : 命令の実行。
・Dispose() : 破棄。
4. DOTSパッケージのインストール
「Unity DOTS 入門 (1)」と同様。
5. GameObjectのプレハブの作成
立方体(Cube)の「GameObjectプレハブ」を作成します。
(1) Hierarchyウィンドウの「+ → 3D Object → Cube」で「Cube」を生成。(2) 「Cube」をProjectウィンドウにドラッグ&ドロップし、プレハブ「Cube」を生成。
(3) Hierarchyウィンドウの「Cube」を削除。
6. ECBの実装
スクリプト「Spawner」を作成し、「ECB」を使って「エンティティ」を生成します。
(1) Hierarchyウィンドウの「+ → Create Empty」で空のゲームオブジェクトを作成し、名前に「Spawner」を指定。
(2) Projectウィンドウにスクリプト「Spawner」を生成。
using Unity.Entities;
public struct Spawner : IComponentData
{
public Entity prefab;
}
(3) Projectウィンドウにスクリプト「SpawnerAuthoring」を生成。
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
[RequiresEntityConversion]
[ConverterVersion("joe", 1)]
public class SpawnerAuthoring : MonoBehaviour,
IDeclareReferencedPrefabs, IConvertGameObjectToEntity
{
public GameObject prefab; // GameObjectプレハブ
// Entityプレハブに変換するGameObjectプレハブを指定
public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
{
referencedPrefabs.Add(prefab);
}
// GameObjectをエンティティに変換
public void Convert(Entity entity, EntityManager entityManager,
GameObjectConversionSystem conversionSystem)
{
// Spawnerコンポーネントの追加
var spawner = new Spawner
{
prefab = conversionSystem.GetPrimaryEntity(prefab)
};
entityManager.AddComponentData(entity, spawner);
}
}
◎ IDeclareReferencedPrefabs
「IDeclareReferencedPrefabs」を継承し、DeclareReferencedPrefabs()でEntityプレハブに変換するGameObjectプレハブを指定することで、conversionSystem.GetPrimaryEntity()でEntityプレハブを取得できるようになります。
(4) 「Spawner」にスクリプト「SpawnerAuthoring」を追加し、「Prefab」に「Cube」をドラッグ&ドロップ。
(5) Projectウィンドウにスクリプト「SpawnerSystem」を生成。
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
[UpdateInGroup(typeof(SimulationSystemGroup))]
public class SpawnerSystem : SystemBase
{
// ECBシステム
BeginInitializationEntityCommandBufferSystem entityCommandBufferSystem;
protected override void OnCreate()
{
// ECBシステムの取得
entityCommandBufferSystem =
World.GetOrCreateSystem<BeginInitializationEntityCommandBufferSystem>();
}
protected override void OnUpdate()
{
// ECBを取得し、並列ECBに変換
var commandBuffer = entityCommandBufferSystem.CreateCommandBuffer().ToConcurrent();
// 並列Jobの実行
Entities
.WithBurst(FloatMode.Default, FloatPrecision.Standard, true)
.ForEach((Entity entity, int entityInQueryIndex, in Spawner spawnerFromEntity, in LocalToWorld location) =>
{
// エンティティの生成
var instance = commandBuffer.Instantiate(entityInQueryIndex, spawnerFromEntity.prefab);
// コンポーネントの設定
var position = new float3(0f, 2f, 0f);
commandBuffer.SetComponent(entityInQueryIndex, instance, new Translation {Value = position});
// エンティティの破棄
commandBuffer.DestroyEntity(entityInQueryIndex, entity);
}).ScheduleParallel();
// 指定したJob完了後にECBに登録した命令を実行
entityCommandBufferSystem.AddJobHandleForProducer(Dependency);
}
}
◎ ECBシステムの取得
「ECBシステム」を取得するには、World.GetOrCreateSystem<BeginInitializationEntityCommandBufferSystem>()を使います。
◎ ECBの取得
「ECB」を取得するには、entityCommandBufferSystem.CreateCommandBuffer()を使います。
◎ 並列ECB
「並列Job」(ScheduleParallel())から「ECB」を使用する場合は、ToConcurrent()を呼び出して、「並列ECB」に変換する必要があります。
var commandBuffer = entityCommandBufferSystem.CreateCommandBuffer().ToConcurrent();
さらに、命令の順番が分散方法に依存しないようにするため、entityQueryIndexを各命令に渡す必要があります。
// エンティティの生成
var instance = commandBuffer.Instantiate(entityInQueryIndex, spawnerFromEntity.prefab);
// コンポーネントの設定
var position = new float3(0f, 2f, 0f);
commandBuffer.SetComponent(entityInQueryIndex, instance, new Translation {Value = position});
// エンティティの破棄
commandBuffer.DestroyEntity(entityInQueryIndex, entity);
◎ 指定したJob完了後にECBに登録した命令を実行
指定したJob完了後にECBに登録した命令を実行します。Jobの指定は、を使います。
entityCommandBufferSystem.AddJobHandleForProducer(Dependency);
(6) 実行。
立方体が生成されることを確認できます。
この記事が気に入ったらサポートをしてみませんか?