Right now it is possible to remove all components on an entity and have a blank component-less entity orphaned in the pool. What do you think of adding a check on component removed that removes the Entity if it has no remaining components?
I have a few place in my code handling manual cleanup, but it would be really nice to have this handled by Entitas. I could see this having an issue with the OnComponentRemoved callback, but there may be a way to work around that.
I think that would make removing and replacing components slower because of the check isn't ?
In my case to destroy entities I add a "destroy" component or similar to flag those entities in a reactive system that executes at the end of the update/frame.
Edit: You can do "pool.DestroyEntity(entity);" too
I think that would make removing and replacing components slower because of the check isn't ?
I think the check should be fairly fast, this is what I do in my code:
if (entity.GetComponents().Length == 0)
{
pool.DestroyEntity(entity);
}
In my case to destroy entities I add a "destroy" component or similar to flag those entities in a reactive system that executes at the end of the update/frame.
Yeah I actually do the same thing in my codebase (well except for the example above).
Two thoughts:
entity.GetComponents().Length == 0) which will allocate an empty array (because of GetComponts())As @JuDelCo suggested, I'd go for flagging entites with DestroyComponent
But of course, in your case, if you are sure that you can destroy entities as soon as they don't have any components anymore, you could implement it like:
pool.OnEntityCreated += ... e.OnComponentRemoved += ... => pool.Destroy(entity)
Those are good points but I still think it's weird that you can end up with orphaned entities. Essentially there is no way to pull them from groups at the at that point, so they just hang around in the pool and take up memory, almost like a memory leak. While there are definitely ways to handle this in application code it seems like a universal issue that you may want to the library itself handle.
Removing all components not necessarily means that you want to destroy the entity. Maybe you remove a component just to add an other one
Yeah, the timing on that is tricky. Maybe flag the entity as deletable and then fire on the next frame after doing another check? I think my suggested implementation above was naive, I think the final solution would need to be more involved.
the pool would need to listen to on component removed (which it already does) then could check entity.GetComponents().Length == 0) which will allocate an empty array (because of GetComponts())
Yeah for optimization purposes that should probably be another more efficient way to check the number of components on an entity.
Just to clarify. This is not something that is causing issues in my application. I've handled all cases of empty of entities (that I've thought of at least). But my thoughts on that matter are this:
When i use entitas and design my game components & systems, I can see two "main" types of entities:
Can you provide an example where you need to use entities which holds only temporally flag components? (those entities are the type i think they could end "detached" without components in the pool accidentally)
Yes, if you could share an example, that would be great because I never encountered a case where I had an empty entity that I need to take care of myself.
Sure, I can give you a concert example from my codebase; I have a bunch of timer based systems that use timer components. In simple terms, these timer components are used to countdown to discrete events which execute when the timer hits zero. Essentially they are my way of implementing Unity's Invoke / Coroutines while still keeping the code friendly to ECS.
Often I attach other components to these timers so they can be associated with other data. To give you an example, I have a system that increases the pitch of the music over a period of time, so the entity in this example has a music pitch component, a music pitch over time component, and a timer attached to it. The entity is this example is easy to use because I can check the timer and preform the relevant mutations of the data based on a single entity. After the end of timer event some systems remove the timer and effectively shutdown. So in some cases, when the timer is removed there are other components attached to the entity that I want to keep around, which means I just drop the timer and keep the other data on the entity. Other cases, the timer is the only component on the entity, so I preform a check and destroy the entire entity if nothing else is attached.
Hope I didn't simplify that example too much, I don't want to go too far into the minutia of my codebase. But high level, if you have a situation where you don't want to just blow away an entity carte blanche, ignoring any existing data, you have to do a check to make sure empty entities are cleaned up.
Why you would have a entity with only a timer component ?
I use timers for a bunch of stuff in my game, sometimes they don't need any other data connected to them.
Few example:
Basically anything in my game you would traditionally use coroutines or Invoke for in a MonoBehavior I use timers for. However, I don't want to focused on the timer aspect for the purposes of this discussion, it is just a concrete example I choose from my code base of a situation where I need to check for empty entities on component removal.
If an entity has only a timer component (i imagine a single integer data with the timestamp), why not just destroy the entity when timer ends?
That's what I am doing. I remove the timer component when it's over and sometimes the entity has more components on it so I don't delete it, sometimes it doesn't so I clean it up.
But if the entity has only a timer component, how you know for what use is that timer?
Maybe the problem isn't the timers but data components instead. Why you delete all data components in the entity and not destroy the entity itself?
Maybe sharing some code can ease our understanding of your problem (i'm still convinced that maybe be a component design problem)
But if the entity has only a timer component, how you know for what use is that timer?
It's pulled by a system by the timer component. I think I did a bad job of explaining this point, while I have a base timer component there are several distinct timers components in my codebase, so TimerAComponent is distinct from TimerBComponent, so the can pull the proper timer.
Again, I don't want to go too deep into my codebase because I was hoping the generic utility of this change be apparent. I don't think I am doing a good job of advocating for this. I am drawing a blank on another specific example. It's not hard to do application side, so if I'm not making my case well I can always handle it on my side.
@JuDelCo You actually do the same kind of manual cleanup I am talking about in your codebase as well,
if(e.GetComponentIndices().Length == 0)
{
e.isDestroy = true;
}
So that's at least one other example 馃槈
(The coroutine system is pretty interesting BTW)
If you can only have one Timer per type, you should use SingleEntities
In my "cleanup", note i destroy all entities in _one_ system at the end of the frame and I destroy the entity, not his components.
By the way, the ideal system should not have state, remember that :smiley: (maybe you are storing some data in those timer systems you should store them in the entities as components)
Again, this is too specific on that one scenario and not the on the general idea. So let me try and refocus this discussion.
Let's say this is not a good idea at a framework level, does Entitas provide the tools to create a generic system that cleans up empty entities?
I haven't tried this myself, I am handling specific instances of this as it comes up, but I think if you were dealing with a single pool it would be easy enough to iterate through all entities in pool and delete them if they had no components.
I can think of a few issues:
Just some thoughts. I think if Entitas has all the tools needed to do this I can just create a generic system and stick it in future projects.
I was reading through the discussion again, but I still have the points in my head that I made earlier. I think your solution with a custom cleanup system as a last system in your list could solve this.
And yes, there is a way for having one system for multiple pools (pretty new feature):
See AnimateDestroyViewSystem.cs
Interesting, so you could conceivable go through each pool and clear out empty components.
public interface IGroupObserverSystem : IReactiveExecuteSystem {
GroupObserver groupObserver { get; }
}
Where is the pools method?
Ok, I'll close this one. Thanks for the discussion on the feature guys.
Most helpful comment
I was reading through the discussion again, but I still have the points in my head that I made earlier. I think your solution with a custom cleanup system as a last system in your list could solve this.
And yes, there is a way for having one system for multiple pools (pretty new feature):
See AnimateDestroyViewSystem.cs