benmoran56 / esper

An ECS (Entity Component System) for Python
MIT License
544 stars 69 forks source link

added optional class alias for Components #48

Closed BonitaTerror closed 3 years ago

BonitaTerror commented 3 years ago

This change allowed me to conveniently use components which solely contained an object from a third party library. For example, I could have a velocity and position component that just contain one variable for a pygame.Vector2().

Without aliasing, the processor would be

for ent, (vel, pos) in world.get_components(Velocity, Position):
    pos.position += vel.velocity

many of my processors have even more clutter, since I have many components that have just one numpy array or vector, but this is the simplest example.

With aliasing, we can do:

world.add_component(entity, pygame.Vector2(0, 0), alias=Velocity())
world.add_component(entity, pygame.Vector2(10, 10), alias=Position())
for ent, (vel, pos) in world.get_components(Velocity, Position):
    pos += vel

I know simplicity and performance is of high importance for this project, so I'd love to know if esper would or would not benefit from these changes!

benmoran56 commented 3 years ago

Hi @BonitaTerror, thanks for opening the pull request.

I have actually thought about something like this, but for a slightly different reason. In cases where a user might want to make custom subclasses, this type of pattern could also be used. For example:

class BaseClass:
    ...

class SubClassA(BaseClass):
    ...

world.add.component(entity, SubClassA(...), type_alias=BaseClass)
world.add.component(entity, SubClassB(...), type_alias=BaseClass)

Your proposal looks good, and seems like it has at least two use cases. I would suggest some slight tweaks however. It might be a bit wasteful to pass in a class instance every time, so it's probably best to just pass in the type directly:

world.add_component(entity, pygame.Vector2(0, 0), type_alias=Velocity)
world.add_component(entity, pygame.Vector2(10, 10), type_alias=Position)

Then inside the add_component method, just check for None:

   def add_component(self, entity, component_instance, type_alias=None):
        ...
        component_type = type_alias or type(component_instance)
        ...

What do you think?

BonitaTerror commented 3 years ago

@benmoran56, I like the subclass idea. As many times as I reviewed my change I hadn't thought of just passing the type rather than an instance :smile: I agree passing in the type would be much better. I made those changes with the correct type annotations

benmoran56 commented 3 years ago

Thanks for the updates! I'll get this merged in.