Système d'entités et de composants #1
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"[python]": {
|
||||||
|
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||||
|
}
|
||||||
|
}
|
44
ecs.py
44
ecs.py
|
@ -15,7 +15,7 @@ class World:
|
||||||
self._mapping: dict[type, set[Entity]] = {}
|
self._mapping: dict[type, set[Entity]] = {}
|
||||||
self._to_apply_resources: list[tuple[type, object]] = []
|
self._to_apply_resources: list[tuple[type, object]] = []
|
||||||
self._resources: dict[type, object] = {}
|
self._resources: dict[type, object] = {}
|
||||||
|
|
||||||
def create_entity(self, *components):
|
def create_entity(self, *components):
|
||||||
"""
|
"""
|
||||||
Crée une entité avec les composants donnés en paramètres.
|
Crée une entité avec les composants donnés en paramètres.
|
||||||
|
@ -28,7 +28,7 @@ class World:
|
||||||
"""
|
"""
|
||||||
return Entity(self, *components)
|
return Entity(self, *components)
|
||||||
|
|
||||||
def remove_entity(self, entity: 'Entity'):
|
def remove_entity(self, entity: "Entity"):
|
||||||
"""
|
"""
|
||||||
Supprime une entité du monde.
|
Supprime une entité du monde.
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class World:
|
||||||
"""
|
"""
|
||||||
entity._deleted = True
|
entity._deleted = True
|
||||||
self._to_apply.add(entity)
|
self._to_apply.add(entity)
|
||||||
|
|
||||||
def set(self, *resources):
|
def set(self, *resources):
|
||||||
"""
|
"""
|
||||||
Définit les ressources données en paramètres.
|
Définit les ressources données en paramètres.
|
||||||
|
@ -48,7 +48,7 @@ class World:
|
||||||
"""
|
"""
|
||||||
for resource in resources:
|
for resource in resources:
|
||||||
self._to_apply_resources.append((type(resource), resource))
|
self._to_apply_resources.append((type(resource), resource))
|
||||||
|
|
||||||
def remove(self, *resource_types):
|
def remove(self, *resource_types):
|
||||||
"""
|
"""
|
||||||
Supprime les ressources données en paramètres.
|
Supprime les ressources données en paramètres.
|
||||||
|
@ -81,14 +81,16 @@ class World:
|
||||||
entity._to_apply.clear()
|
entity._to_apply.clear()
|
||||||
self._to_apply.clear()
|
self._to_apply.clear()
|
||||||
for resource_type, resource in self._to_apply_resources:
|
for resource_type, resource in self._to_apply_resources:
|
||||||
if resource is None: del self._resources[resource_type]
|
if resource is None:
|
||||||
else: self._resources[resource_type] = resource
|
del self._resources[resource_type]
|
||||||
|
else:
|
||||||
|
self._resources[resource_type] = resource
|
||||||
self._to_apply_resources.clear()
|
self._to_apply_resources.clear()
|
||||||
|
|
||||||
def query(self, *needed: type, without: tuple[type] = []) -> Iterator['Entity']:
|
def query(self, *needed: type, without: tuple[type] = []) -> Iterator["Entity"]:
|
||||||
"""
|
"""
|
||||||
Renvoie les entités qui ont les composants de *needed et sans les composants de *without.
|
Renvoie les entités qui ont les composants de *needed et sans les composants de *without.
|
||||||
|
|
||||||
Paramètres:
|
Paramètres:
|
||||||
*needed: Le type de composants que les entités doivent avoir.
|
*needed: Le type de composants que les entités doivent avoir.
|
||||||
*without: Les type de composants que les entités ne doivent pas avoir.
|
*without: Les type de composants que les entités ne doivent pas avoir.
|
||||||
|
@ -103,10 +105,12 @@ class World:
|
||||||
yield entity
|
yield entity
|
||||||
else:
|
else:
|
||||||
for entity in self._mapping.get(needed[0], set()):
|
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 \
|
if all(
|
||||||
all(without_type not in entity for without_type in without):
|
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
|
yield entity
|
||||||
|
|
||||||
def __getitem__(self, resource_type: type) -> object:
|
def __getitem__(self, resource_type: type) -> object:
|
||||||
"""
|
"""
|
||||||
Renvoie la ressource de type *resource_type.
|
Renvoie la ressource de type *resource_type.
|
||||||
|
@ -118,7 +122,7 @@ class World:
|
||||||
La ressource de type *resource_type.
|
La ressource de type *resource_type.
|
||||||
"""
|
"""
|
||||||
return self._resources[resource_type]
|
return self._resources[resource_type]
|
||||||
|
|
||||||
def __contains__(self, resource_type: type) -> bool:
|
def __contains__(self, resource_type: type) -> bool:
|
||||||
"""
|
"""
|
||||||
Renvoie si la ressource de type *resource_type existe.
|
Renvoie si la ressource de type *resource_type existe.
|
||||||
|
@ -131,10 +135,12 @@ class World:
|
||||||
"""
|
"""
|
||||||
return resource_type in self._resources
|
return resource_type in self._resources
|
||||||
|
|
||||||
|
|
||||||
class Entity:
|
class Entity:
|
||||||
"""
|
"""
|
||||||
Une entité du monde.
|
Une entité du monde.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, world: World, *components):
|
def __init__(self, world: World, *components):
|
||||||
"""
|
"""
|
||||||
Créer une entité avec les composants en paramètres et l'ajoute au monde.
|
Créer une entité avec les composants en paramètres et l'ajoute au monde.
|
||||||
|
@ -144,11 +150,13 @@ class Entity:
|
||||||
*components: Les composants de l'entité.
|
*components: Les composants de l'entité.
|
||||||
"""
|
"""
|
||||||
self._world = world
|
self._world = world
|
||||||
self._to_apply: list[tuple[type, object]] = [(type(component), component) for component in components]
|
self._to_apply: list[tuple[type, object]] = [
|
||||||
|
(type(component), component) for component in components
|
||||||
|
]
|
||||||
self._components: dict[type, object] = {}
|
self._components: dict[type, object] = {}
|
||||||
self._deleted = False
|
self._deleted = False
|
||||||
self._world._to_apply.add(self)
|
self._world._to_apply.add(self)
|
||||||
|
|
||||||
def set(self, *components):
|
def set(self, *components):
|
||||||
"""
|
"""
|
||||||
Définit les composants de l'entité donnés en paramètres.
|
Définit les composants de l'entité donnés en paramètres.
|
||||||
|
@ -159,7 +167,7 @@ class Entity:
|
||||||
for component in components:
|
for component in components:
|
||||||
self._to_apply.append((type(component), component))
|
self._to_apply.append((type(component), component))
|
||||||
self._world._to_apply.add(self)
|
self._world._to_apply.add(self)
|
||||||
|
|
||||||
def remove(self, *component_types: type):
|
def remove(self, *component_types: type):
|
||||||
"""
|
"""
|
||||||
Supprime les composants de l'entité donnés en paramètres.
|
Supprime les composants de l'entité donnés en paramètres.
|
||||||
|
@ -171,7 +179,7 @@ class Entity:
|
||||||
if component_type in self._components:
|
if component_type in self._components:
|
||||||
self._to_apply.append((component_type, None))
|
self._to_apply.append((component_type, None))
|
||||||
self._world._to_apply.add(self)
|
self._world._to_apply.add(self)
|
||||||
|
|
||||||
def __getitem__(self, component_type: type) -> object:
|
def __getitem__(self, component_type: type) -> object:
|
||||||
"""
|
"""
|
||||||
Renvoie le composant de type *component_type.
|
Renvoie le composant de type *component_type.
|
||||||
|
@ -194,4 +202,4 @@ class Entity:
|
||||||
Retourne:
|
Retourne:
|
||||||
Si le composant de type *component_type existe.
|
Si le composant de type *component_type existe.
|
||||||
"""
|
"""
|
||||||
return component_type in self._components
|
return component_type in self._components
|
||||||
|
|
16
main.py
16
main.py
|
@ -1,8 +1,14 @@
|
||||||
from ecs import World
|
from ecs import World
|
||||||
|
|
||||||
|
|
||||||
# Création de composants pouvant être ajouté a des entitées
|
# Création de composants pouvant être ajouté a des entitées
|
||||||
class Name(str): pass
|
class Name(str):
|
||||||
class Age(int): pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Age(int):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# Création d'un monde
|
# Création d'un monde
|
||||||
world = World()
|
world = World()
|
||||||
|
@ -50,8 +56,11 @@ for entity in world.query():
|
||||||
print(entity[Age], end=" ")
|
print(entity[Age], end=" ")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
# Création d'une ressource pouvant être ajoutée a un monde
|
# Création d'une ressource pouvant être ajoutée a un monde
|
||||||
class Gravity(float): pass
|
class Gravity(float):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# On peut aussi ajouter des ressources globales
|
# On peut aussi ajouter des ressources globales
|
||||||
world.set(Gravity(9.81))
|
world.set(Gravity(9.81))
|
||||||
|
@ -79,4 +88,3 @@ world.apply()
|
||||||
|
|
||||||
print("On vérifie que la ressource Gravity n'existe plus")
|
print("On vérifie que la ressource Gravity n'existe plus")
|
||||||
print(Gravity in world)
|
print(Gravity in world)
|
||||||
|
|
||||||
|
|
29
tasks.py
Normal file
29
tasks.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
from typing import Callable
|
||||||
|
from ecs import World
|
||||||
|
|
||||||
|
|
||||||
|
class TaskManager:
|
||||||
|
"""
|
||||||
|
Contient des fonctions de logique s'appliquant sur un monde.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Créer un gestionnaire de taches vide.
|
||||||
|
"""
|
||||||
|
self.tasks: dict[int, list[Callable[[World], None]]] = {}
|
||||||
|
|
||||||
|
def add(self, priority: int, task: Callable[[World], None]):
|
||||||
|
"""
|
||||||
|
Ajoute une tache à un gestionnaire de taches.
|
||||||
|
"""
|
||||||
|
self.tasks.setdefault(priority, []).append(task)
|
||||||
|
|
||||||
|
def run(self, world: World):
|
||||||
|
"""
|
||||||
|
Exécute toutes les taches.
|
||||||
|
"""
|
||||||
|
priorities = sorted(list(self.tasks.keys()))
|
||||||
|
for priority in priorities:
|
||||||
|
for task in self.tasks[priority]:
|
||||||
|
task(world)
|
Loading…
Reference in a new issue