EntityManager.Exists is unreliable

I’ve just spent a while debugging why my Exists checks are returning true after explicitly calling Destroy on them. It took stumbling upon the ICleanupComponentData documentation to realise what was happening: a Netcode component was preventing the entity from being registered as destroyed. Worse, my component lookup for checking the value of an enabled component was throwing “component not found on entity” exceptions - all components except the cleanup components had been removed.

In my opinion, this completely goes against expectations, and renders certain key functionality as unreliable. You simply cannot rely on Exists, IsComponentEnabled, etc. for entities you know which may/will be destroyed. And since cleanup components can be added to an entity from other packages (e.g. Netcode), you have no explicit knowledge that they’re there, and once known, you have to rejig your checks. As a workaround, I’ve added a tag component to all destroyable entities, which I check the existence of instead of calling Exists.

I feel that calling Destroy should register an entity as destroyed, no exceptions. For cleanup components, having a “WithDestroyed” option in the query (in the same manner as when dealing with prefabs, system entities, etc) would be clean and straightforward.

Thoughts?

2 Likes

So EntityManager.Exists(myEntity) could return false, even if the entity does exist in memory? This could also be viewed as “counter to expectations”.

I believe this is what the DOTS team intended users to do. Personally, I recommend using HasComponent/HasBuffer over EntityManager.Exists, when possible.

Yes, the same as prefabs, system handles, etc are hidden from queries by default. I cannot imagine that the current manner of explicitly calling DestroyEntity(e), then finding Exists(e) == true to be more intuitive than using the same pattern of accessing “special” entities.

Is there a link to this in the docs? I find it hard to believe, and even if so, it doesn’t address the unreliability of Exists.

One of the pillars traditional ECS is no (or very little) data abstraction. ECS wants the programmer to be aware of how the memory works. That’s probably why Unity used a different approach than with GameObjects.

No, pure speculation on my part.

It is only unreliable when you think of Exists(entity) as a NotDestroyed(entity) method. This might be what Unity intended at first, but as you said, with cleanup components, that’s not what Exists means anymore. To me, it’s become intuitive.

When you spend several hours trying to narrow down a bug simply because Exists didn’t return as expected, because the netcode package added a cleanup component, I would disagree that the function is intuitive.

I Think it is like go.activeSelf vs go.activeInHierarchy methods

so we need 2 methods like ExistsNow and ExistsAndNotMarkedForDestroy ← may be some shorter name

2 Likes

You shouldn’t really check against if Entity exists. Check against required data instead. This is more robust.
Non cleanup components are always removed.

1 Like

Yes, this is the reason I made this post. Checking for known non-cleanup components feels wrong when I simply need to know if an entity exists. It’s non-intuitive.

1 Like

Entity exists used to work as expected… now we need to add our components to mark and check if it really exists??

It also seems very unreliable. One object is destroyed instantly after .DestroyEntity and others are not