WIP
This commit is contained in:
parent
6fa1b5c70f
commit
bac035f00d
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
@ -6,7 +6,7 @@ Pour moddifier le monde, on n'agis que sur les composants.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from typing import Iterator, Optional, Sequence, TypeVar
|
from typing import Iterator, Optional, Sequence
|
||||||
|
|
||||||
|
|
||||||
class Entity:
|
class Entity:
|
||||||
|
@ -17,15 +17,6 @@ class Entity:
|
||||||
utilitaire pour acceder au monde plus facilement.
|
utilitaire pour acceder au monde plus facilement.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__T = TypeVar("__T")
|
|
||||||
"""
|
|
||||||
Ce type est utilisé pour permettre la gestion des types
|
|
||||||
pour certaines fonctions. Par exemple, la fonction `__getitem__`
|
|
||||||
utilise cette variable pour retourner un object du type demandé.
|
|
||||||
Cela permet d'avoir des vérifications de types et de l'autocomplétion
|
|
||||||
en utilisant notre IDE.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, world: "World", identifier: int) -> None:
|
def __init__(self, world: "World", identifier: int) -> None:
|
||||||
self.__world = world
|
self.__world = world
|
||||||
self.__identifier = identifier
|
self.__identifier = identifier
|
||||||
|
@ -55,13 +46,13 @@ class Entity:
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"Entity({self.__identifier})"
|
return f"Entity({self.__identifier})"
|
||||||
|
|
||||||
def __getitem__(self, component_type: type[__T]) -> __T:
|
def __getitem__[T](self, component_type: type[T]) -> T:
|
||||||
return self.__world.get_component(self, component_type)
|
return self.__world.get_component(self, component_type)
|
||||||
|
|
||||||
def __delitem__(self, component_type: type[object]):
|
def __delitem__(self, component_type: type[object]):
|
||||||
self.__world.remove_component(self, component_type)
|
self.__world.remove_component(self, component_type)
|
||||||
|
|
||||||
def __setitem__(self, component_type: type[__T], component: __T):
|
def __setitem__[T](self, component_type: type[T], component: T):
|
||||||
if component_type != type(component):
|
if component_type != type(component):
|
||||||
component = component_type(component)
|
component = component_type(component)
|
||||||
self.__world.set_component(self, component)
|
self.__world.set_component(self, component)
|
||||||
|
@ -75,7 +66,7 @@ class Entity:
|
||||||
def __iter__(self) -> Iterator[object]:
|
def __iter__(self) -> Iterator[object]:
|
||||||
return iter(self.__world.all_components(self))
|
return iter(self.__world.all_components(self))
|
||||||
|
|
||||||
def get(self, component_type: type[__T], default: Optional[__T] = None) -> __T:
|
def get[T](self, component_type: type[T], default: Optional[T] = None) -> T:
|
||||||
"""
|
"""
|
||||||
Renvoie le composant de type `component_type` de l'entité.
|
Renvoie le composant de type `component_type` de l'entité.
|
||||||
Si aucun composant de type `component_type` n'est dans l'entité:
|
Si aucun composant de type `component_type` n'est dans l'entité:
|
||||||
|
@ -126,15 +117,6 @@ class World(Entity):
|
||||||
globaux, relatif au monde.
|
globaux, relatif au monde.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__T = TypeVar("__T")
|
|
||||||
"""
|
|
||||||
Ce type est utilisé pour permettre la gestion des types
|
|
||||||
pour certaines fonctions. Par exemple, la fonction `get_component`
|
|
||||||
utilise cette variable pour retourner un object du type demandé.
|
|
||||||
Cela permet d'avoir des vérifications de types et de l'autocomplétion
|
|
||||||
en utilisant notre IDE.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(self, 0)
|
super().__init__(self, 0)
|
||||||
self.__components: dict[int, dict[type[object], object]] = {}
|
self.__components: dict[int, dict[type[object], object]] = {}
|
||||||
|
@ -167,9 +149,9 @@ class World(Entity):
|
||||||
self.__components.setdefault(entity.identifier, {})[type(component)] = component
|
self.__components.setdefault(entity.identifier, {})[type(component)] = component
|
||||||
self.__entities.setdefault(type(component), set()).add(entity.identifier)
|
self.__entities.setdefault(type(component), set()).add(entity.identifier)
|
||||||
|
|
||||||
def get_component(
|
def get_component[T](
|
||||||
self, entity: "Entity", component_type: type[__T], default: Optional[__T] = None
|
self, entity: "Entity", component_type: type[T], default: Optional[T] = None
|
||||||
) -> __T:
|
) -> T:
|
||||||
"""
|
"""
|
||||||
Renvoie le composant de type `component_type` de l'entité `entity`.
|
Renvoie le composant de type `component_type` de l'entité `entity`.
|
||||||
Si aucun composant de type `component_type` n'est dans l'entité:
|
Si aucun composant de type `component_type` n'est dans l'entité:
|
||||||
|
|
26
src/main.py
26
src/main.py
|
@ -2,9 +2,29 @@
|
||||||
Module d'exemple de l'utilisation du moteur de jeu.
|
Module d'exemple de l'utilisation du moteur de jeu.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from engine import start_game
|
from engine import Scene, start_game
|
||||||
|
from engine.ecs import World
|
||||||
from plugins import defaults
|
from plugins import defaults
|
||||||
from scenes import menu
|
from plugins.render import Origin, Position, Scale, Texture
|
||||||
|
|
||||||
|
|
||||||
start_game(defaults.PLUGIN, menu.SCENE)
|
def __initialize(world: World):
|
||||||
|
"""
|
||||||
|
Initialise les ressources pour le moteur de jeu.
|
||||||
|
"""
|
||||||
|
world.new_entity().set(
|
||||||
|
Texture("background.png", 0),
|
||||||
|
# Scale(1000, 1000),
|
||||||
|
# Origin(0.5, 0.5),
|
||||||
|
# Position(600, 600),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
MENU = Scene(
|
||||||
|
[__initialize],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
start_game(defaults.PLUGIN, MENU)
|
||||||
|
|
10
src/plugins save/__init__.py
Normal file
10
src/plugins save/__init__.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
"""
|
||||||
|
Module contenant tous les plugins du jeu.
|
||||||
|
|
||||||
|
Un plugin est une scène pouvant être ajouté a d'autres scènes
|
||||||
|
afin d'ajouter des fonctionnalités au jeu.
|
||||||
|
|
||||||
|
Le but est de faire en sorte que les plugins soient génériques
|
||||||
|
afin de pouvoir les utilisers dans plusieurs scènes et donc
|
||||||
|
éviter de répéter plusieurs fois le même code.
|
||||||
|
"""
|
48
src/plugins save/click.py
Normal file
48
src/plugins save/click.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
"""
|
||||||
|
Un plugin permettant de savoir si l'on a cliqué sur une entité.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Callable
|
||||||
|
from engine import GlobalPlugin
|
||||||
|
from engine.ecs import Entity, World
|
||||||
|
from plugins.hover import Hovered
|
||||||
|
from plugins.inputs import Pressed
|
||||||
|
from plugins.render import Sprite
|
||||||
|
|
||||||
|
|
||||||
|
class Clicked:
|
||||||
|
"""
|
||||||
|
Component ajouté a toutes les entitées qui viennent d'être cliqué.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Clickable:
|
||||||
|
"""
|
||||||
|
Composant qui permet d'executer une fonction lorsqu'une entité est cliquee.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, callback: Callable[[World, Entity], object]):
|
||||||
|
self.callback = callback
|
||||||
|
|
||||||
|
|
||||||
|
def __update_clicked(world: World):
|
||||||
|
"""
|
||||||
|
Met à jour les composants `Clicked`.
|
||||||
|
"""
|
||||||
|
mouse_click = "button_1" in world[Pressed]
|
||||||
|
sprite_entities = world.query(Sprite)
|
||||||
|
for entity in sprite_entities:
|
||||||
|
if Hovered in entity and mouse_click:
|
||||||
|
entity[Clicked] = Clicked()
|
||||||
|
if Clickable in entity:
|
||||||
|
entity[Clickable].callback(world, entity)
|
||||||
|
else:
|
||||||
|
del entity[Clicked]
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN = GlobalPlugin(
|
||||||
|
[],
|
||||||
|
[__update_clicked],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
)
|
34
src/plugins save/defaults.py
Normal file
34
src/plugins save/defaults.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
"""
|
||||||
|
Plugin qui rassemple tous les plugins globaux.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from plugins import (
|
||||||
|
animation,
|
||||||
|
assets,
|
||||||
|
click,
|
||||||
|
coroutine,
|
||||||
|
display,
|
||||||
|
hover,
|
||||||
|
inputs,
|
||||||
|
multisound,
|
||||||
|
render,
|
||||||
|
sound,
|
||||||
|
text,
|
||||||
|
timing,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN = (
|
||||||
|
display.PLUGIN
|
||||||
|
+ timing.PLUGIN
|
||||||
|
+ assets.PLUGIN
|
||||||
|
+ inputs.PLUGIN
|
||||||
|
+ hover.PLUGIN
|
||||||
|
+ click.PLUGIN
|
||||||
|
+ coroutine.PLUGIN
|
||||||
|
+ multisound.PLUGIN
|
||||||
|
+ sound.PLUGIN
|
||||||
|
+ text.PLUGIN
|
||||||
|
+ animation.PLUGIN
|
||||||
|
+ render.PLUGIN
|
||||||
|
)
|
32
src/plugins save/display.py
Normal file
32
src/plugins save/display.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
"""
|
||||||
|
Un plugin pour la gestion de la fenetre du jeu.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
from engine.ecs import World
|
||||||
|
from engine import GlobalPlugin
|
||||||
|
|
||||||
|
|
||||||
|
def __initialize(_world: World):
|
||||||
|
"""
|
||||||
|
Initialise pygame et les ressources pour la gestion de la fenetre.
|
||||||
|
"""
|
||||||
|
pygame.init()
|
||||||
|
pygame.display.set_caption("Guess The Number")
|
||||||
|
pygame.display.set_mode((800, 600), pygame.RESIZABLE)
|
||||||
|
|
||||||
|
|
||||||
|
def __terminate(_world: World):
|
||||||
|
"""
|
||||||
|
Arrête pygame.
|
||||||
|
"""
|
||||||
|
pygame.quit()
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN = GlobalPlugin(
|
||||||
|
[__initialize],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
[__terminate],
|
||||||
|
)
|
92
src/plugins save/hover.py
Normal file
92
src/plugins save/hover.py
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
"""
|
||||||
|
Un plugin permettant de savoir si la souris est par dessus une entité.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
from engine import GlobalPlugin
|
||||||
|
from engine.ecs import World
|
||||||
|
from engine.math import Vec2
|
||||||
|
from plugins.inputs import MousePosition
|
||||||
|
from plugins.render import Sprite
|
||||||
|
|
||||||
|
|
||||||
|
class HoverEnter:
|
||||||
|
"""
|
||||||
|
Composant indicant que l'entité vient de commencer a être survolée par la souris.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Hovered:
|
||||||
|
"""
|
||||||
|
Composant indicant que l'entité est survolée par la souris.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class HoverExit:
|
||||||
|
"""
|
||||||
|
Composant indicant que la souris viens d'arreter de survoler l'entité.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class HoveredTexture:
|
||||||
|
"""
|
||||||
|
Composant permettant de changer la texture d'une entité lorsque
|
||||||
|
celle-ci est survolée par la souris.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, normal: pygame.Surface, hovered: pygame.Surface):
|
||||||
|
self.normal = normal
|
||||||
|
self.hovered = hovered
|
||||||
|
|
||||||
|
|
||||||
|
def __update_hovered(world: World):
|
||||||
|
"""
|
||||||
|
Vérifie le survol de la souris sur les entitées.
|
||||||
|
"""
|
||||||
|
# On met à jour les composants
|
||||||
|
mouse_position = world[MousePosition]
|
||||||
|
for entity in world.query(Sprite):
|
||||||
|
# Récupération de la position et taille de l'entité
|
||||||
|
sprite = entity[Sprite]
|
||||||
|
size = Vec2(*sprite.texture.get_size()) * Vec2(*sprite.area[2:])
|
||||||
|
position = sprite.position - (sprite.origin * size)
|
||||||
|
|
||||||
|
# On détermine si la souris est sur l'entité
|
||||||
|
if (
|
||||||
|
mouse_position.x >= position.x
|
||||||
|
and mouse_position.x <= position.x + size.x
|
||||||
|
and mouse_position.y >= position.y
|
||||||
|
and mouse_position.y <= position.y + size.y
|
||||||
|
):
|
||||||
|
if Hovered not in entity:
|
||||||
|
entity[HoverEnter] = HoverEnter()
|
||||||
|
else:
|
||||||
|
del entity[HoverEnter]
|
||||||
|
entity[Hovered] = Hovered()
|
||||||
|
else:
|
||||||
|
if Hovered in entity:
|
||||||
|
entity[HoverExit] = HoverExit()
|
||||||
|
else:
|
||||||
|
del entity[HoverExit]
|
||||||
|
del entity[Hovered]
|
||||||
|
|
||||||
|
# On affiche la bonne texture
|
||||||
|
for entity in world.query(HoveredTexture):
|
||||||
|
if Hovered in entity:
|
||||||
|
texture = entity[HoveredTexture].hovered
|
||||||
|
else:
|
||||||
|
texture = entity[HoveredTexture].normal
|
||||||
|
if Sprite in entity:
|
||||||
|
entity[Sprite].texture = texture
|
||||||
|
else:
|
||||||
|
entity[Sprite] = Sprite(texture)
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN = GlobalPlugin(
|
||||||
|
[],
|
||||||
|
[__update_hovered],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
)
|
90
src/plugins save/inputs.py
Normal file
90
src/plugins save/inputs.py
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
"""
|
||||||
|
Un plugin permettant de gérer les entrées utilisateur du jeu.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
from engine import CurrentScene, GlobalPlugin, KeepAlive
|
||||||
|
from engine.ecs import World
|
||||||
|
from engine.math import Vec2
|
||||||
|
from plugins import render
|
||||||
|
|
||||||
|
|
||||||
|
class Pressed(KeepAlive, set[str]):
|
||||||
|
"""
|
||||||
|
Ressource qui correspond aux touches qui viennent d'être préssées par l'utilisateur.
|
||||||
|
|
||||||
|
Pour les boutons de la souris la touche sera nommée `button_x` avec `x` le numéro du bouton.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Held(KeepAlive, set[str]):
|
||||||
|
"""
|
||||||
|
Ressource qui correspond aux touches qui sont actuellement préssées par l'utilisateur.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Released(KeepAlive, set[str]):
|
||||||
|
"""
|
||||||
|
Ressource qui correspond aux touches qui viennent d'être relachées par l'utilisateur.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MousePosition(KeepAlive, Vec2):
|
||||||
|
"""
|
||||||
|
Ressource qui correspond à la position de la souris.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def __initialize(world: World):
|
||||||
|
"""
|
||||||
|
Initialise des ressources pour la gestion des entrées utilisateur.
|
||||||
|
"""
|
||||||
|
world.set(Pressed(), Held(), Released(), MousePosition())
|
||||||
|
|
||||||
|
|
||||||
|
def __update_input(world: World):
|
||||||
|
"""
|
||||||
|
Met à jour les ressources qui permettent de savoir quels sont les
|
||||||
|
entrées utilisateur.
|
||||||
|
"""
|
||||||
|
# On récupère les ressources
|
||||||
|
pressed = world[Pressed]
|
||||||
|
held = world[Held]
|
||||||
|
released = world[Released]
|
||||||
|
|
||||||
|
# On clear les touches pressées et relachées
|
||||||
|
pressed.clear()
|
||||||
|
released.clear()
|
||||||
|
|
||||||
|
# On récupère les évenements de pygame
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
del world[CurrentScene]
|
||||||
|
elif event.type == pygame.KEYDOWN:
|
||||||
|
key_name = pygame.key.name(event.key)
|
||||||
|
held.add(key_name)
|
||||||
|
pressed.add(key_name)
|
||||||
|
elif event.type == pygame.KEYUP:
|
||||||
|
key_name = pygame.key.name(event.key)
|
||||||
|
held.remove(key_name)
|
||||||
|
released.add(key_name)
|
||||||
|
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||||
|
held.add(f"button_{event.button}")
|
||||||
|
pressed.add(f"button_{event.button}")
|
||||||
|
elif event.type == pygame.MOUSEBUTTONUP:
|
||||||
|
held.remove(f"button_{event.button}")
|
||||||
|
released.add(f"button_{event.button}")
|
||||||
|
elif event.type == pygame.MOUSEMOTION:
|
||||||
|
x, y, w, h = render.calculate_surface_rect()
|
||||||
|
world[MousePosition] = Vec2(
|
||||||
|
((event.pos[0] - x) / w) * render.WIDTH,
|
||||||
|
((event.pos[1] - y) / h) * render.HEIGHT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN = GlobalPlugin(
|
||||||
|
[__initialize],
|
||||||
|
[__update_input],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
)
|
106
src/plugins save/render.py
Normal file
106
src/plugins save/render.py
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
"""
|
||||||
|
Un plugin qui s'occupe de rendre des choses dans la fenetre.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
import pygame
|
||||||
|
from engine import GlobalPlugin, KeepAlive
|
||||||
|
from engine.ecs import World
|
||||||
|
from engine.math import Vec2
|
||||||
|
|
||||||
|
|
||||||
|
WIDTH = 1440
|
||||||
|
HEIGHT = 1080
|
||||||
|
RATIO = WIDTH / HEIGHT
|
||||||
|
INVERT_RATIO = HEIGHT / WIDTH
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_surface_rect() -> tuple[float, float, float, float]:
|
||||||
|
"""
|
||||||
|
Calcule et renvoie un rectangle qui corresponed a la zone dans laquelle le jeu est rendu.
|
||||||
|
"""
|
||||||
|
width, height = pygame.display.get_surface().get_size()
|
||||||
|
if width / height < RATIO:
|
||||||
|
target_height = width * INVERT_RATIO
|
||||||
|
offset = (height - target_height) / 2
|
||||||
|
return 0.0, offset, float(width), target_height
|
||||||
|
target_width = height * RATIO
|
||||||
|
offset = (width - target_width) / 2
|
||||||
|
return offset, 0.0, target_width, float(height)
|
||||||
|
|
||||||
|
|
||||||
|
class Surface(KeepAlive, pygame.Surface):
|
||||||
|
"""
|
||||||
|
Ressource qui stocke la surface de rendu du jeu.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Sprite:
|
||||||
|
"""
|
||||||
|
Composant donnant la texture d'une entité, sa position et son ordre de rendu.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
texture: pygame.Surface,
|
||||||
|
position: Vec2 = Vec2(0),
|
||||||
|
order: float = -1.0,
|
||||||
|
area: Optional[tuple[float, float, float, float]] = None,
|
||||||
|
origin: Vec2 = Vec2(0),
|
||||||
|
):
|
||||||
|
self.texture = texture
|
||||||
|
self.position = position
|
||||||
|
self.order = order
|
||||||
|
if area is None:
|
||||||
|
self.area = (0.0, 0.0, 1.0, 1.0)
|
||||||
|
else:
|
||||||
|
self.area = area
|
||||||
|
self.origin = origin
|
||||||
|
|
||||||
|
|
||||||
|
def __initialize(world: World):
|
||||||
|
"""
|
||||||
|
Prépare le monde pour la gestion du rendu.
|
||||||
|
"""
|
||||||
|
world.set(Surface((WIDTH, HEIGHT)))
|
||||||
|
|
||||||
|
|
||||||
|
def __render(world: World):
|
||||||
|
"""
|
||||||
|
Rend le monde du jeu sur la surface puis l'affiche sur la fenetre.
|
||||||
|
"""
|
||||||
|
# On rend le monde sur la surface
|
||||||
|
surface = world[Surface]
|
||||||
|
sprites = [entity[Sprite] for entity in world.query(Sprite)]
|
||||||
|
for sprite in sorted(sprites, key=lambda sprite: sprite.order):
|
||||||
|
original_size = Vec2(*sprite.texture.get_size())
|
||||||
|
size = original_size * Vec2(*sprite.area[2:])
|
||||||
|
position = sprite.position - (sprite.origin * size)
|
||||||
|
surface.blit(
|
||||||
|
sprite.texture,
|
||||||
|
(position.x, position.y),
|
||||||
|
(
|
||||||
|
sprite.area[0] * original_size.x,
|
||||||
|
sprite.area[1] * original_size.y,
|
||||||
|
sprite.area[2] * original_size.x,
|
||||||
|
sprite.area[3] * original_size.y,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# On affiche la surface sur la fenetre
|
||||||
|
rect = calculate_surface_rect()
|
||||||
|
pygame.transform.set_smoothscale_backend("MMX")
|
||||||
|
pygame.transform.smoothscale(
|
||||||
|
surface,
|
||||||
|
(rect[2], rect[3]),
|
||||||
|
pygame.display.get_surface().subsurface(rect),
|
||||||
|
)
|
||||||
|
pygame.display.flip()
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN = GlobalPlugin(
|
||||||
|
[__initialize],
|
||||||
|
[],
|
||||||
|
[__render],
|
||||||
|
[],
|
||||||
|
)
|
52
src/plugins save/timing.py
Normal file
52
src/plugins save/timing.py
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
"""
|
||||||
|
Un plugin permettant de connaitre le temps depuis le
|
||||||
|
lancement du jeu et le temps depuis la dernière frame.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from time import time
|
||||||
|
from engine import GlobalPlugin, KeepAlive
|
||||||
|
from engine.ecs import World
|
||||||
|
|
||||||
|
|
||||||
|
class GlobalTime(KeepAlive, float):
|
||||||
|
"""
|
||||||
|
Ressource qui représente le temps global de l'ordinateur sur lequel tourne le jeu.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Time(KeepAlive, float):
|
||||||
|
"""
|
||||||
|
Ressource qui représente le temps depuis le lancement du jeu.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Delta(KeepAlive, float):
|
||||||
|
"""
|
||||||
|
Ressource qui détermine le temps depuis la première frame.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def __initialize(world: World):
|
||||||
|
"""
|
||||||
|
Initialise les ressources pour la gestion du temps.
|
||||||
|
"""
|
||||||
|
world.set(GlobalTime(time()), Time(0.0))
|
||||||
|
|
||||||
|
|
||||||
|
def __update(world: World):
|
||||||
|
"""
|
||||||
|
Met à jour les ressources de temps.
|
||||||
|
"""
|
||||||
|
now = time()
|
||||||
|
world[Delta] = delta = now - world[GlobalTime]
|
||||||
|
world[GlobalTime] = now
|
||||||
|
world[Time] += delta
|
||||||
|
|
||||||
|
|
||||||
|
PLUGIN = GlobalPlugin(
|
||||||
|
[__initialize],
|
||||||
|
[__update],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
)
|
|
@ -2,12 +2,13 @@
|
||||||
Un plugin permettant de savoir si l'on a cliqué sur une entité.
|
Un plugin permettant de savoir si l'on a cliqué sur une entité.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from tkinter import Scale
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from engine import GlobalPlugin
|
from engine import GlobalPlugin
|
||||||
from engine.ecs import Entity, World
|
from engine.ecs import Entity, World
|
||||||
from plugins.hover import Hovered
|
from plugins.hover import Hovered
|
||||||
from plugins.inputs import Pressed
|
from plugins.inputs import Pressed
|
||||||
from plugins.render import Sprite
|
from plugins.render import Position
|
||||||
|
|
||||||
|
|
||||||
class Clicked:
|
class Clicked:
|
||||||
|
@ -30,7 +31,7 @@ def __update_clicked(world: World):
|
||||||
Met à jour les composants `Clicked`.
|
Met à jour les composants `Clicked`.
|
||||||
"""
|
"""
|
||||||
mouse_click = "button_1" in world[Pressed]
|
mouse_click = "button_1" in world[Pressed]
|
||||||
sprite_entities = world.query(Sprite)
|
sprite_entities = world.query(Position, Scale)
|
||||||
for entity in sprite_entities:
|
for entity in sprite_entities:
|
||||||
if Hovered in entity and mouse_click:
|
if Hovered in entity and mouse_click:
|
||||||
entity[Clicked] = Clicked()
|
entity[Clicked] = Clicked()
|
||||||
|
|
|
@ -2,33 +2,14 @@
|
||||||
Plugin qui rassemple tous les plugins globaux.
|
Plugin qui rassemple tous les plugins globaux.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from plugins import (
|
from plugins import display, inputs, sound, render, timing, hover
|
||||||
animation,
|
|
||||||
assets,
|
|
||||||
click,
|
|
||||||
coroutine,
|
|
||||||
display,
|
|
||||||
hover,
|
|
||||||
inputs,
|
|
||||||
multisound,
|
|
||||||
render,
|
|
||||||
sound,
|
|
||||||
text,
|
|
||||||
timing,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
PLUGIN = (
|
PLUGIN = (
|
||||||
display.PLUGIN
|
display.PLUGIN
|
||||||
+ timing.PLUGIN
|
+ timing.PLUGIN
|
||||||
+ assets.PLUGIN
|
|
||||||
+ inputs.PLUGIN
|
+ inputs.PLUGIN
|
||||||
+ hover.PLUGIN
|
+ hover.PLUGIN
|
||||||
+ click.PLUGIN
|
|
||||||
+ coroutine.PLUGIN
|
|
||||||
+ multisound.PLUGIN
|
|
||||||
+ sound.PLUGIN
|
+ sound.PLUGIN
|
||||||
+ text.PLUGIN
|
|
||||||
+ animation.PLUGIN
|
|
||||||
+ render.PLUGIN
|
+ render.PLUGIN
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,13 +3,10 @@ Un plugin permettant de savoir si la souris est par dessus une entité.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import pygame
|
|
||||||
|
|
||||||
from engine import GlobalPlugin
|
from engine import GlobalPlugin
|
||||||
from engine.ecs import World
|
from engine.ecs import World
|
||||||
from engine.math import Vec2
|
|
||||||
from plugins.inputs import MousePosition
|
from plugins.inputs import MousePosition
|
||||||
from plugins.render import Sprite
|
from plugins.render import Origin, Position, Scale, Texture
|
||||||
|
|
||||||
|
|
||||||
class HoverEnter:
|
class HoverEnter:
|
||||||
|
@ -36,7 +33,7 @@ class HoveredTexture:
|
||||||
celle-ci est survolée par la souris.
|
celle-ci est survolée par la souris.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, normal: pygame.Surface, hovered: pygame.Surface):
|
def __init__(self, normal: str, hovered: str):
|
||||||
self.normal = normal
|
self.normal = normal
|
||||||
self.hovered = hovered
|
self.hovered = hovered
|
||||||
|
|
||||||
|
@ -47,11 +44,10 @@ def __update_hovered(world: World):
|
||||||
"""
|
"""
|
||||||
# On met à jour les composants
|
# On met à jour les composants
|
||||||
mouse_position = world[MousePosition]
|
mouse_position = world[MousePosition]
|
||||||
for entity in world.query(Sprite):
|
for entity in world.query(Position, Scale):
|
||||||
# Récupération de la position et taille de l'entité
|
# Récupération de la position et taille de l'entité
|
||||||
sprite = entity[Sprite]
|
size = entity[Scale]
|
||||||
size = Vec2(*sprite.texture.get_size()) * Vec2(*sprite.area[2:])
|
position = entity[Position] - (entity.get(Origin, Origin(0)) * size)
|
||||||
position = sprite.position - (sprite.origin * size)
|
|
||||||
|
|
||||||
# On détermine si la souris est sur l'entité
|
# On détermine si la souris est sur l'entité
|
||||||
if (
|
if (
|
||||||
|
@ -75,13 +71,9 @@ def __update_hovered(world: World):
|
||||||
# On affiche la bonne texture
|
# On affiche la bonne texture
|
||||||
for entity in world.query(HoveredTexture):
|
for entity in world.query(HoveredTexture):
|
||||||
if Hovered in entity:
|
if Hovered in entity:
|
||||||
texture = entity[HoveredTexture].hovered
|
entity[Texture] = entity[HoveredTexture].hovered
|
||||||
else:
|
else:
|
||||||
texture = entity[HoveredTexture].normal
|
entity[Texture] = entity[HoveredTexture].normal
|
||||||
if Sprite in entity:
|
|
||||||
entity[Sprite].texture = texture
|
|
||||||
else:
|
|
||||||
entity[Sprite] = Sprite(texture)
|
|
||||||
|
|
||||||
|
|
||||||
PLUGIN = GlobalPlugin(
|
PLUGIN = GlobalPlugin(
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
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 GlobalPlugin, KeepAlive
|
from engine import GlobalPlugin, KeepAlive
|
||||||
from engine.ecs import World
|
from engine.ecs import World
|
||||||
|
@ -29,35 +28,42 @@ def calculate_surface_rect() -> tuple[float, float, float, float]:
|
||||||
return offset, 0.0, target_width, float(height)
|
return offset, 0.0, target_width, float(height)
|
||||||
|
|
||||||
|
|
||||||
|
class Texture(str):
|
||||||
|
"""
|
||||||
|
Composant donnant le nom de la texture d'une entité.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Order(float):
|
||||||
|
"""
|
||||||
|
Composant donnant l'ordre d'affichage d'une entité.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Position(Vec2):
|
||||||
|
"""
|
||||||
|
Composant donnant la position de l'origine d'un objet.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Scale(Vec2):
|
||||||
|
"""
|
||||||
|
Composant donnant la taille de la texture d'une entité.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Origin(Vec2):
|
||||||
|
"""
|
||||||
|
Composant définissant l'emplacement de l'origine d'un objet sur sa texture.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Surface(KeepAlive, pygame.Surface):
|
class Surface(KeepAlive, pygame.Surface):
|
||||||
"""
|
"""
|
||||||
Ressource qui stocke la surface de rendu du jeu.
|
Ressource qui stocke la surface de rendu du jeu.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Sprite:
|
|
||||||
"""
|
|
||||||
Composant donnant la texture d'une entité, sa position et son ordre de rendu.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
texture: pygame.Surface,
|
|
||||||
position: Vec2 = Vec2(0),
|
|
||||||
order: float = -1.0,
|
|
||||||
area: Optional[tuple[float, float, float, float]] = None,
|
|
||||||
origin: Vec2 = Vec2(0),
|
|
||||||
):
|
|
||||||
self.texture = texture
|
|
||||||
self.position = position
|
|
||||||
self.order = order
|
|
||||||
if area is None:
|
|
||||||
self.area = (0.0, 0.0, 1.0, 1.0)
|
|
||||||
else:
|
|
||||||
self.area = area
|
|
||||||
self.origin = origin
|
|
||||||
|
|
||||||
|
|
||||||
def __initialize(world: World):
|
def __initialize(world: World):
|
||||||
"""
|
"""
|
||||||
Prépare le monde pour la gestion du rendu.
|
Prépare le monde pour la gestion du rendu.
|
||||||
|
@ -65,27 +71,25 @@ def __initialize(world: World):
|
||||||
world.set(Surface((WIDTH, HEIGHT)))
|
world.set(Surface((WIDTH, HEIGHT)))
|
||||||
|
|
||||||
|
|
||||||
def __render(world: World):
|
def __render(world: World, cache: dict[str, pygame.Surface] = {}):
|
||||||
"""
|
"""
|
||||||
Rend le monde du jeu sur la surface puis l'affiche sur la fenetre.
|
Rend le monde du jeu sur la surface puis l'affiche sur la fenetre.
|
||||||
"""
|
"""
|
||||||
# On rend le monde sur la surface
|
# On rend le monde sur la surface
|
||||||
surface = world[Surface]
|
surface: Surface = world[Surface]
|
||||||
sprites = [entity[Sprite] for entity in world.query(Sprite)]
|
entities = sorted(world.query(Texture), key=lambda entity: entity.get(Order, -1))
|
||||||
for sprite in sorted(sprites, key=lambda sprite: sprite.order):
|
for entity in entities:
|
||||||
original_size = Vec2(*sprite.texture.get_size())
|
texture_name = entity[Texture]
|
||||||
size = original_size * Vec2(*sprite.area[2:])
|
texture = cache.get(texture_name)
|
||||||
position = sprite.position - (sprite.origin * size)
|
if texture is None:
|
||||||
surface.blit(
|
texture = pygame.image.load(f"assets/textures/{texture_name}")
|
||||||
sprite.texture,
|
cache[texture_name] = texture
|
||||||
(position.x, position.y),
|
scale = entity.get(Scale, Scale(128))
|
||||||
(
|
texture = pygame.transform.scale(texture, (scale.x, scale.y))
|
||||||
sprite.area[0] * original_size.x,
|
position = (
|
||||||
sprite.area[1] * original_size.y,
|
entity.get(Position, Position(0)) - (entity.get(Origin, Origin(0))) * scale
|
||||||
sprite.area[2] * original_size.x,
|
|
||||||
sprite.area[3] * original_size.y,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
surface.blit(texture, (position.x, position.y))
|
||||||
|
|
||||||
# On affiche la surface sur la fenetre
|
# On affiche la surface sur la fenetre
|
||||||
rect = calculate_surface_rect()
|
rect = calculate_surface_rect()
|
||||||
|
|
|
@ -22,7 +22,7 @@ class Sound:
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
sound: pygame.mixer.Sound,
|
sound: str,
|
||||||
loop: bool = False,
|
loop: bool = False,
|
||||||
volume: float = 1.0,
|
volume: float = 1.0,
|
||||||
fade_ms: int = 0,
|
fade_ms: int = 0,
|
||||||
|
@ -42,7 +42,11 @@ def __initialize(world: World):
|
||||||
world.set(Channels())
|
world.set(Channels())
|
||||||
|
|
||||||
|
|
||||||
def __update_sounds(world: World):
|
def __update_sounds(
|
||||||
|
world: World,
|
||||||
|
channels: dict[Entity, pygame.mixer.Channel] = {},
|
||||||
|
cache: dict[str, pygame.mixer.Sound] = {},
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Met à jour les sons du jeu.
|
Met à jour les sons du jeu.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,9 +5,8 @@ lancement du jeu et le temps depuis la dernière frame.
|
||||||
|
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
from typing import Callable
|
|
||||||
from engine import GlobalPlugin, KeepAlive
|
from engine import GlobalPlugin, KeepAlive
|
||||||
from engine.ecs import Entity, World
|
from engine.ecs import World
|
||||||
|
|
||||||
|
|
||||||
class GlobalTime(KeepAlive, float):
|
class GlobalTime(KeepAlive, float):
|
||||||
|
@ -28,16 +27,6 @@ class Delta(KeepAlive, float):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class TimedEvent:
|
|
||||||
"""
|
|
||||||
Composant permettant d'executer un callback après un certain temps.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, timer: float, callback: Callable[[World, Entity], object]):
|
|
||||||
self.timer = timer
|
|
||||||
self.callback = callback
|
|
||||||
|
|
||||||
|
|
||||||
def __initialize(world: World):
|
def __initialize(world: World):
|
||||||
"""
|
"""
|
||||||
Initialise les ressources pour la gestion du temps.
|
Initialise les ressources pour la gestion du temps.
|
||||||
|
@ -47,22 +36,13 @@ def __initialize(world: World):
|
||||||
|
|
||||||
def __update(world: World):
|
def __update(world: World):
|
||||||
"""
|
"""
|
||||||
Met à jour les ressources de temps et execute les `TimedEvent`.
|
Met à jour les ressources de temps.
|
||||||
"""
|
"""
|
||||||
# On met à jour le temps
|
|
||||||
now = time()
|
now = time()
|
||||||
world[Delta] = delta = now - world[GlobalTime]
|
world[Delta] = delta = now - world[GlobalTime]
|
||||||
world[GlobalTime] = now
|
world[GlobalTime] = now
|
||||||
world[Time] += delta
|
world[Time] += delta
|
||||||
|
|
||||||
# On met à jour les `TimedEvent`
|
|
||||||
for entity in world.query(TimedEvent):
|
|
||||||
event = entity[TimedEvent]
|
|
||||||
event.timer -= delta
|
|
||||||
if event.timer <= 0:
|
|
||||||
del entity[TimedEvent]
|
|
||||||
event.callback(world, entity)
|
|
||||||
|
|
||||||
|
|
||||||
PLUGIN = GlobalPlugin(
|
PLUGIN = GlobalPlugin(
|
||||||
[__initialize],
|
[__initialize],
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
"""
|
|
||||||
Definit un plugin qui crée un texte avec les touches frappées
|
|
||||||
"""
|
|
||||||
|
|
||||||
from engine import Scene, World
|
|
||||||
from plugins.inputs import Pressed
|
|
||||||
from plugins.text import Text
|
|
||||||
from scenes import CLICK_SOUND
|
|
||||||
|
|
||||||
|
|
||||||
class Writing:
|
|
||||||
"""
|
|
||||||
Marque une entité comme un texte qui s'ecrit en fonction du clavier
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, accepted_chars: str, max_chars: int = 10, base_text: str = ""
|
|
||||||
) -> None:
|
|
||||||
self.accepted_chars = accepted_chars
|
|
||||||
self.max_chars = max_chars
|
|
||||||
self.base_text = base_text
|
|
||||||
|
|
||||||
|
|
||||||
def __update(world: World):
|
|
||||||
"""
|
|
||||||
Met a jour les entitées contenant le composant Typing
|
|
||||||
"""
|
|
||||||
pressed = world[Pressed]
|
|
||||||
for entity in world.query(Writing, Text):
|
|
||||||
writing = entity[Writing]
|
|
||||||
text = entity[Text]
|
|
||||||
for key in pressed:
|
|
||||||
if key == "backspace" and text.text != writing.base_text:
|
|
||||||
text.text = text.text[:-1]
|
|
||||||
world.new_entity().set(CLICK_SOUND)
|
|
||||||
if text.text == "":
|
|
||||||
text.text = writing.base_text
|
|
||||||
if key.startswith("["): # pavé numerique
|
|
||||||
key = key[1]
|
|
||||||
if key in writing.accepted_chars and (
|
|
||||||
text.text == writing.base_text or len(text.text) < writing.max_chars
|
|
||||||
):
|
|
||||||
if text.text == writing.base_text:
|
|
||||||
text.text = key
|
|
||||||
else:
|
|
||||||
text.text += key
|
|
||||||
world.new_entity().set(CLICK_SOUND)
|
|
||||||
if text.text == "":
|
|
||||||
text.text = writing.base_text
|
|
||||||
|
|
||||||
|
|
||||||
PLUGIN = Scene(
|
|
||||||
[],
|
|
||||||
[__update],
|
|
||||||
[],
|
|
||||||
)
|
|
|
@ -1,8 +1,3 @@
|
||||||
"""
|
"""
|
||||||
Module contenant toutes les scènes du jeu.
|
Module contenant toutes les scènes du jeu.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from plugins.multisound import MultiSound
|
|
||||||
|
|
||||||
|
|
||||||
CLICK_SOUND = MultiSound("click/0", "click/1", "click/2")
|
|
||||||
|
|
Loading…
Reference in a new issue