ecs #58
BIN
assets/loaded.png
Normal file
BIN
assets/loaded.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
BIN
assets/unloaded.png
Normal file
BIN
assets/unloaded.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
|
@ -3,7 +3,7 @@ Permet de lancer un jeu et de gérer la boucle principale de celui-ci.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from typing import Callable
|
from typing import Callable, Optional
|
||||||
from engine.ecs import World
|
from engine.ecs import World
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ class Scene:
|
||||||
init_systems: list[Callable[[World], object]],
|
init_systems: list[Callable[[World], object]],
|
||||||
update_systems: list[Callable[[World], object]],
|
update_systems: list[Callable[[World], object]],
|
||||||
stop_systems: list[Callable[[World], object]],
|
stop_systems: list[Callable[[World], object]],
|
||||||
):
|
) -> None:
|
||||||
self.init_systems = init_systems
|
self.init_systems = init_systems
|
||||||
self.update_systems = update_systems
|
self.update_systems = update_systems
|
||||||
self.stop_systems = stop_systems
|
self.stop_systems = stop_systems
|
||||||
|
@ -68,13 +68,21 @@ class KeepAlive:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class CurrentScene(KeepAlive, str):
|
class CurrentScene(KeepAlive):
|
||||||
"""
|
"""
|
||||||
Resource qui permet de savoir et de changer la scène actuelle.
|
Resource qui permet de savoir et de changer la scène actuelle.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, scene: Scene):
|
||||||
|
self.scene = scene
|
||||||
|
|
||||||
def start_game(global_scene: GlobalScene, scenes: dict[str, Scene], scene_name: str):
|
def __eq__(self, value: object) -> bool:
|
||||||
|
if isinstance(value, CurrentScene):
|
||||||
|
return self.scene == value.scene
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def start_game(global_scene: GlobalScene, scene: Optional[Scene]):
|
||||||
"""
|
"""
|
||||||
Lance un jeu.
|
Lance un jeu.
|
||||||
|
|
||||||
|
@ -84,14 +92,14 @@ def start_game(global_scene: GlobalScene, scenes: dict[str, Scene], scene_name:
|
||||||
"""
|
"""
|
||||||
# On création du monde
|
# On création du monde
|
||||||
world = World()
|
world = World()
|
||||||
world[CurrentScene] = CurrentScene(scene_name)
|
if scene is not None:
|
||||||
|
world[CurrentScene] = CurrentScene(scene)
|
||||||
|
|
||||||
# On initialise la scène globale
|
# On initialise la scène globale
|
||||||
for system in global_scene.init_systems:
|
for system in global_scene.init_systems:
|
||||||
system(world)
|
system(world)
|
||||||
|
|
||||||
# Boucle principale
|
# Boucle principale
|
||||||
scene = scenes.get(scene_name)
|
|
||||||
while scene is not None:
|
while scene is not None:
|
||||||
# On retire les ressources de l'ancienne scène
|
# On retire les ressources de l'ancienne scène
|
||||||
for resource in world:
|
for resource in world:
|
||||||
|
@ -107,7 +115,7 @@ def start_game(global_scene: GlobalScene, scenes: dict[str, Scene], scene_name:
|
||||||
system(world)
|
system(world)
|
||||||
|
|
||||||
# Tant que la scène n'est pas terminé on la met à jour
|
# Tant que la scène n'est pas terminé on la met à jour
|
||||||
while world.get(CurrentScene, "") == scene_name:
|
while world.get(CurrentScene, ()) == CurrentScene(scene):
|
||||||
# On met à jour la scène globale
|
# On met à jour la scène globale
|
||||||
for system in global_scene.pre_update_systems:
|
for system in global_scene.pre_update_systems:
|
||||||
system(world)
|
system(world)
|
||||||
|
@ -124,8 +132,12 @@ def start_game(global_scene: GlobalScene, scenes: dict[str, Scene], scene_name:
|
||||||
for system in scene.stop_systems:
|
for system in scene.stop_systems:
|
||||||
system(world)
|
system(world)
|
||||||
|
|
||||||
# Récupération de la scène suivante
|
# On met à jour la scène
|
||||||
scene = scenes.get(world.get(CurrentScene, ""))
|
current_scene = world.get(CurrentScene, ())
|
||||||
|
if isinstance(current_scene, tuple):
|
||||||
|
scene = None
|
||||||
|
else:
|
||||||
|
scene = current_scene.scene
|
||||||
|
|
||||||
# On arrête la scène globale
|
# On arrête la scène globale
|
||||||
for system in global_scene.stop_systems:
|
for system in global_scene.stop_systems:
|
||||||
|
|
10
src/main.py
10
src/main.py
|
@ -3,13 +3,7 @@ Module d'exemple de l'utilisation du moteur de jeu.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from engine import Scene, start_game
|
from engine import Scene, start_game
|
||||||
from plugins import defaults
|
from plugins import assets, defaults
|
||||||
|
|
||||||
|
|
||||||
start_game(
|
start_game(defaults.PLUGIN, assets.loading_scene(Scene([], [], []), "textures"))
|
||||||
defaults.PLUGIN,
|
|
||||||
{
|
|
||||||
"menu": Scene([], [], []),
|
|
||||||
},
|
|
||||||
"menu",
|
|
||||||
)
|
|
||||||
|
|
23
src/plugins/animation.py
Normal file
23
src/plugins/animation.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
"""
|
||||||
|
Un plugin qui permet de jouer des animations de sprites.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from engine import GlobalScene
|
||||||
|
|
||||||
|
|
||||||
|
class Animation:
|
||||||
|
"""
|
||||||
|
Composant qui contient toutes les informations d'une animation est en cour
|
||||||
|
sur l'entité.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN = GlobalScene(
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
)
|
161
src/plugins/assets.py
Normal file
161
src/plugins/assets.py
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
"""
|
||||||
|
Un plugin qui gère les ressources du jeu.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import pygame
|
||||||
|
from engine import CurrentScene, GlobalScene, KeepAlive, Scene
|
||||||
|
from engine.ecs import World
|
||||||
|
from plugins import render
|
||||||
|
|
||||||
|
|
||||||
|
class Assets(KeepAlive):
|
||||||
|
"""
|
||||||
|
Ressource qui gère les assets du jeu.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# Création de la texture d'erreur
|
||||||
|
error_texture = pygame.Surface((256, 256))
|
||||||
|
error_texture.fill((0, 0, 0))
|
||||||
|
pygame.draw.rect(error_texture, (255, 0, 255), (0, 0, 128, 128))
|
||||||
|
pygame.draw.rect(error_texture, (255, 0, 255), (128, 128, 128, 128))
|
||||||
|
self.__error_texture = error_texture.convert()
|
||||||
|
|
||||||
|
# Chargement des textures de chargement
|
||||||
|
self.__unloaded_texture = pygame.image.load("assets/unloaded.png").convert()
|
||||||
|
self.__loaded_texture = pygame.image.load("assets/loaded.png").convert()
|
||||||
|
|
||||||
|
# Cache des ressources
|
||||||
|
self.__textures: dict[str, pygame.Surface] = {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unloaded_texture(self) -> pygame.Surface:
|
||||||
|
return self.__unloaded_texture
|
||||||
|
|
||||||
|
@property
|
||||||
|
def loaded_texture(self) -> pygame.Surface:
|
||||||
|
return self.__loaded_texture
|
||||||
|
|
||||||
|
def load_texture(self, name: str, path: str) -> pygame.Surface:
|
||||||
|
"""
|
||||||
|
Charge une texture et la renvoi. Si une texture existe déja dans le cache,
|
||||||
|
elle sera remplacée par la nouvelle.
|
||||||
|
"""
|
||||||
|
surface = pygame.image.load(path).convert_alpha()
|
||||||
|
self.__textures[name] = surface
|
||||||
|
return surface
|
||||||
|
|
||||||
|
def get_texture(self, name: str) -> pygame.Surface:
|
||||||
|
"""
|
||||||
|
Renvoie la texture demandée.
|
||||||
|
"""
|
||||||
|
return self.__textures.get(name, self.__error_texture)
|
||||||
|
|
||||||
|
def clear_cache(self):
|
||||||
|
"""
|
||||||
|
Vide le cache des assets.
|
||||||
|
"""
|
||||||
|
self.__textures.clear()
|
||||||
|
|
||||||
|
|
||||||
|
def __initialize(world: World):
|
||||||
|
"""
|
||||||
|
Ajoute la ressource `Assets` au monde.
|
||||||
|
"""
|
||||||
|
world.set(Assets())
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN = GlobalScene(
|
||||||
|
[__initialize],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def loading_scene(target: Scene, name: str):
|
||||||
|
"""
|
||||||
|
Retourne une scène de chargement des assets qui passe à la scène donné
|
||||||
|
en paramètres lorsque tous les assets de la scène sont chargées.
|
||||||
|
|
||||||
|
Paramètres:
|
||||||
|
- `target`: la scène qui sera lancé après le chargement des assets.
|
||||||
|
- `name`: le nom de la scène, ce nom est utilisé pour savoir dans quel
|
||||||
|
dossier sont les assets de la scène. Les assets de la scène
|
||||||
|
seront récupéré dans le dossier `assets/<name>`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class AssetIterator:
|
||||||
|
"""
|
||||||
|
Une ressource qui contient un itérateur sur les fichiers des assets
|
||||||
|
de la scène à charger.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.files = glob.glob(f"assets/{name}/**/*", recursive=True)
|
||||||
|
self.total = len(self.files)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def prepare_world(world: World):
|
||||||
|
"""
|
||||||
|
Retire toutes les ressource précédentes du monde puis
|
||||||
|
ajoute `ResourceIterator` et la barre de progression dans le monde.
|
||||||
|
"""
|
||||||
|
assets = world[Assets]
|
||||||
|
assets.clear_cache()
|
||||||
|
world.set(AssetIterator())
|
||||||
|
world.new_entity().set(
|
||||||
|
render.Sprite(assets.unloaded_texture, order=100000000000)
|
||||||
|
)
|
||||||
|
world.new_entity().set(
|
||||||
|
ProgessBar(),
|
||||||
|
render.Sprite(
|
||||||
|
assets.loaded_texture,
|
||||||
|
order=100000000001,
|
||||||
|
area=(0, 0, 0, render.HEIGHT),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_next(world: World):
|
||||||
|
"""
|
||||||
|
Charge le fichier suivant de l'itérateur.
|
||||||
|
"""
|
||||||
|
asset_iterator = world[AssetIterator]
|
||||||
|
if len(asset_iterator.files) == 0:
|
||||||
|
world[CurrentScene] = target
|
||||||
|
else:
|
||||||
|
file = asset_iterator.files.pop().replace("\\", "/")
|
||||||
|
ressource_extension = file.split(".")[-1]
|
||||||
|
ressource_name = "/".join(file.split("/")[2:])[
|
||||||
|
: -len(ressource_extension) - 1
|
||||||
|
]
|
||||||
|
if ressource_extension in ("png", "jpg"):
|
||||||
|
world[Assets].load_texture(ressource_name, file)
|
||||||
|
|
||||||
|
class ProgessBar:
|
||||||
|
"""
|
||||||
|
Composant marquant une entité comme étant une barre de progression.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def render(world: World):
|
||||||
|
"""
|
||||||
|
Affiche une barre de progression du chargement des assets.
|
||||||
|
"""
|
||||||
|
# Calcul du pourcentage de chargement
|
||||||
|
asset_iterator = world[AssetIterator]
|
||||||
|
file_loaded = asset_iterator.total - len(asset_iterator.files)
|
||||||
|
progress = file_loaded / asset_iterator.total
|
||||||
|
pixels = int(render.WIDTH * progress)
|
||||||
|
|
||||||
|
# Affichage de la barre de progression
|
||||||
|
progress_bar = world.query(ProgessBar).pop()
|
||||||
|
progress_bar[render.Sprite].area = (0, 0, pixels, render.HEIGHT)
|
||||||
|
|
||||||
|
return Scene(
|
||||||
|
[AssetIterator.prepare_world],
|
||||||
|
[AssetIterator.load_next, ProgessBar.render],
|
||||||
|
[],
|
||||||
|
)
|
|
@ -2,7 +2,7 @@
|
||||||
Plugin qui rassemple tous les plugins globaux.
|
Plugin qui rassemple tous les plugins globaux.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from plugins import display, inputs, render
|
from plugins import assets, display, inputs, render
|
||||||
|
|
||||||
|
|
||||||
PLUGIN = display.PLUGIN + inputs.PLUGIN + render.PLUGIN
|
PLUGIN = display.PLUGIN + assets.PLUGIN + inputs.PLUGIN + render.PLUGIN
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Un plugin qui s'occupe de rendre des choses dans la fenetre.
|
Un plugin qui s'occupe de rendre des choses dans la fenetre.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
import pygame
|
import pygame
|
||||||
from engine import GlobalScene, KeepAlive
|
from engine import GlobalScene, KeepAlive
|
||||||
from engine.ecs import World
|
from engine.ecs import World
|
||||||
|
@ -40,11 +41,24 @@ class Sprite:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, surface: pygame.Surface, position: Vec2 = Vec2(0), order: float = -1.0
|
self,
|
||||||
|
surface: pygame.Surface,
|
||||||
|
position: Vec2 = Vec2(0),
|
||||||
|
order: float = -1.0,
|
||||||
|
area: Optional[tuple[float, float, float, float]] = None,
|
||||||
):
|
):
|
||||||
self.surface = surface
|
self.surface = surface
|
||||||
self.position = position
|
self.position = position
|
||||||
self.order = order
|
self.order = order
|
||||||
|
if area is None:
|
||||||
|
self.area = (
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
float(surface.get_width()),
|
||||||
|
float(surface.get_height()),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.area = area
|
||||||
|
|
||||||
|
|
||||||
def __initialize(world: World):
|
def __initialize(world: World):
|
||||||
|
@ -62,7 +76,11 @@ def __render(world: World):
|
||||||
surface = world[Surface]
|
surface = world[Surface]
|
||||||
sprites = [entity[Sprite] for entity in world.query(Sprite)]
|
sprites = [entity[Sprite] for entity in world.query(Sprite)]
|
||||||
for sprite in sorted(sprites, key=lambda sprite: sprite.order):
|
for sprite in sorted(sprites, key=lambda sprite: sprite.order):
|
||||||
surface.blit(sprite.surface, (sprite.position.x, sprite.position.y))
|
surface.blit(
|
||||||
|
sprite.surface,
|
||||||
|
(sprite.position.x, sprite.position.y),
|
||||||
|
sprite.area,
|
||||||
|
)
|
||||||
|
|
||||||
# On affiche la surface sur la fenetre
|
# On affiche la surface sur la fenetre
|
||||||
rect = calculate_surface_rect()
|
rect = calculate_surface_rect()
|
||||||
|
|
Loading…
Reference in a new issue