alecthomas / entityx

EntityX - A fast, type-safe C++ Entity-Component system
MIT License
2.22k stars 295 forks source link

Cloning an entity #126

Closed zackthehuman closed 8 years ago

zackthehuman commented 8 years ago

Hi. First off, I've been playing with EntityX for a few months now and I really am enjoying it. Thanks for your work!

I've been trying to find a way to do the two following things:

  1. Create "prototype" instances of entities, such that I can simply clone them at runtime to get a duplicate of a given entity.
  2. Keep the entity prototypes from being processed since they're basically templates.

I have a working solution to my first problem, which is to just repeatedly call has_component<T> for each component type followed by a call to assign_from_copy<T>. This works but is far from elegant and requires me to keep updating the function as component types are added/removed.

I was hoping to find or develop a built-in way to do this by leveraging the entity's component mask. However, I don't see a way to take a component mask and translate it into component instances. Basically, I want to generate a recursive template "unpacking" of components using the component mask and automatically invoke assign_from_copy on a new entity.

For my second issue, I thought about using multiple EntityManagers -- one to keep prototypes in, and another for the actual runtime entities. Since the IDs of the prototypes and runtime objects should never clash and won't be cloned, this seems like a reasonable approach but I'm not sure if it was intended to be used this way so I wanted to check if you have any suggestions.

Once again, thanks for all your hard work on this. Given a few pointers, I would be happy to contribute back anything that is generally useful!

alecthomas commented 8 years ago

There's no good solution to this. As you've noticed, the underlying code doesn't really know how to translate back from a component ID to the concrete component type. The compile_time branch (which will eventually be v3) could support this relatively easily, but does not yet.

Two EntityManager's could work, though I haven't really tested it. Entity IDs will almost certainly collide, but that shouldn't matter.

Personally I would just create factory functions that build new entities with the appropriate components. eg. Entity create_enemy(EntityManager &)

zackthehuman commented 8 years ago

Ah, you've mentioned that this could be solved in the compile_time branch before. For some reason, I thought that branch had been merged into master, but I guess not?

alecthomas commented 8 years ago

No it has not, it still needs some work before that can be done.

zackthehuman commented 8 years ago

My motivation for cloning stems from the fact that I'm trying to be as data-driven as possible. If I hard-code the "definition" of different entities into the code, then I'm constantly recompiling to test and tweak entity changes. My goal is to read in a data file, construct entities from it, and then instance those entities.

alecthomas commented 8 years ago

I don't think a data-driven approach precludes using a factory function; just have the factory generate the entity from the data: create_enemy(EntityManager&, FileResource&).

zackthehuman commented 8 years ago

With the minor refactor that #129 introduces, it would be possible to clone entities by basically doing something similar to how EntityManager::destroy works, by looping over the pools and asking them to insert their component into an entity.

alecthomas commented 8 years ago

👍

zackthehuman commented 8 years ago

I was fleshing this idea out a bit more and my approach was basically to add a create_from_copy method to Pool in order to clone the components directly into a specified ID. However, I feel like this is not a great idea since we have to keep polluting the Pool interface with Entity specific knowledge.

Instead, I have another idea. The basic approach would be to create a small helper class, ComponentHelper<T>. The EntityManager would keep a vector of these helpers, one for each component type, the same as it does for BasePool. This would allow the helper to have knowledge of Entity as well as each component type, such that it could invoke methods on the entity with the correct concrete type.

This kind of design allows for cloning as well as the same behavior I implemented for destroying entities and emitting removed events for each component, and lets the Pools be very simple.

Thoughts?

zackthehuman commented 8 years ago

Since #140 was merged it is now possible to clone entities as long as they have the same EntityManager. It's possible to clone across managers as long as you an ensure they have pools are the same components (a little dirty, but possible). Since this feature is now possible I'm going to close the issue.