Chunk component data
Use chunk components to associate data with a specific chunk.
Chunk components contain data that applies to all entities in a specific chunk. For example, if you have chunks of entities that represent 3D objects that are organized by proximity, you can use a chunk component to store a collective bounding box for them. chunk components use the interface type IComponentData.
Add and set the values of a chunk component
Although chunk components can have values unique to an individual chunk, they are still part of the archetype of the entities in the chunk. Therefore, if you remove a chunk component from an entity, ECS moves that entity to a different chunk (possibly a new one). Likewise, if you add a chunk component to an entity, ECS moves that entity to a different chunk because its archetype changes; the addition of the chunk component does not affect the remaining entities in the original chunk.
If you use an entity in a chunk to change the value of a chunk component, it changes the value of the chunk component that is common to all the entities in that chunk. If you change the archetype of an entity so that it moves into a new chunk that has the same type of chunk component, then the existing value in the destination chunk is unaffected. Note: If the entity is moved to a newly created chunk, then ECS creates a new chunk component for that chunk and assigns its default value.
The main differences between working with chunk components and general-purpose components is that you use different functions to add, set, and remove them.
Relevant APIs
Purpose | Function |
---|---|
Declaration | IComponentData |
ArchetypeChunk methods | |
Read | GetChunkComponentData<T>(ArchetypeChunkComponentType<T>) |
Check | [HasChunkComponent<T>(ArchetypeChunkComponentType<T>)] |
Write | SetChunkComponentData<T>(ArchetypeChunkComponentType<T>, T) |
EntityManager methods | |
Create | AddChunkComponentData<T>(Entity) |
Create | AddChunkComponentData<T>(EntityQuery, T) |
Create | AddComponents(Entity,ComponentTypes) |
Get type info | [GetComponentTypeHandle] |
Read | [GetChunkComponentData<T>(ArchetypeChunk)] |
Read | GetChunkComponentData<T>(Entity) |
Check | HasChunkComponent<T>(Entity) |
Delete | RemoveChunkComponent<T>(Entity) |
Delete | RemoveChunkComponentData<T>(EntityQuery) |
Write | EntityManager.SetChunkComponentData<T>(ArchetypeChunk, T) |
Declaring a chunk component
Chunk components use the interface type IComponentData.
public struct ChunkComponentA : IComponentData
{
public float Value;
}
Creating a chunk component
To add a chunk component directly, use an entity in the target chunk, or use an entity query that selects a group of target chunks. You cannot add chunk components inside a job, nor can they be added with an EntityCommandBuffer
.
You can also include chunk components as part of the EntityArchetype or list of [ComponentType] objects that ECS uses to create entities. ECS creates the chunk components for each chunk and stores entities with that archetype.
Use ComponentType.ChunkComponent<T> or [ComponentType.ChunkComponentReadOnly<T>] with these methods. Otherwise, ECS treats the component as a general-purpose component instead of a chunk component.
With an entity in a chunk
Given an entity in the target chunk, you can use the EntityManager.AddChunkComponentData<T>() function to add a chunk component to the chunk:
EntityManager.AddChunkComponentData<ChunkComponentA>(entity);
When you use this method, you cannot immediately set a value for the chunk component.
With an EntityQuery
Given an entity query that selects all the chunks that you want to add a chunk component to, you can use the EntityManager.AddChunkComponentData<T>() function to add and set the component:
EntityQueryDesc ChunksWithoutComponentADesc = new EntityQueryDesc()
{
None = new ComponentType[] { ComponentType.ChunkComponent<ChunkComponentA>() }
};
EntityQuery ChunksWithoutChunkComponentA = GetEntityQuery(ChunksWithoutComponentADesc);
EntityManager.AddChunkComponentData<ChunkComponentA>(ChunksWithoutChunkComponentA,
new ChunkComponentA() { Value = 4 });
When you use this method, you can set the same initial value for all of the new chunk components.
With an EntityArchetype
When you create entities with an archetype or a list of component types, include the chunk component types in the archetype:
EntityArchetype ArchetypeWithChunkComponent = EntityManager.CreateArchetype(
ComponentType.ChunkComponent(typeof(ChunkComponentA)),
ComponentType.ReadWrite<GeneralPurposeComponentA>());
Entity newEntity = EntityManager.CreateEntity(ArchetypeWithChunkComponent);
or list of component types:
ComponentType[] compTypes = {ComponentType.ChunkComponent<ChunkComponentA>(),
ComponentType.ReadOnly<GeneralPurposeComponentA>()};
Entity entity = EntityManager.CreateEntity(compTypes);
When you use these methods, the chunk components for new chunks that ECS creates as part of entity construction receive the default struct value. ECS does not change chunk components in existing chunks. See Updating a chunk component for how to set the chunk component value given a reference to an entity.
Reading a chunk component
To read a chunk component, you can use the ArchetypeChunk object that represents the chunk, or use an entity in the target chunk.
With the ArchetypeChunk instance
Given a chunk, you can use the EntityManager.GetChunkComponentData<T> function to read its chunk component. The following code iterates over all of the chunks that match a query and accesses a chunk component of type ChunkComponentA
:
NativeArray<ArchetypeChunk> chunks = ChunksWithChunkComponentA.CreateArchetypeChunkArray(Allocator.TempJob);
foreach (var chunk in chunks)
{
var compValue = EntityManager.GetChunkComponentData<ChunkComponentA>(chunk);
//..
}
chunks.Dispose();
With an entity in a chunk
Given an entity, you can access a chunk component in the chunk that contains the entity with EntityManager.GetChunkComponentData<T>:
if (EntityManager.HasChunkComponent<ChunkComponentA>(entity))
{
ChunkComponentA chunkComponentValue = EntityManager.GetChunkComponentData<ChunkComponentA>(entity);
}
Updating a chunk component
You can update a chunk component given a reference to the chunk it belongs to. In an IJobChunk
job, you can call [ArchetypeChunk.SetChunkComponentData]. On the main thread, you can use the EntityManager version: [EntityManager.SetChunkComponentData]. Note: You cannot access chunk components using SystemBase Entities.ForEach because you do not have access to the ArchetypeChunk
object or the EntityManager.
With the ArchetypeChunk instance
To update a chunk component in a job, see Reading and writing in a system.
To update a chunk component on the main thread, use the EntityManager:
EntityManager.SetChunkComponentData<ChunkComponentA>(chunk, new ChunkComponentA() { Value = 7 });
With an Entity instance
If you have an entity in the chunk rather than the chunk reference itself, you can also use the EntityManger to get the chunk that contains the entity:
Note: If you only want to read a chunk component and not write to it, you should use ComponentType.ChunkComponentReadOnly when you define the entity query to avoid creating unnecessary job scheduling constraints.
Deleting a chunk component
Use the EntityManager.RemoveChunkComponent functions to delete a chunk component. You can remove a chunk component given an entity in the target chunk or you can remove all of the chunk components of a given type from all chunks an entity query selects.
If you remove a chunk component from an individual entity, that entity moves to a different chunk because the archetype of the entity changes. The chunk keeps the unchanged chunk component as long as there are other entities that remain in the chunk.
Using a chunk component in a query
To use a chunk component in an entity query, you must use either the ComponentType.ChunkComponent<T> or [ComponentType.ChunkComponentReadOnly<T>] functions to specify the type. Otherwise, ECS treats the component as a general-purpose component instead of a Chunk component.
In an EntityQueryDesc
You can use the following query description to create an entity query that selects all chunks, and entities in those chunks, that have a chunk component of type, ChunkComponentA:
EntityQueryDesc ChunksWithChunkComponentADesc = new EntityQueryDesc()
{
All = new ComponentType[] { ComponentType.ChunkComponent<ChunkComponentA>() }
};
Iterating over chunks to set chunk components
To iterate over all chunks for which you want to set a chunk component, you can create an entity query that selects the correct chunks and then use the EntityQuery object to get a list of the ArchetypeChunk instances as a native array. The ArchetypeChunk object allows you to write a new value to the chunk component.
public class ChunkComponentExamples : SystemBase
{
private EntityQuery ChunksWithChunkComponentA;
protected override void OnCreate()
{
EntityQueryDesc ChunksWithComponentADesc = new EntityQueryDesc()
{
All = new ComponentType[] { ComponentType.ChunkComponent<ChunkComponentA>() }
};
ChunksWithChunkComponentA = GetEntityQuery(ChunksWithComponentADesc);
}
[BurstCompile]
struct ChunkComponentCheckerJob : IJobChunk
{
public ComponentTypeHandle<ChunkComponentA> ChunkComponentATypeHandle;
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
{
var compValue = chunk.GetChunkComponentData(ChunkComponentATypeHandle);
//...
var squared = compValue.Value * compValue.Value;
chunk.SetChunkComponentData(ChunkComponentATypeHandle,
new ChunkComponentA() { Value = squared });
}
}
protected override void OnUpdate()
{
var job = new ChunkComponentCheckerJob()
{
ChunkComponentATypeHandle = GetComponentTypeHandle<ChunkComponentA>()
};
this.Dependency = job.Schedule(ChunksWithChunkComponentA, this.Dependency);
}
}
Note that if you need to read the components in a chunk to determine the proper value of a chunk component, you should use IJobEntityBatch. For example, the following code calculates the axis-aligned bounding box for all chunks containing entities that have LocalToWorld components:
public struct ChunkAABB : IComponentData
{
public AABB Value;
}
[UpdateInGroup(typeof(PresentationSystemGroup))]
[UpdateBefore(typeof(UpdateAABBSystem))]
public class AddAABBSystem : SystemBase
{
EntityQuery queryWithoutChunkComponent;
protected override void OnCreate()
{
queryWithoutChunkComponent = GetEntityQuery(new EntityQueryDesc()
{
All = new ComponentType[] { ComponentType.ReadOnly<LocalToWorld>() },
None = new ComponentType[] { ComponentType.ChunkComponent<ChunkAABB>() }
});
}
protected override void OnUpdate()
{
// This is a structural change and a sync point
EntityManager.AddChunkComponentData<ChunkAABB>(queryWithoutChunkComponent, new ChunkAABB());
}
}
[UpdateInGroup(typeof(PresentationSystemGroup))]
public class UpdateAABBSystem : SystemBase
{
EntityQuery queryWithChunkComponent;
protected override void OnCreate()
{
queryWithChunkComponent = GetEntityQuery(new EntityQueryDesc()
{
All = new ComponentType[] { ComponentType.ReadOnly<LocalToWorld>(),
ComponentType.ChunkComponent<ChunkAABB>()}
});
}
[BurstCompile]
struct AABBJob : IJobChunk
{
[ReadOnly] public ComponentTypeHandle<LocalToWorld> LocalToWorldTypeHandleInfo;
public ComponentTypeHandle<ChunkAABB> ChunkAabbTypeHandleInfo;
public uint L2WChangeVersion;
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
{
bool chunkHasChanges = chunk.DidChange(LocalToWorldTypeHandleInfo, L2WChangeVersion);
if (!chunkHasChanges)
return; // early out if the chunk transforms haven't changed
NativeArray<LocalToWorld> transforms = chunk.GetNativeArray<LocalToWorld>(LocalToWorldTypeHandleInfo);
UnityEngine.Bounds bounds = new UnityEngine.Bounds();
bounds.center = transforms[0].Position;
for (int i = 1; i < transforms.Length; i++)
{
bounds.Encapsulate(transforms[i].Position);
}
chunk.SetChunkComponentData(ChunkAabbTypeHandleInfo, new ChunkAABB() { Value = bounds.ToAABB() });
}
}
protected override void OnUpdate()
{
var job = new AABBJob()
{
LocalToWorldTypeHandleInfo = GetComponentTypeHandle<LocalToWorld>(true),
ChunkAabbTypeHandleInfo = GetComponentTypeHandle<ChunkAABB>(false),
L2WChangeVersion = this.LastSystemVersion
};
this.Dependency = job.Schedule(queryWithChunkComponent, this.Dependency);
}
}