diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed8ebf5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7b0016e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Module", + "type": "python", + "request": "launch", + "module": "main", + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/ecs.py b/ecs.py new file mode 100644 index 0000000..8468a2e --- /dev/null +++ b/ecs.py @@ -0,0 +1,57 @@ +from typing import Iterator + + +class World: + def __init__(self): + self.entities: set[Entity] = set() + self.mapping: dict[type, set[Entity]] = {} + + 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: + 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 + +class Entity: + def __init__(self, world: World): + self.world = world + self.world.entities.add(self) + self.components: dict[type, object] = {} + + 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] diff --git a/main.py b/main.py new file mode 100644 index 0000000..cdc7757 --- /dev/null +++ b/main.py @@ -0,0 +1,49 @@ +from ecs import World, Entity + +# Création de composants pouvant être ajouté a des entitées +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 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) + +print("Récupération de toutes les entitées qui ont un nom") +for entity in world.query(Name): + print(entity[Name]) + +print("Récupération de toutes les entitées qui ont un age") +for entity in world.query(Age): + print(entity[Age]) + +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]) + +print("Récupération de toutes les entitées qui ont un nom mais pas d'age") +for entity in world.query(Name, without=(Age,)): + print(entity[Name]) + +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]) + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29