diff --git a/ecs.py b/ecs.py index 8468a2e..1a6539b 100644 --- a/ecs.py +++ b/ecs.py @@ -3,55 +3,66 @@ from typing import Iterator class World: def __init__(self): + self.to_apply: set[Entity] = set() self.entities: set[Entity] = set() self.mapping: dict[type, set[Entity]] = {} + + def create_entity(self, *components): + return Entity(self, *components) + def remove_entity(self, entity: 'Entity'): + entity._deleted = True + self.to_apply.add(entity) + + def apply(self): + for entity in self.to_apply: + if entity._deleted: + self.entities.remove(entity) + for component_type in entity._components: + self.mapping[component_type].remove(entity) + else: + self.entities.add(entity) + for component_type, component in entity._to_apply: + if component is None: + del entity._components[component_type] + self.mapping[component_type].remove(entity) + else: + entity._components[component_type] = component + self.mapping.setdefault(component_type, set()).add(entity) + entity._to_apply.clear() + def query(self, *needed: type, without: tuple[type] = []) -> Iterator['Entity']: if not needed: for entity in self.entities: - for without_type in without: - if without_type in entity: - break - else: + if all(without_type not in entity for without_type in without): yield entity else: - first_component = needed[0] - for entity in self.mapping.get(first_component, set()): - for component_type in needed[1:]: - if entity not in self.mapping.get(component_type, set()): - break - else: - for without_type in without: - if without_type in entity: - break - else: - yield entity + for entity in self.mapping.get(needed[0], set()): + if all(entity in self.mapping.get(component_type, set()) for component_type in needed[1:]) and \ + all(without_type not in entity for without_type in without): + yield entity class Entity: - def __init__(self, world: World): - self.world = world - self.world.entities.add(self) - self.components: dict[type, object] = {} + def __init__(self, world: World, *components): + self._world = world + self._to_apply: list[tuple[type, object]] = [(type(component), component) for component in components] + self._components: dict[type, object] = {} + self._deleted = False + self._world.to_apply.add(self) + + def set(self, *components): + for component in components: + self._to_apply.append((type(component), component)) + self._world.to_apply.add(self) + + def remove(self, *component_types: type): + for component_type in component_types: + if component_type in self._components: + self._to_apply.append((component_type, None)) + self._world.to_apply.add(self) + + def __getitem__(self, component_type: type) -> object: + return self._components[component_type] - def __getitem__(self, component_type): - return self.components[component_type] - - def __setitem__(self, component_type, component): - self.components[component_type] = component - self.world.mapping.setdefault(component_type, set()).add(self) - - def __delitem__(self, component_type): - del self.components[component_type] - self.world.mapping[component_type].remove(self) - if len(self.world.mapping[component_type]) == 0: - del self.world.mapping[component_type] - - def __contains__(self, component_type): - return component_type in self.components - - def __del__(self): # TODO: Réparer ça marche pas car il n'est pas détruit car il est toujours dans World - self.world.entities.remove(self) - for component_type in self.components: - self.world.mapping[component_type].remove(self) - if len(self.world.mapping[component_type]) == 0: - del self.world.mapping[component_type] + def __contains__(self, component_type: type) -> bool: + return component_type in self._components \ No newline at end of file diff --git a/main.py b/main.py index cdc7757..e05b7eb 100644 --- a/main.py +++ b/main.py @@ -1,31 +1,20 @@ -from ecs import World, Entity +from ecs import World # Création de composants pouvant être ajouté a des entitées -class Name(str): - pass -class Age(int): - pass +class Name(str): pass +class Age(int): pass # Création d'un monde world = World() -# Création d'une entité -david = Entity(world) -david[Name] = Name("David") -david[Age] = Age(25) +# Création de plusieurs entités +david = world.create_entity(Name("David"), Age(25)) +fred = world.create_entity(Name("Fred"), Age(30)) +paul_sans_age = world.create_entity(Name("Paul")) +age_tout_cour = world.create_entity(Age(14)) -# Création d'une autre entité -fred = Entity(world) -fred[Name] = Name("Fred") -fred[Age] = Age(30) - -# Création d'une autre entité -paul_sans_age = Entity(world) -paul_sans_age[Name] = Name("Paul") - -# Création d'une autre entité -age_tout_cour = Entity(world) -age_tout_cour[Age] = Age(14) +# On applique les moddifications +world.apply() print("Récupération de toutes les entitées qui ont un nom") for entity in world.query(Name): @@ -35,6 +24,12 @@ print("Récupération de toutes les entitées qui ont un age") for entity in world.query(Age): print(entity[Age]) +# On change l'age de Fred +fred.set(Age(45)) + +# On applique les moddifications +world.apply() + print("Récupération de toutes les entités qui ont un nom et un age") for entity in world.query(Name, Age): print(entity[Name], entity[Age]) @@ -47,3 +42,11 @@ print("Récupération de toutes les entités qui ont un age mais pas de nom") for entity in world.query(Age, without=(Name,)): print(entity[Age]) +print("Récupération de toutes les entités") +for entity in world.query(): + if Name in entity: + print(entity[Name], end=" ") + if Age in entity: + print(entity[Age], end=" ") + print() +