Prefabs in baking
During the baking process, prefabs are baked into entity prefabs. An entity prefab is an entity that has the following components:
- A prefab tag: Identifies the entity as a prefab and excludes them from queries by default.
- A
LinkedEntityGroup
buffer: Stores all children within the prefab in a flat list. For example, to quickly create the whole set of entities within a prefab without having to traverse the hierarchy.
You can use an entity prefabs in a similar way to GameObject prefabs, because they can be instantiated at runtime. However, to use them at runtime, you must bake the GameObject Prefabs and make them available in the entity scene.
Note
When prefab instances are present in the subscene hierarchy, baking treats them as normal GameObjects because they don't have the Prefab
or LinkedEntityGroup
components.
Create and register an Entity prefab
To ensure that prefabs are baked and available in the entity scene, you must register them to a baker. This makes sure that there is a dependency on the prefab object, and that the prefab is baked and receives the proper components. When you reference the entity prefab in a component, Unity serializes the content into the subscene that uses it.
public struct EntityPrefabComponent : IComponentData
{
public Entity Value;
}
public class GetPrefabAuthoring : MonoBehaviour
{
public GameObject Prefab;
}
public class GetPrefabBaker : Baker<GetPrefabAuthoring>
{
public override void Bake(GetPrefabAuthoring authoring)
{
// Register the Prefab in the Baker
var entityPrefab = GetEntity(authoring.Prefab, TransformUsageFlags.Dynamic);
// Add the Entity reference to a component for instantiation later
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent(entity, new EntityPrefabComponent() {Value = entityPrefab});
}
}
Alternatively, to reference the entity prefab during baking, use the EntityPrefabReference
struct. This serializes the ECS content of the prefab into a separate entity scene file that can be loaded at runtime before using the prefab. This prevents Unity from duplicating the entity prefab in every subscene that it's used in.
public struct EntityPrefabReferenceComponent : IComponentData
{
public EntityPrefabReference Value;
}
public class GetPrefabReferenceAuthoring : MonoBehaviour
{
public GameObject Prefab;
}
public class GetPrefabReferenceBaker : Baker<GetPrefabReferenceAuthoring>
{
public override void Bake(GetPrefabReferenceAuthoring authoring)
{
// Create an EntityPrefabReference from a GameObject. This will allow the
// serialization process to serialize the prefab in its own entity scene
// file instead of duplicating the prefab ECS content everywhere it is used
var entityPrefab = new EntityPrefabReference(authoring.Prefab);
var entity = GetEntity(TransformUsageFlags.Dynamic);
AddComponent(entity, new EntityPrefabReferenceComponent() {Value = entityPrefab});
}
}
Instantiate prefabs
To instantiate prefabs that are referenced in components, use an EntityManager
or an entity command buffer:
public partial struct InstantiatePrefabSystem : ISystem
{
public void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.Temp);
// Get all Entities that have the component with the Entity reference
foreach (var prefab in
SystemAPI.Query<RefRO<EntityPrefabComponent>>())
{
// Instantiate the prefab Entity
var instance = ecb.Instantiate(prefab.ValueRO.Value);
// Note: the returned instance is only relevant when used in the ECB
// as the entity is not created in the EntityManager until ECB.Playback
ecb.AddComponent<ComponentA>(instance);
}
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
}
Note
Instanced prefabs will contain a SceneSection component. This could affect the lifetime of the entity.
To instantiate a prefab referenced with EntityPrefabReference
, you must also add the RequestEntityPrefabLoaded
struct to the entity. This is because Unity needs to load the prefab before it can use it. RequestEntityPrefabLoaded
ensures that the prefab is loaded and the result is added to the PrefabLoadResult
component. Unity adds the PrefabLoadResult
component to the same entity that contains the RequestEntityPrefabLoaded
.
public partial struct InstantiatePrefabReferenceSystem : ISystem
{
public void OnStartRunning(ref SystemState state)
{
// Add the RequestEntityPrefabLoaded component to the Entities that have an
// EntityPrefabReference but not yet have the PrefabLoadResult
// (the PrefabLoadResult is added when the prefab is loaded)
// Note: it might take a few frames for the prefab to be loaded
var query = SystemAPI.QueryBuilder()
.WithAll<EntityPrefabComponent>()
.WithNone<PrefabLoadResult>().Build();
state.EntityManager.AddComponent<RequestEntityPrefabLoaded>(query);
}
public void OnUpdate(ref SystemState state)
{
var ecb = new EntityCommandBuffer(Allocator.Temp);
// For the Entities that have a PrefabLoadResult component (Unity has loaded
// the prefabs) get the loaded prefab from PrefabLoadResult and instantiate it
foreach (var (prefab, entity) in
SystemAPI.Query<RefRO<PrefabLoadResult>>().WithEntityAccess())
{
var instance = ecb.Instantiate(prefab.ValueRO.PrefabRoot);
// Remove both RequestEntityPrefabLoaded and PrefabLoadResult to prevent
// the prefab being loaded and instantiated multiple times, respectively
ecb.RemoveComponent<RequestEntityPrefabLoaded>(entity);
ecb.RemoveComponent<PrefabLoadResult>(entity);
}
ecb.Playback(state.EntityManager);
ecb.Dispose();
}
}
Prefabs in queries
By default, Unity excludes prefabs from queries. To include entity prefabs in queries, use the IncludePrefab
field in the query:
// This query will return all baked entities, including the prefab entities
var prefabQuery = SystemAPI.QueryBuilder()
.WithAll<BakedEntity>().WithOptions(EntityQueryOptions.IncludePrefab).Build();
Destroy prefab instances
To destroy a prefab instance, use an EntityManager
or an entity command buffer, in the same way that you destroy an entity. Also, destroying a prefab has structural change costs.
var ecb = new EntityCommandBuffer(Allocator.Temp);
foreach (var (component, entity) in
SystemAPI.Query<RefRO<RotationSpeed>>().WithEntityAccess())
{
if (component.ValueRO.RadiansPerSecond <= 0)
{
ecb.DestroyEntity(entity);
}
}
ecb.Playback(state.EntityManager);
ecb.Dispose();