Can We Be Sure About Ordering Of Components?

Greetings,

While I was tinkering with ECS I realized that I’m relying on ordering from ComponentDataArray extensively.Without this assumed ordering,my systems will fail miserably.Given that my low bug-hunting skills and basic ECS debugging tools I felt the need to clarify it.

Here is an example to show my worries:

There is an entity archetype of “enemy”.EnemyID,HealthData,ManaData and CoinData are components.
I loop through them with IJobParallelFor.

[ReadOnly] public ComponentDataArray<EnemyID> EnemyIDs;
[ReadOnly] public ComponentDataArray<HealthData> HealthDatas;
[ReadOnly] public ComponentDataArray<ManaData> ManaDatas;
[ReadOnly] public ComponentDataArray<CoinData> CoinDatas;

public void Execute(int index) .
{
    var enemyID= EnemyIDs[index].ID;
    var health=HealthDatas[index].value;
    var mana= ManaDatas[index].value;
    var coins= CoinDatas[index].value;    
}

Normally,if i understood ECS correctly,this should work as entities and their components align correctly in table-like fashion.

Index 0 - Entity 0 - HealthComponent 0 - ManaComponent 0 - CoinComponent 0
Index 1 - Entity 1 - HealthComponent 1 - ManaComponent 1 - CoinComponent 1
Index 2 - Entity 2 - HealthComponent 2 - ManaComponent 2 - CoinComponent 2

But what if I add or remove components on the go,how will ECS responds to that?Let’s assume I removed CoinComponent 1.Can I still use CoinDatas[2] to fetch Entity 2’s CoinComponent?

If not,is there another way to adapt to this other than having an ID value in each component to loop through and find their “parent entity” accordingly?

Thanks in advance.

Couple of things:

  1. The ordering of entities and components is constant given that entities remain constant and components are neither created nor destroyed. However, when entities are created or destroyed or components are added or removed, component data may shift around in memory. This is a result of component data being stored with related archetypes in a chunk.

  2. In your example, you are accessing data by index, note this is not an entity index. I assume you retrieved all these ComponentDataArrays from the same ComponentGroup (and not using EntityArchetypeQuery - I’m not sure if the following is true in that case). If so, then all entities in this group will be of the same archetype (having the same component types). If you removed CoinComponent from Entity 1, then that entity will no longer be a part of that component group and index 1 will contain the data of Entity 2.

  3. Chunk Iteration - it’s nice :slight_smile: and conveniently has an API to determine if the data has changed, which might suite your needs.

1 Like

CDA is an iterator that can go through multiple kinds of chunk and it is made anew on every before OnUpdate if you use [Inject] or in OnUpdate if you use componentGroup.GetComponentDataArray.

It travels through the database to make an iterator for you every time, so the index changes everytime it is remade. When you remove a component then remake CDA, CDA.Length will be decreased accordingly. Using [2] would be over its new Length and what was [2] previously should be at [1] now in this simple case. But you cannot assume either that data will move back linearly like in C# List’s Remove. The iterator’s travel linearly in chunk level, but which chunk will be iterated first cannot be depend on.

If you instead “keep” the old CDA with old Length to use old index that won’t work either since that index will now go to invalid area or even entirely different data, and Unity prevent that by invalidating all existing CDA if you do any operation that would cause data movement. Using invalidated CDA will throw error.

ECS is pretty weak to ordering, it needs to do something to everything of the same archetype equally. In your case I would solve it by storing an Entity variable in some of the ComponentData to represent its parent entity.