Let’s go with simple example.
There is game with ropes, a lot of ropes. Each rope has around 5-15 segments and they are simulated by some entity systems. Assume each segment is separate entity with components like position, stiffness, color.
The problem is that simulation need to know te previous and next segment to be able to solve distance constraint and stuff like that - this can be done, but it’s problematic at the end of rope. Last element can’t solve constraints with first element of the other rope. Another thing is that it must be ensured that segments of single rope are in the same chunk.
It could be solved with shared component, but it would fragment memory a lot and there would be a lot of unused space, plus if someone decides to make super long rope it might actually go out of chunk.
My question is: how to deal with these kind of problems in ECS?
What if I wanted to operate only on certain rope or list of ropes? For example select 3 rops with query or just apply wind forces or do some destruction isn’t that a bit of problem, especially write access?
For the wind you want make a wind component tag (IComponentData with no data) that way they will be in another archetype that you can select through query.
For destruction same applies you can make a tag component that tell a system it should cut the rope (remove some of the segments from the current entity and spawn a new one with the remaining segments). Then remove the tag component so it doesn’t cut the rope again. That’s called a reactive system.
A single rope is easy. That is just a single-threaded job or main thread access. For a handful of ropes, if you have a list of rope entities and you know the list does not contain duplicates (pretty easy to verify with a hashset), then it is safe to process that list in parallel, and you can slap on a [NativeDisableParallelForRestriction] on a BufferFromEntity and do your thing.
Thank you, that makes sense, however what if there was more complex scenario that would require use of entities, to make it possible to use components on them, or simply element of buffer would be huge and I wanted to use only float3 in single system instead of whole bunch of data (8 x float3) - is there other approach that can be taken?
You could make a RopeBundle SharedComponent which has multiple ropes all batched together, and use an ICD index for the specific rope. I actually thought about doing that for exposed skeletons in Kinemation, but ultimately I choose to eat the random access costs and do other optimizations instead to avoid the structural change costs.
What do you mean by ICD? I am not sure I get the idea of multiple ropes batched in single shared component. You mean somehow to store like 10 ropes in single entity?
So let’s say you have a rope Entity which has an ICD called RopeID which is an int, but you want the rope points to be in separate entities so you can attach components. Each rope point has a memberOfRope component with the same index as the RopeID as well as an Entity reference to the rope entity. However, having a Shared Component RopeMemberIDRange lets you group multiple ropes into a single chunk. The order of the entities in the chunk will still be random, but you at least get some form of cache coherency.