I came across some posts here talking about the behavior of SharedComponentData, and I’ve been trying to wrap my head around how to handle an extremely large grid map.
My thoughts were that at the heart of a chunk based map, you need to be able to gather data based on a chunk, and request the chunks nearest a position to iterate and find what is at a position to execute code, whether it’s performing an action on the object or just determining if the grid space is taken to reject movement.
My understanding is that SharedComponentData forces the entities it’s attached to to be in the same chunk as long as the shared value is the same.
How does that work with differing entities besides the shared component? I assume that to be in the same chunk, it still has to have the same components otherwise?
Also, could this be used to store all tiles of a grid “chunk” in the same literal chunk? For instance, a SharedComponentData holding only a int3 position or some other identifier for chunk, added to “tile” entities would mean all tiles within that chunk are also within the same grid chunk as long as their values of the SCD match?
If so this would mean I would just have to ensure that I chunk them via the SharedComponentData value and if I need to get an entity at a certain grid position, I’d only be irritating through the entities within a single chunk no matter how many I actually have created, right?
Yes, SCD groups entities in chunks by SCD value inside archetype, which mean different archetypes with same SCD value will be in different chunks.
If they in different archetype - it’s impossible by design. If they in same archetype and all entities can fit in 16k chunk, then yes they in one chunk.
Besidse archetype separation, chunks has limited 16K size, thus it depends on archetype sizeof and count of entities.
But anyway you can separate entities by SCD value, and gets chunks from different archetypes by EntityArchetypeQuery, and then just process this chunks in IJobChunks for example, and use only chunks with specific SCD index, just get SCD value\index lists from EntityManager on main thread and pass required index to some IJobChunk field and use it for comparing with ArchetypeChunnk.GetSharedComponentIndex(ArchetypeChunkSharedComponentType)
Thank you! So just so I am understanding correctly:
It would work for tiles, correct? As these technically never move causing a mem copy to move chunks and would be identical archetypes, just with different values inside other components and grouped per value of SCD into chunks. If I made sure that the tiles were grouped in a small enough size to not hit the 16KB limit, would this work? (like 32x32 tiles, holding position, type, and maybe a couple other fields shouldn’t hit 16KB)
I suppose this would be more beneficial if this set of data/system didn’t actually hold the rendering entities of tiles or similar and rather other systems read this data to place pooled entities based on the “loaded” chunk?
But i could use this to dynamically place objects as I move around the map, as well as check for occupied positions for tile-based collision via the method you listed above?
Oh and by the way, thank you for answering so many questions on the forums about ECS. I’ve learned a lot by reading your answers.
They wiil. Simple math - you have tile with:
float3 (position) - size == 12 bytes
int (type) - size == 4 bytes
float3 (just some other field) - size == 12 bytes
Total Tile size: 4 + 12 + 12 = 28 bytes
Total Tiles count: 32 * 32 = 1024 tiles
Total size, which tiles require: 28 * 1024 = 28 672 bytes = 28K which more than 16K and thus they can’t be in one chunk physically.
Besides this is not final size.
Here is simple test, as you can see 1024 entities with
public struct Component1 : IComponentData
{
public int Type;
public float3 Position;
public float3 SomeField;
}
takes 3 chunks, where 2 chunks full - 445 entities per chunk and 1 chunk has 134 entity.
My apologies, i wasn’t thinking. It seems like this would be efficient though with a smaller entitity size. Like 100 (10x10) tiles per chunk would allow 160 bytes per entity which should be more than enough.
Iteration should be quick through that small of a chunk for collision checking.
Hello. I‘m learning about ECS recently, i need to set all different Textures to the same Tiles. But i dont want to change the material of the tiles, and i want to use the Function:“SetPropertyBlock”,but before use this i need to get the RenderTexture of the Entity. I tried but i failed. I wish for someone’s help if he knows it~
One thing to be aware of if trying to calculate chunk entity counts is that Unity adds extra components in response to other components.
If you add the transforms.Position component, Unity also adds LocalToWorld for example.
Thanks for the heads-up but for now, the tiles will hold minimal data and I’m hoping i can pool other entities for rendering based on the minimal info (type, if its occupied) provided by this system. At 10x10 chunks I’m only storing 100 entities/chunk which leaves me with 160 bytes of info per entity. Testing with this, and not needing a position or transform related component i only have like 4 bytes of overhead (im guessing just the entity index) plus an int3 position and a couple other fields not nearing 160 bytes. I was able to chunk 1000000 tiles into 100 tile chunks just fine by setting a ChunkPosition SCD to its Mathf. roundtoint(gridPosition / 10)*10.
If anyone could provide examples on the current optimal way of handling chunk iteration for this, that would be amazing. I’m working on it, but its hard for me to make sure I’m doing it right with so few examples to follow that aren’t outdated .
I just watched the end of the 9 hour Unite livestream video and it seemed like what was discussed was that IJobChunk could be passed a ComponentGroup including a filter to avoid all other chunks but at the same time, determining if a chunk includes a certain value in its Shared Component Data seems complex…
I’m trying to wrap my head around it, but for instance, i need to get the type and index of a shared component data from EntityManager. But do i do this once and cache it during OnCreateManager or during OnUpdate when scheduling the job?
Also, then can you really grab only the chunks of a certain SCD value via filter or are you still irritating each chunk to compare it’s SHD index with the desired SCD index, as well as the SHD value?
Its also confusing to me because you query for the chunk SCD type, but you provide the type when asking for it? What’s the point of that? Or can chunks contain multiple types of SCD? It’s just unclear to me why the method call for finding the shared component type to pass to the method for finding the index of the SCD requires a type as well.
If chunks can have multiple SCD then how do they chunk data based on the SCD of two different types?
@eizenhorn if you could answer any of these i would be so grateful. I’m trying to learn and i hate to bother but I’m so confused.
You can set filter to component group, and get chunks from group
ComponentGroup g = GetComponentGroup(typeof(Position));
g.SetFilter(SomeSharedComponent);
NativeArray<ArchetypeChunk> filteredChunks = g.CreateArchetypeChunkArray(Allocator.TempJob);
MyIJobChunk job = new MyIJobChunk();
job.Schedule(g);
--------------------------------------------------------------------------------
public struct MyIJobChunk : IJobChunk
{
public void Execute(ArchetypeChunk chunk, int chunkIndex)
{
// Do stuff with filtered chunk
}
}
In OnUpdate, because it’s changes when SCD added\removed.
See my code snippet above. But remember direct chunk iteration can be done in multithread and more performant than set filter, but on small amount of data it’s fine.
Of course chunk can have different SCD types and it’s completely normal and expected It’s filtering multiple values, like as AND operator. ( if(A && B && C) {//Do stuff } )