使用 Prefab 创建 Entity

在之前的例子当中,都是使用场景中已存在的 GameObject 将其通过 Baking 后转换为 Entity。但是一般在实际开发的时候,都是封装好了许多的 Prefab,因此就有了这个需求。

改造

  • 首先创建一个新的 SubScene 命名为 Spawner,并且将原来的 SubScene 的 Active 设置为 false。注意要将其中的 Execute 复制过来。并且将我们的 RotatingCube 做成 Prefab。
  • 接着在 Spawner 场景中创建一个空的 GameObject,挂载上我们的 SpawnerAuthoring.cs
  • 再创建一个 SpawnSystem.cs 用于真正的生产逻辑。
  • Execute 上面勾选 Prefabs 以及任意一个实现了选择的 System 即可。

SpawnerAuthoring.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace HelloCube.Prefabs
{
public class SpawnerAuthoring : MonoBehaviour
{
public GameObject Prefab;

class Baker : Baker<SpawnerAuthoring>
{
public override void Bake(SpawnerAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.None);
AddComponent(entity, new Spawner
{
Prefab = GetEntity(authoring.Prefab, TransformUsageFlags.None)
});
}
}
}

struct Spawner : IComponentData
{
public Entity Prefab;
}
}

此处对场景中每一个 entity,都加上了一个 Spawner 的组件,其中使用 GetEntity 去获取与之 GameObject 相关联的 Entity

SpawnSystem.cs

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
namespace HelloCube.Prefabs
{
public partial struct SpawnSystem : ISystem
{
private uint updateCounter;

[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<Spawner>();
state.RequireForUpdate<Execute.Prefabs>();
}

[BurstCompile]
public void OnUpdate(ref SystemState state)
{
// 创建一个 query 来查找所有含有 RotationSpeed 的 entity
// query 会被缓冲到 source generation,因此不会每次 update 都创建
var spinningCubeQuery = SystemAPI.QueryBuilder().WithAll<RotationSpeed>().Build();

if (spinningCubeQuery.IsEmpty)
{
var prefab = SystemAPI.GetSingleton<Spawner>().Prefab;
var instances = state.EntityManager.Instantiate(prefab, 500, Allocator.Temp);
var random = Random.CreateFromIndex(updateCounter++);

foreach (var entity in instances)
{
var transform = SystemAPI.GetComponentRW<LocalTransform>(entity);
transform.ValueRW.Position = (random.NextFloat3() - new float3(0.5f, 0.0f, 0.5f)) * 20;
}
}
}
}
}

  • 此处的 updateCounter 相当于作为随机数种子,确保每一次生成的位置不一样。
  • 接着通过 QueryBuilder() 返回的 SystemAPIQueryBuilder 查询场景中所有带有 RotationSpeed 组件的 entity。如果场景中没有,则进入下面的生成环节。
  • 接着使用 GetSingleton 获取我们 Spawner 场景中的 Spawner Object(只能有一个实例)。就拿到了其上面放置的 RotationCube 预制体。
  • 此处又使用 EntityManager 创造一个 NativeArray<Entity>。其中的 Allocator.Temp分配效率最高的分配器,但是其只是应该临时数据,只存在于当前帧,帧结束后销毁。其中 NativeArray<T> 是可以在托管代码(c#)和本地代码(c++或c)之间高效传递和共享数据。不需要复制或者序列化操作。
  • 最后再偏移一下生成的 entity 的中心即可。