How to get components by index in ECS?

Given an EntityQuery, how do you get components by index using the same component ordering as EntityQuery.ToComponentDataArray?

My use case involves an EntityQuery from which I am getting the LocalTransform and LocalToWorld components. At the beginning of ISystem.Update, I copy the LocalTransforms to a native array using EntityQuery.ToComponentDataArray, and at the end, I copy them back to their chunks using EntityQuery.CopyFromComponentDataArray because I modify LocalTransform for most entities in the query. I access LocalTransforms using indices, and the access pattern is pretty random. I don’t just go from index 0 to the end. I might access index 5, then 1, then 32, etc.

But given some index, how do I get the corresponding LocalToWorld? Creating a native array of LocalToWorlds using EntityQuery.ToComponentDataArray involves copying, which seems inefficient. I guess what I want is to transform an index into a pointer to the LocalToWorld in its chunk. I’m thinking of calling EntityQuery.ToArchetypeChunkArray, mapping the given index to a specific chunk, calling ArchetypeChunk.GetComponentDataPtrRO on that chunk, and offseting the pointer to get the specific component. All this assumes that ToArchetypeChunk returns components in the same order as ToComponentDataArray, which I have yet to verify.

I also thought about using SystemAPI.GetComponent, but this function takes an Entity as parameter, meaning I would need to allocate an array of Entitys.

Anybody with experience have any advice?

Easiest way is ToComponentDataArray as you mentioned.

I cannot suggest the fastest way without knowing more about how you are using the the LocalToWorld values. But let me ask you this, is the copying of LocalToWorld a performance problem that you have uncovered in the profiler?

you can use ToArchetypeChunkArray with GetComponentTypeHandle

EntityQueryBuilder

Query.ToArchetypeChunkArray(Allocator.TempJob)

LocalTransformType  GetComponentTypeHandle<LocalTransform>()

  for (int c = 0; c < ChunkArray.Length; c++)
 {
     var chunk = ChunkArray[c];
     chunk.GetNativeArray(ref LocalTransformType );
}

I admit, I have not profiled whether copying LocalToWorlds is a performance problem. But I think it would be. The way I am iterating through them is that I first iterate through all of them, but then go back and access a few of them again at specific indices. I do not write to LocalToWorld; only read. Because I only access components sequentially once and then access them randomly afterwards, I assumed that copying all of the LocalToWorlds into an array would not give spatial locality performance boosts. But yes, I should profile at some point.

Ah, your ArchetypeChunk.GetNativeArray seems more convenient than the GetComponentDataPtrRO I was thinking of using. Thanks.

Why do you think that? How many LocalToWorlds would you be copying?

With that said, if you have sparse reads, you probably want to switch to entity handles or chunk + index in chunk for indexing rather than entityInQueryIndex.

I expect to copy anywhere from a dozen to a couple hundred LocalToWorlds, but there’s a non-zero chance I will have a couple thousand. I am prototyping a custom physics engine for a game and still experimenting with gameplay ideas. The LocalToWorlds I have been mentioning are for colliders.

Sorry, I’m still new to ECS. By “entity handles,” do you mean EntityTypeHandle? Are you suggesting that I convert an index into an entity, then get my LocalToWorld using that entity?

And about “chunk + index in chunk for indexing rather than entityInQueryIndex”, are you talking about the attributes you can put on the int parameters of IJobEntity.Execute? Why is combining ChunkIndexInQuery and EntityIndexInChunk better than EntityIndexInQuery?

I have experience with this: Latios-Framework-Documentation/Psyshock Physics/README.md at main · Dreaming381/Latios-Framework-Documentation · GitHub
I can tell you right now that the copying you are concerned about will probably be the least of your problems. If you were doing 100x more entities, then maybe…

I mean Entity and ComponentLookup.

Yes. And this is better because you can actually use this to look up a value directly in the chunk without an entity or a copied array. You only need an array of ArchetypeChunk, which will be a much smaller array. But honestly, copying the array is going to be a lot more maintainable, and will have cheaper lookups inside the physics algorithms. Plus, you can pre-parse the matrix into vector representation if you need it.

Sounds good. I will just copy my components then. Thanks.

Also, your Psyshock physics was one of the first things I looked at when I started using ECS. Very helpful.