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
|
||||
|
||||
|
||||
|
@ -46,7 +46,7 @@ class Scene:
|
|||
init_systems: list[Callable[[World], object]],
|
||||
update_systems: list[Callable[[World], object]],
|
||||
stop_systems: list[Callable[[World], object]],
|
||||
):
|
||||
) -> None:
|
||||
self.init_systems = init_systems
|
||||
self.update_systems = update_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.
|
||||
"""
|
||||
|
||||
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.
|
||||
|
||||
|
@ -84,14 +92,14 @@ def start_game(global_scene: GlobalScene, scenes: dict[str, Scene], scene_name:
|
|||
"""
|
||||
# On création du monde
|
||||
world = World()
|
||||
world[CurrentScene] = CurrentScene(scene_name)
|
||||
if scene is not None:
|
||||
world[CurrentScene] = CurrentScene(scene)
|
||||
|
||||
# On initialise la scène globale
|
||||
for system in global_scene.init_systems:
|
||||
system(world)
|
||||
|
||||
# Boucle principale
|
||||
scene = scenes.get(scene_name)
|
||||
while scene is not None:
|
||||
# On retire les ressources de l'ancienne scène
|
||||
for resource in world:
|
||||
|
@ -107,7 +115,7 @@ def start_game(global_scene: GlobalScene, scenes: dict[str, Scene], scene_name:
|
|||
system(world)
|
||||
|
||||
# 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
|
||||
for system in global_scene.pre_update_systems:
|
||||
system(world)
|
||||
|
@ -124,8 +132,12 @@ def start_game(global_scene: GlobalScene, scenes: dict[str, Scene], scene_name:
|
|||
for system in scene.stop_systems:
|
||||
system(world)
|
||||
|
||||
# Récupération de la scène suivante
|
||||
scene = scenes.get(world.get(CurrentScene, ""))
|
||||
# On met à jour la scène
|
||||
current_scene = world.get(CurrentScene, ())
|
||||
if isinstance(current_scene, tuple):
|
||||
scene = None
|
||||
else:
|
||||
scene = current_scene.scene
|
||||
|
||||
# On arrête la scène globale
|
||||
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 plugins import defaults
|
||||
from plugins import assets, defaults
|
||||
|
||||
|
||||
start_game(
|
||||
defaults.PLUGIN,
|
||||
{
|
||||
"menu": Scene([], [], []),
|
||||
},
|
||||
"menu",
|
||||
)
|
||||
start_game(defaults.PLUGIN, assets.loading_scene(Scene([], [], []), "textures"))
|
||||
|
|
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.
|
||||
"""
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
import pygame
|
||||
from engine import GlobalScene, KeepAlive
|
||||
from engine.ecs import World
|
||||
|
@ -40,11 +41,24 @@ class Sprite:
|
|||
"""
|
||||
|
||||
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.position = position
|
||||
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):
|
||||
|
@ -62,7 +76,11 @@ def __render(world: World):
|
|||
surface = world[Surface]
|
||||
sprites = [entity[Sprite] for entity in world.query(Sprite)]
|
||||
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
|
||||
rect = calculate_surface_rect()
|
||||
|
|
Loading…
Reference in a new issue