Doraku / DefaultEcs

Entity Component System framework aiming for syntax and usage simplicity with maximum performance for game development.
MIT No Attribution
658 stars 62 forks source link

Detect Component Or Entity Removal #89

Closed generatives closed 4 years ago

generatives commented 4 years ago

I am having trouble integrating a physics engine with DefaultECS 0.14.1. I have a position component, physics body definition component, and a physics body component which holds a reference to the actual physics body the physics engine uses.

I have an EntitySet defined as the following

world.GetEntities() .With<Transform>().With<PhysicsBody>().WhenAddedEither<PhysicsDefinition>().WhenChangedEither<PhysicsDefinition>().AsSet();

I use this EntitySet to create the physics body in the physics engine and update it if the definition changes.

With DefaultECS 0.13.1 I had another EntitySet defined as the following

world.GetEntities().With<Transform>().With<PhysicsBody>().With<PhysicsDefinition>().AsSet();

I would user the EntityRemoved event on that EntitySet to remove the physics body from the physics engine if the entity was removed or its component makeup changed to make the body invalid.

Now that the EntityRemoved event has been removed I cannot use this setup. Is there an alternative strategy?

Doraku commented 4 years ago

I wasn't a fan of those events, particularly the EntityRemoved one because you wouldn't get the value of the component removed so it felt more like a hack sometimes when used to put in some clean up logic (EntitySet a supposed to be processed at precise time but thoses events can occur whenever, seemed like problem of expectation to me). It has been replaced in v0.14.0 with the SubscribeComponent.../SubscribeEntity... methods family on the World class which I believe give better control and more expected behaviors.

In your case then, you could use SubscribeComponentRemoved to clear your entities of their bodie:

world.SubscribeComponentRemoved<Transform>(OnRemoved);
world.SubscribeComponentRemoved<PhysicsBody>(OnRemoved);
world.SubscribeComponentRemoved<PhysicsDefinition>(OnRemoved);

...

private void Clean(in Entity entity)
{
    ...
}

private void OnRemoved(in Entity entity, in Transform value) => Clean(entity);

private void OnRemoved(in Entity entity, in PhysicsBody value) => Clean(entity);

private void OnRemoved(in Entity entity, in PhysicsDefinition value) => Clean(entity);

Note that those callbacks are called even when the entities are disposed if they had the component but NOT when those component are disabled, complete this with SubscribeComponentDisabled if needed.

While this is much more verbose than what you had I hope it fills the gap, tell me if this works for you. If it is too much a hassle I will see what can be done to bring it closer to v0.13.1 code.

Doraku commented 4 years ago

Just an other idea: When you add your entities to the physics engine, maybe add a flag component to them to simplify the logic.

// Your cleanup system would become:
world.GetEntities().With<PhysicsFlag>().WithEitherRemoved<Transform>().Or<PhysicsBody>().Or<PhysicsDefinition>().AsSet();

...

// only subscribe to PhysicsFlag component removal
world.SubscribeComponentRemoved<PhysicsFlag>(OnRemoved);
generatives commented 4 years ago

Both these solutions seem reasonable, Thanks! I will try implementing tonight.