Question

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.

  1. 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!
  2. 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.

Was it helpful?

Solution

I am answering this for completeness. The resulting API design that I went with can be best illustrated with some example user code:

from pyorama.core.app import *
from pyorama.core.system import *
from pyorama.core.world import *

class EnemySystem(System):

    def init(self):
        pass

    def quit(self):
        pass

    def update(self):
        COMP_GROUP = self.comp_group_types["test"]
        print("Updating...", id(self.world), COMP_GROUP)

class Game(App):

    def init(self):
        MAX_ENTITIES = 10000
        COMP_A = 123
        COMP_GROUP = 0

        super().init()
        self.world = World()
        self.world.init(MAX_ENTITIES)
        self.world.register_comp_type(COMP_A, 1000, 32, b"4Q")
        self.world.register_comp_group_type(COMP_GROUP, [COMP_A])

        comp_a = self.world.create_comp(COMP_A)
        comp_b = self.world.create_comp(COMP_A)

        ent_a = self.world.create_entity()
        self.world.attach_comp(ent_a, comp_a)
        self.world.attach_comp(ent_a, comp_b)

        enemy_system = EnemySystem()
        self.world.attach_system(enemy_system, {"test": COMP_GROUP})
        self.world.set_system_update_order([enemy_system])

    def quit(self):
        super().quit()

    def update(self):
        self.world.update()

game = Game(use_sleep=True, use_vsync=False, ms_per_update=1000.0/60.0)
game.run()

The solution that I came up with to map System objects to the appropriate World and CompGroupType objects (what I had previously referred to as Archetypes) was to create an attach_system method. This method would be responsible for registering a system and supplying the correct world data. The correct components could then be located using the keys passed into the dict of CompGroupTypes.

Licensed under: CC-BY-SA with attribution
scroll top