Ajout de vérification de types et de documentation #3
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
__pycache__
|
||||
.mypy_cache
|
3
.pylintrc
Normal file
3
.pylintrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
[MESSAGES CONTROL]
|
||||
disable=all
|
||||
enable= missing-docstring
|
8
.vscode/extensions.json
vendored
Normal file
8
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"matangover.mypy",
|
||||
"ms-python.pylint",
|
||||
"ms-python.black-formatter",
|
||||
"ms-python.python"
|
||||
]
|
||||
}
|
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
|
@ -6,7 +6,8 @@
|
|||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "main",
|
||||
"justMyCode": true
|
||||
"justMyCode": true,
|
||||
"preLaunchTask": "Python: Check",
|
||||
}
|
||||
]
|
||||
}
|
29
.vscode/tasks.json
vendored
Normal file
29
.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Python: MyPy",
|
||||
"type": "shell",
|
||||
"command": "mypy",
|
||||
"args": [
|
||||
"."
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Python: Pylint",
|
||||
"type": "shell",
|
||||
"command": "pylint",
|
||||
"args": [
|
||||
"**/*.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Python: Check",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"Python: MyPy",
|
||||
"Python: Pylint",
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
97
engine.py
97
engine.py
|
@ -1,23 +1,33 @@
|
|||
from typing import Iterator, Callable
|
||||
"""
|
||||
Système de gestion d'entités, composants et ressources.
|
||||
"""
|
||||
|
||||
from typing import Iterator, Callable, Tuple, TypeVar, Type, Optional
|
||||
from logging import error
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
"""
|
||||
Un type générique utilisé pour les composants et les ressources.
|
||||
"""
|
||||
|
||||
|
||||
class World:
|
||||
"""
|
||||
Un monde contenant des entités et des ressources.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
Permet de créer un nouveau monde vide.
|
||||
"""
|
||||
self._to_apply: set[Entity] = set()
|
||||
self._entities: set[Entity] = set()
|
||||
self._mapping: dict[type, set[Entity]] = {}
|
||||
self._to_apply_resources: list[tuple[type, object]] = []
|
||||
self._resources: dict[type, object] = {}
|
||||
self._mapping: dict[Type, set[Entity]] = {}
|
||||
self._to_apply_resources: list[tuple[Type, Optional[object]]] = []
|
||||
self._resources: dict[Type, object] = {}
|
||||
|
||||
def create_entity(self, *components):
|
||||
def create_entity(self, *components: _T) -> "Entity":
|
||||
"""
|
||||
Crée une entité avec les composants donnés en paramètres.
|
||||
|
||||
|
@ -29,7 +39,7 @@ class World:
|
|||
"""
|
||||
return Entity(self, *components)
|
||||
|
||||
def remove_entity(self, entity: "Entity"):
|
||||
def remove_entity(self, entity: "Entity") -> None:
|
||||
"""
|
||||
Supprime une entité du monde.
|
||||
|
||||
|
@ -39,7 +49,7 @@ class World:
|
|||
entity._deleted = True
|
||||
self._to_apply.add(entity)
|
||||
|
||||
def set(self, *resources):
|
||||
def set(self, *resources: _T) -> None:
|
||||
"""
|
||||
Définit les ressources données en paramètres.
|
||||
Si les ressources existent deja, elles seront remplacées.
|
||||
|
@ -50,7 +60,7 @@ class World:
|
|||
for resource in resources:
|
||||
self._to_apply_resources.append((type(resource), resource))
|
||||
|
||||
def remove(self, *resource_types):
|
||||
def remove(self, *resource_types: Type[_T]) -> None:
|
||||
"""
|
||||
Supprime les ressources données en paramètres.
|
||||
|
||||
|
@ -61,7 +71,7 @@ class World:
|
|||
if resource_type in self._resources:
|
||||
self._to_apply_resources.append((resource_type, None))
|
||||
|
||||
def apply(self):
|
||||
def apply(self) -> None:
|
||||
"""
|
||||
Applique les changements réaliser dans le monde.
|
||||
"""
|
||||
|
@ -77,7 +87,7 @@ class World:
|
|||
del entity._components[component_type]
|
||||
self._mapping[component_type].remove(entity)
|
||||
else:
|
||||
entity._components[component_type] = component
|
||||
entity._components[component_type] = component # type: ignore[assignment]
|
||||
self._mapping.setdefault(component_type, set()).add(entity)
|
||||
entity._to_apply.clear()
|
||||
self._to_apply.clear()
|
||||
|
@ -88,7 +98,9 @@ class World:
|
|||
self._resources[resource_type] = resource
|
||||
self._to_apply_resources.clear()
|
||||
|
||||
def query(self, *needed: type, without: tuple[type] = []) -> Iterator["Entity"]:
|
||||
def query(
|
||||
self, *needed: Type[_T], without: Tuple[Type[_T], ...] = ()
|
||||
) -> Iterator["Entity"]:
|
||||
"""
|
||||
Renvoie les entités qui ont les composants de *needed et sans les composants de *without.
|
||||
|
||||
|
@ -112,7 +124,7 @@ class World:
|
|||
) and all(without_type not in entity for without_type in without):
|
||||
yield entity
|
||||
|
||||
def __getitem__(self, resource_type: type) -> object:
|
||||
def __getitem__(self, resource_type: Type[_T]) -> _T:
|
||||
"""
|
||||
Renvoie la ressource de type *resource_type.
|
||||
|
||||
|
@ -122,19 +134,23 @@ class World:
|
|||
Retourne:
|
||||
La ressource de type *resource_type.
|
||||
"""
|
||||
return self._resources[resource_type]
|
||||
resource: _T = self._resources[resource_type] # type: ignore[assignment]
|
||||
return resource
|
||||
|
||||
def __contains__(self, resource_type: type) -> bool:
|
||||
def __contains__(self, *resource_types: Type[_T]) -> bool:
|
||||
"""
|
||||
Renvoie si la ressource de type *resource_type existe.
|
||||
Renvoie True si le monde contient toutes les ressources de *resource_types.
|
||||
|
||||
Paramètres:
|
||||
resource_type: Le type de ressource à tester.
|
||||
*resource_types: Les types de ressource à tester.
|
||||
|
||||
Retourne:
|
||||
Si la ressource de type *resource_type existe.
|
||||
Si toutes les ressources de *resource_types sont dans le monde.
|
||||
"""
|
||||
return resource_type in self._resources
|
||||
for resource_type in resource_types:
|
||||
if resource_type not in self._resources:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Entity:
|
||||
|
@ -142,7 +158,7 @@ class Entity:
|
|||
Une entité du monde.
|
||||
"""
|
||||
|
||||
def __init__(self, world: World, *components):
|
||||
def __init__(self, world: World, *components: _T):
|
||||
"""
|
||||
Créer une entité avec les composants en paramètres et l'ajoute au monde.
|
||||
|
||||
|
@ -151,14 +167,14 @@ class Entity:
|
|||
*components: Les composants de l'entité.
|
||||
"""
|
||||
self._world = world
|
||||
self._to_apply: list[tuple[type, object]] = [
|
||||
self._to_apply: list[tuple[Type, Optional[object]]] = [
|
||||
(type(component), component) for component in components
|
||||
]
|
||||
self._components: dict[type, object] = {}
|
||||
self._components: dict[Type[_T], _T] = {}
|
||||
self._deleted = False
|
||||
self._world._to_apply.add(self)
|
||||
|
||||
def set(self, *components):
|
||||
def set(self, *components: _T) -> None:
|
||||
"""
|
||||
Définit les composants de l'entité donnés en paramètres.
|
||||
|
||||
|
@ -169,7 +185,7 @@ class Entity:
|
|||
self._to_apply.append((type(component), component))
|
||||
self._world._to_apply.add(self)
|
||||
|
||||
def remove(self, *component_types: type):
|
||||
def remove(self, *component_types: Type[_T]) -> None:
|
||||
"""
|
||||
Supprime les composants de l'entité donnés en paramètres.
|
||||
|
||||
|
@ -181,7 +197,7 @@ class Entity:
|
|||
self._to_apply.append((component_type, None))
|
||||
self._world._to_apply.add(self)
|
||||
|
||||
def __getitem__(self, component_type: type) -> object:
|
||||
def __getitem__(self, component_type: Type[_T]) -> _T:
|
||||
"""
|
||||
Renvoie le composant de type *component_type.
|
||||
|
||||
|
@ -193,17 +209,20 @@ class Entity:
|
|||
"""
|
||||
return self._components[component_type]
|
||||
|
||||
def __contains__(self, component_type: type) -> bool:
|
||||
def __contains__(self, *component_types: Type[_T]) -> bool:
|
||||
"""
|
||||
Renvoie si le composant de type *component_type existe.
|
||||
Renvoie True si l'entité contient tous les composants de *component_types.
|
||||
|
||||
Paramètres:
|
||||
component_type: Le type du composant à tester.
|
||||
component_type: Les types des composants à tester.
|
||||
|
||||
Retourne:
|
||||
Si le composant de type *component_type existe.
|
||||
Si tous les composants de *component_types sont dans l'entité.
|
||||
"""
|
||||
return component_type in self._components
|
||||
for component_type in component_types:
|
||||
if component_type not in self._components:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Game:
|
||||
|
@ -211,7 +230,7 @@ class Game:
|
|||
Permet de faire une simple boucle de jeu.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
Créer une un jeu.
|
||||
"""
|
||||
|
@ -220,7 +239,7 @@ class Game:
|
|||
self._update_tasks: dict[int, list[Callable[[World], None]]] = {}
|
||||
self._shutdown_tasks: dict[int, list[Callable[[World], None]]] = {}
|
||||
|
||||
def add_startup_tasks(self, priority: int, *tasks: Callable[[World], None]):
|
||||
def add_startup_tasks(self, priority: int, *tasks: Callable[[World], None]) -> None:
|
||||
"""
|
||||
Ajoute des taches qui s'executeront au démarrage du jeu.
|
||||
|
||||
|
@ -232,7 +251,7 @@ class Game:
|
|||
raise RuntimeError("Cannot add startup task while the loop is running")
|
||||
self._startup_tasks.setdefault(priority, []).extend(tasks)
|
||||
|
||||
def add_tasks(self, priority: int, *tasks: Callable[[World], None]):
|
||||
def add_tasks(self, priority: int, *tasks: Callable[[World], None]) -> None:
|
||||
"""
|
||||
Ajoute des taches qui s'executeront a chaque mise à jour du jeu.
|
||||
|
||||
|
@ -244,7 +263,9 @@ class Game:
|
|||
raise RuntimeError("Cannot add task while the loop is running")
|
||||
self._update_tasks.setdefault(priority, []).extend(tasks)
|
||||
|
||||
def add_shutdown_tasks(self, priority: int, *tasks: Callable[[World], None]):
|
||||
def add_shutdown_tasks(
|
||||
self, priority: int, *tasks: Callable[[World], None]
|
||||
) -> None:
|
||||
"""
|
||||
Ajoute des taches qui s'executeront à la fin de la boucle de jeu.
|
||||
|
||||
|
@ -256,7 +277,9 @@ class Game:
|
|||
raise RuntimeError("Cannot add shutdown task while the loop is running")
|
||||
self._shutdown_tasks.setdefault(priority, []).extend(tasks)
|
||||
|
||||
def _run_tasks(self, tasks: dict[int, list[Callable[[World], None]]], world: World):
|
||||
def _run_tasks(
|
||||
self, tasks: dict[int, list[Callable[[World], None]]], world: World
|
||||
) -> None:
|
||||
"""
|
||||
Execute toutes les taches donnes en paramètres en respectant la priorité.
|
||||
"""
|
||||
|
@ -277,7 +300,7 @@ class Game:
|
|||
self._running = True
|
||||
|
||||
# On initialize le monde
|
||||
world = World()
|
||||
world: World = World()
|
||||
world.set(self)
|
||||
|
||||
# On applique les moddifications pour l'ajout de la ressource
|
||||
|
@ -305,7 +328,7 @@ class Game:
|
|||
# On retourne le monde
|
||||
return world
|
||||
|
||||
def stop(self):
|
||||
def stop(self) -> None:
|
||||
"""
|
||||
Demande la fin de la boucle de jeu. La boucle s'arretera a la prochaine mise à jour.
|
||||
"""
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
from engine import World # Doit être mis dans le dossier principale pour fonctionner
|
||||
|
||||
|
||||
# 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 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))
|
||||
|
||||
# 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):
|
||||
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])
|
||||
|
||||
# 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])
|
||||
|
||||
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])
|
||||
|
||||
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()
|
||||
|
||||
|
||||
# Création d'une ressource pouvant être ajoutée a un monde
|
||||
class Gravity(float):
|
||||
pass
|
||||
|
||||
|
||||
# On peut aussi ajouter des ressources globales
|
||||
world.set(Gravity(9.81))
|
||||
|
||||
print("On vérifie que la ressource Gravity existe")
|
||||
print(Gravity in world)
|
||||
|
||||
# On applique les moddifications
|
||||
world.apply()
|
||||
|
||||
print("On vérifie que la ressource Gravity existe après l'application")
|
||||
print(Gravity in world)
|
||||
|
||||
print("Récupération de la ressource Gravity")
|
||||
print(world[Gravity])
|
||||
|
||||
# On supprime la ressource Gravity
|
||||
world.remove(Gravity)
|
||||
|
||||
print("On vérifie que la ressource Gravity n'existe plus")
|
||||
print(Gravity in world)
|
||||
|
||||
# On applique les moddifications
|
||||
world.apply()
|
||||
|
||||
print("On vérifie que la ressource Gravity n'existe plus")
|
||||
print(Gravity in world)
|
5
main.py
5
main.py
|
@ -1,3 +1,8 @@
|
|||
"""
|
||||
Ceci est un exemple de comment l'on peut utiliser le moteur du jeu.
|
||||
"""
|
||||
|
||||
|
||||
from engine import Game
|
||||
|
||||
|
||||
|
|
3
mypy.ini
Normal file
3
mypy.ini
Normal file
|
@ -0,0 +1,3 @@
|
|||
[mypy]
|
||||
check_untyped_defs = True
|
||||
disallow_untyped_defs = True
|
|
@ -0,0 +1,2 @@
|
|||
mypy
|
||||
pylint
|
Loading…
Reference in a new issue