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:
|
||||
|
@ -17,15 +17,6 @@ class Entity:
|
|||
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:
|
||||
self.__world = world
|
||||
self.__identifier = identifier
|
||||
|
@ -55,13 +46,13 @@ class Entity:
|
|||
def __repr__(self) -> str:
|
||||
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)
|
||||
|
||||
def __delitem__(self, component_type: type[object]):
|
||||
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):
|
||||
component = component_type(component)
|
||||
self.__world.set_component(self, component)
|
||||
|
@ -75,7 +66,7 @@ class Entity:
|
|||
def __iter__(self) -> Iterator[object]:
|
||||
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é.
|
||||
Si aucun composant de type `component_type` n'est dans l'entité:
|
||||
|
@ -126,15 +117,6 @@ class World(Entity):
|
|||
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):
|
||||
super().__init__(self, 0)
|
||||
self.__components: dict[int, dict[type[object], object]] = {}
|
||||
|
@ -167,9 +149,9 @@ class World(Entity):
|
|||
self.__components.setdefault(entity.identifier, {})[type(component)] = component
|
||||
self.__entities.setdefault(type(component), set()).add(entity.identifier)
|
||||
|
||||
def get_component(
|
||||
self, entity: "Entity", component_type: type[__T], default: Optional[__T] = None
|
||||
) -> __T:
|
||||
def get_component[T](
|
||||
self, entity: "Entity", component_type: type[T], default: Optional[T] = None
|
||||
) -> T:
|
||||
"""
|
||||
Renvoie le composant de type `component_type` de l'entité `entity`.
|
||||
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.
|
||||
"""
|
||||
|
||||
from engine import start_game
|
||||
from engine import Scene, start_game
|
||||
from engine.ecs import World
|
||||
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é.
|
||||
"""
|
||||
|
||||
from tkinter import Scale
|
||||
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
|
||||
from plugins.render import Position
|
||||
|
||||
|
||||
class Clicked:
|
||||
|
@ -30,7 +31,7 @@ def __update_clicked(world: World):
|
|||
Met à jour les composants `Clicked`.
|
||||
"""
|
||||
mouse_click = "button_1" in world[Pressed]
|
||||
sprite_entities = world.query(Sprite)
|
||||
sprite_entities = world.query(Position, Scale)
|
||||
for entity in sprite_entities:
|
||||
if Hovered in entity and mouse_click:
|
||||
entity[Clicked] = Clicked()
|
||||
|
|
|
@ -2,33 +2,14 @@
|
|||
Plugin qui rassemple tous les plugins globaux.
|
||||
"""
|
||||
|
||||
from plugins import (
|
||||
animation,
|
||||
assets,
|
||||
click,
|
||||
coroutine,
|
||||
display,
|
||||
hover,
|
||||
inputs,
|
||||
multisound,
|
||||
render,
|
||||
sound,
|
||||
text,
|
||||
timing,
|
||||
)
|
||||
from plugins import display, inputs, sound, render, timing, hover
|
||||
|
||||
|
||||
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
|
||||
)
|
||||
|
|
|
@ -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.ecs import World
|
||||
from engine.math import Vec2
|
||||
from plugins.inputs import MousePosition
|
||||
from plugins.render import Sprite
|
||||
from plugins.render import Origin, Position, Scale, Texture
|
||||
|
||||
|
||||
class HoverEnter:
|
||||
|
@ -36,7 +33,7 @@ class HoveredTexture:
|
|||
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.hovered = hovered
|
||||
|
||||
|
@ -47,11 +44,10 @@ def __update_hovered(world: World):
|
|||
"""
|
||||
# On met à jour les composants
|
||||
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é
|
||||
sprite = entity[Sprite]
|
||||
size = Vec2(*sprite.texture.get_size()) * Vec2(*sprite.area[2:])
|
||||
position = sprite.position - (sprite.origin * size)
|
||||
size = entity[Scale]
|
||||
position = entity[Position] - (entity.get(Origin, Origin(0)) * size)
|
||||
|
||||
# On détermine si la souris est sur l'entité
|
||||
if (
|
||||
|
@ -75,13 +71,9 @@ def __update_hovered(world: World):
|
|||
# On affiche la bonne texture
|
||||
for entity in world.query(HoveredTexture):
|
||||
if Hovered in entity:
|
||||
texture = entity[HoveredTexture].hovered
|
||||
entity[Texture] = entity[HoveredTexture].hovered
|
||||
else:
|
||||
texture = entity[HoveredTexture].normal
|
||||
if Sprite in entity:
|
||||
entity[Sprite].texture = texture
|
||||
else:
|
||||
entity[Sprite] = Sprite(texture)
|
||||
entity[Texture] = entity[HoveredTexture].normal
|
||||
|
||||
|
||||
PLUGIN = GlobalPlugin(
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
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
|
||||
|
@ -29,35 +28,42 @@ def calculate_surface_rect() -> tuple[float, float, float, float]:
|
|||
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):
|
||||
"""
|
||||
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.
|
||||
|
@ -65,27 +71,25 @@ def __initialize(world: World):
|
|||
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.
|
||||
"""
|
||||
# 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,
|
||||
),
|
||||
surface: Surface = world[Surface]
|
||||
entities = sorted(world.query(Texture), key=lambda entity: entity.get(Order, -1))
|
||||
for entity in entities:
|
||||
texture_name = entity[Texture]
|
||||
texture = cache.get(texture_name)
|
||||
if texture is None:
|
||||
texture = pygame.image.load(f"assets/textures/{texture_name}")
|
||||
cache[texture_name] = texture
|
||||
scale = entity.get(Scale, Scale(128))
|
||||
texture = pygame.transform.scale(texture, (scale.x, scale.y))
|
||||
position = (
|
||||
entity.get(Position, Position(0)) - (entity.get(Origin, Origin(0))) * scale
|
||||
)
|
||||
surface.blit(texture, (position.x, position.y))
|
||||
|
||||
# On affiche la surface sur la fenetre
|
||||
rect = calculate_surface_rect()
|
||||
|
|
|
@ -22,7 +22,7 @@ class Sound:
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
sound: pygame.mixer.Sound,
|
||||
sound: str,
|
||||
loop: bool = False,
|
||||
volume: float = 1.0,
|
||||
fade_ms: int = 0,
|
||||
|
@ -42,7 +42,11 @@ def __initialize(world: World):
|
|||
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.
|
||||
"""
|
||||
|
|
|
@ -5,9 +5,8 @@ lancement du jeu et le temps depuis la dernière frame.
|
|||
|
||||
|
||||
from time import time
|
||||
from typing import Callable
|
||||
from engine import GlobalPlugin, KeepAlive
|
||||
from engine.ecs import Entity, World
|
||||
from engine.ecs import World
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
Initialise les ressources pour la gestion du temps.
|
||||
|
@ -47,22 +36,13 @@ def __initialize(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()
|
||||
world[Delta] = delta = now - world[GlobalTime]
|
||||
world[GlobalTime] = now
|
||||
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(
|
||||
[__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.
|
||||
"""
|
||||
|
||||
from plugins.multisound import MultiSound
|
||||
|
||||
|
||||
CLICK_SOUND = MultiSound("click/0", "click/1", "click/2")
|
||||
|
|
Loading…
Reference in a new issue