Unity DOTS学习记录(六)-一些常用操作

环境:
Unity:6000.2.10f1
Entities:1.4.3

参考:
https://github.com/Unity-Technologies/EntityComponentSystemSamples

实例化预设Prefab

在DOTS中,实例化预设的Prefab通常通过Authoring组件和Baker来实现。以下是一个炮塔射击系统的示例,展示了如何在Baker中引用Prefab实体,并在Job中实例化它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class TurretRotationAuthoring : MonoBehaviour
{
public GameObject bulletPrefab;

class TurretRotationBaker : Baker<TurretRotationAuthoring>
{
public override void Bake(TurretRotationAuthoring authoring)
{
// 该实体将被移动
var entity = GetEntity(TransformUsageFlags.Dynamic);
// 为实体添加组件数据
AddComponent(entity, new TurretShootComponent()
{
BulletPrefab = GetEntity(authoring.bulletPrefab, TransformUsageFlags.Dynamic)
});
}
}
}

public struct TurretShootComponent : IComponentData
{
public Entity BulletPrefab;
}

public void OnUpdate(ref SystemState state)
{
var ecb =
SystemAPI
.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>()
.CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter();
state.Dependency = new SpawnJob
{
ECB = ecb
}.ScheduleParallel(state.Dependency);
}

[BurstCompile]
public partial struct TurretShootJob : IJobEntity
{
public float DeltaTime;
public EntityCommandBuffer.ParallelWriter ECB;

void Execute([EntityIndexInQuery] int sortKey, ref LocalTransform transform, ref TurretShootComponent shootComponent, Entity entity)
{
// ECB实例化实体
Entity bullet = ECB.Instantiate(sortKey, shootComponent.BulletPrefab);
// 给实体设置组件(假设component已定义)
ECB.SetComponent(sortKey, bullet, component);
}
}

实体/组件操作

主线程中:

  • 获取实体单例:

    1
    2
    3
    4
    Entity _entity;
    SystemAPI.TryGetSingletonEntity<CharacterAgentComponent>(out _entity);

    Entity e = SystemAPI.GetSingletonEntity<MyComponent>();
  • 获取实体上的组件:

    1
    LocalTransform playerTransform = SystemAPI.GetComponent<LocalTransform>(_entity);
  • 根据组件获取实体:

    1
    2
    3
    4
    5
    EntityQuery query = SystemAPI.QueryBuilder()
    .WithAll<MyComponent>()
    .Build();
    // 转化为列表
    using var entities = query.ToEntityArray(Allocator.Temp);

或者使用foreach:

1
2
3
4
5
6
foreach (var (comp, entity) in
SystemAPI.Query<RefRO<MyComponent>>()
.WithEntityAccess())
{
// entity 就是挂着 MyComponent 的实体
}

Job/子线程中

  • ECB操作

ECB(Entity Command Buffer)是ECS的事务系统(执行失败会报错,但不影响其他命令)。在Job中提交命令,在帧末统一执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Update中
void OnUpdate(ref SystemState state)
{
var ecb = SystemAPI
.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>()
.CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter();
new BulletMoveJob
{
DeltaTime = SystemAPI.Time.DeltaTime
}.ScheduleParallel();
}

[BurstCompile]
public partial struct BulletMoveJob : IJobEntity
{
public float DeltaTime;
public EntityCommandBuffer.ParallelWriter ECB;

void Execute([EntityIndexInQuery] int sortKey, ref LocalTransform transform, ref BulletMoveComponent moveComponent, Entity entity)
{
// ECB实例化实体
Entity bullet = ECB.Instantiate(sortKey, shoot.BulletPrefab);
// 给实体设置组件
ECB.SetComponent(sortKey, bullet, component);
// 添加组件
ECB.AddComponent(sortKey, entity, new Health { Value = 100 });
// 删除组件
ECB.RemoveComponent<DeadTag>(sortKey, entity);
// 销毁实体
ECB.DestroyEntity(sortKey, entity);
}
}

注意:ECB.AddComponent添加组件后,不能马上读到这个组件,需要等到下一帧。ECB是ECS的事务系统:Job里只提交命令,命令在当前帧的ECB System Playback时统一执行,Playback之后World立即改变,但只有在Playback之后运行的系统(通常是下一帧)才能观察到这些变化。

  • 获取实体上的组件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    void OnUpdate(ref SystemState state)
    {
    var ltLookup = SystemAPI.GetComponentLookup<LocalTransform>(true);
    // 将ltLookup传入Job中
    new TurretShootJob
    {
    ltLookup = SystemAPI.GetComponentLookup<LocalTransform>(true)
    }.ScheduleParallel();
    }

    [BurstCompile]
    public partial struct TurretShootJob : IJobEntity
    {
    [ReadOnly] public ComponentLookup<LocalTransform> ltLookup;

    void Execute([ChunkIndexInQuery] int sortKey, ref TurretShootComponent shoot, in LocalToWorld muzzleLtw)
    {
    // 判断entity有没有LocalTransform
    if (ltLookup.HasComponent(entity))
    {
    LocalTransform lt = ltLookup[entity];
    }
    }
    }