I am currently building an Entity Component System (ECS) in cython in order to speed up operating on large numbers of game objects in python. In the process of building this system, I ran into the following architectural issues that I would appreciate shedding some light upon. I have read through lots of online blog posts describing ECS strategies, but most of these are geared towards C++ and do not describe interfacing such systems with a scripting language like python.
I am defining the parts of my ECS as follows for clarity, since everyone seems to have different terminology/mental frameworks for these concepts:
- Handle: This is a uint64_t that is subdivided into index, version, type, and free parts. For use in my CompMaps.
- Entity: Just a handle.
- Comp (Component): Just a handle.
- CompType: A uint8_t (derived from the Handle's "type")
- CompMap: This is a "slotmap" data structure. Memory for each component/entity can be retrieved using Handle objecsts.
- Archetype: A set of CompTypes, mapped to a uint16_t for easy access.
Since the component structs are not directly accessible in python outside of numpy arrays, and since I don't have access to C++ templating in python that is typically used for registering components, I expose all accesses of entities and components through these handles. As such, I have the following questions regarding how to safely provide access to CompTypes and Components in my ECS.
- Should each "world" in the ECS be able to define its own unique CompTypes and Archetypes?
- Pros: Allows some flexibility (i.e. there could be a UI specific set of CompTypes for a "UI world" and there would be no need to "waste" CompType int slots for these in the general gameplay world.
- Cons: Systems can't reliably operate on multiple worlds if the ints for each of the world's CompTypes mean different things!
- How should "built-in" CompTypes and Archetypes from the rest of the game engine be handled?
- Currently doing this as a global enum inside a cython module.
- Should these built-in types be auto-registered into every world upon initialization?
Please let me know if there are any parts that were unclear, and I would be happy to clarify them. Any help would be greatly appreciated.