[ECS] Enabling and Disabling Component Best practice

Lets assume I have a Target component that has reference to entity
is it wise to disable the component and enabling it compared to doing target==Entity.Null check

If so, in which stage should I do the modification like SimulationSystemGroup, BeginSimulationEntityCommandBufferSystem…

What is the best practice?

I can’t say for certain what best practice is, but Entity.Null doesn’t always work. If the entity was destroyed, Entity.Null doesn’t catch it, you have to use Exists. This threw me for a loop at first.

I would say if your system doesn’t need to run at all when there is no target then disabling it would probably be best. When to do it shouldn’t really matter.

Thank you for your reply and time @WonkeeKim.
Thank you also for sharing the info with me.

Are you suggesting the targeted component to go over target component, the TargetEntity value as null?
Like player targeting enemy and enemy telling player’s target component that its Null?

The problem at hand is: should I change component value or just enable or disable the component. If so, what is the best place in system order to do so.

The convention is really up to you. If having a target means having the Target component enabled, you can totally do that but you have to be consistent across all of your systems. This is better in most cases since you skip a boolean check.

Why I am asking is because, target might be read from multiple system so where should I activate that things doesn’t create problem. I mean what is the recommended way of enable/disabling elements.
For death is basically at

[UpdateInGroup(typeof(SimulationSystemGroup), OrderLast = true)]
[UpdateBefore((typeof(EndSimulationEntityCommandBufferSystem)))]

Where should I enable/disable? Or even should I enable/disable. Like think there is a component is alive, it can be done with both IEnableComponent also with a bool in inside it. But which approach is better?

There must be a recomended way right?

The recommended way is whichever way is the most efficient for your use case. And you haven’t provided enough information about your use case for me to suggest an answer (though I think there’s a higher likelihood that IEnableableComponent is what you want). To properly assess the problem, you need to describe roughly the percentage of targets that are enabled at a given moment in time (which best characterizes the scenario you want to optimize for, whether that be the worst case or average case), as well as how you are accessing the targets, and what other logic surrounds these enable checks.

1 Like

Use target == Entity.Null for frequent checks to avoid structural change costs. Disable/enable components for infrequent changes or to exclude entities from systems. Perform structural changes in BeginSimulationEntityCommandBufferSystem.

No, that’s incorrect. Enable/Disable enableable components (IEnableableComponent) doesn’t involve any structural changes. That’s the whole point of enableable components. It’s 128 bit mask per chunk and enable/disable immediately sets bits for chunk entities. Don’t confuse it with adding/removing components.

Entity.Null is just a poor choice of word. Since Entity is a struct, a value type, it can never be null as any reference type. In essence, a “destroyed entity” just means: its value isn’t stored in the database anymore.

Furthermore, because it’s a value type, storing the Entity in a target field you just store a copy of its original value. There is no possible automated mechanism to update the copies when the original value was changed or invalidated. Unless you hold a ref to the underlying native memory which stores the entities.

Entity.None should help people avoid such confusion while they’re not familiar with how value types work.

Entity. Null creates a new Entity and compares against it. I think it should have been called Entity.HasBase.

My god the legend is here.

My question is as it’s TargetComponentData. It can be referenced by weapon, mover, ai everywhere.
I am thinking of using a DynamicBuffer and keep a list of everyone that’s targeting me and when the Entity is dead it looks up buffer and sets is bool isTargetDead = true.

That’s how I did for for MonoBehavior but I feeling it might be really problematic if it stores the full entity in memory instead.

So many questions. Should I null check should I just disabled enable indicating it has no target.

Unless the DynamicBuffer serves an additional purpose, that seems like a really inefficient way about things.

I’d suggest maybe trying to answer the ones I asked that you haven’t answered yet. Notably, probabilities matter a lot in DOTS as you either want to optimize the worst-case scenario, or you want to bias your optimization towards the common scenarios. But also, depending on what else you are doing in those other jobs, you may identify situations where you could kill two birds with one stone or avoid redundant checks, and that may affect what operations you are even doing and therefore what you are optimizing for.

But honestly, I really think you just need to try whatever approach feels most intuitive to you and profile it. Over time, the profiler will help you build an intuition for these things. Unlike GameObjects, “best rpactices” in DOTS happen to be a lot more nuanced, and very fine details can have big impacts. But also, there’s also an art to picking both what to care about, and what not to care about. And the profiler is very good at telling you that.

But honestly, I really think you just need to try whatever approach feels most intuitive to you and profile it. Over time, the profiler will help you build an intuition for these things. Unlike GameObjects, “best rpactices” in DOTS happen to be a lot more nuanced, and very fine details can have big impacts. But also, there’s also an art to picking both what to care about, and what not to care about. And the profiler is very good at telling you that.

Then Let just try and make it using enable disable component to see what happens as I have no idea how it would play out. And in future worry about it if it becomes a problem.

Entity is a struct, so new Entity() is essentially default(Entity) with both Index and Version equal 0. I usually call it a None value.

What do you have in mind for this HasBase?

1 Like