Ajout des entités

This commit is contained in:
Yannis 2023-12-23 13:06:45 +01:00
parent 85ac2c01f7
commit 91cf2db3bd
10 changed files with 178 additions and 24 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

66
src/animation.py Normal file
View file

@ -0,0 +1,66 @@
# Classe animation adaptée depuis ESG Engine :
# https://github.com/yannis300307/ESG_Engine/blob/main/ESG_Engine/client/animation.py
import os
import pygame
class Anim:
"""Une animation contenant les images pygame et le temps entre chaque frames."""
def __init__(self, change_frame_time: float):
self.frames = []
self.change_frame_time = change_frame_time # Delay entre chaque frames
self.current_frame = 0
self.time = 0
def add_frame(self, frame: pygame.Surface):
"""Enregistre une nouvelle image dans la liste des frames."""
self.frames.append(frame.convert_alpha())
def get_frame_nbr(self):
"""Renvoie le nombre d'images enregistrées."""
return len(self.frames)
def get_frame(self, delta: float):
"""Donne l'image courante de l'animation."""
# Avant de retourner l'image, on met à jour le delay
self.update_current_frame(delta)
# Si le delay entre deux images est écoulé, on incrémente le numéro de l'image et on remet le temps à 0
if self.time >= self.change_frame_time:
self.current_frame += 1
self.time = 0
# Si on sort de la liste d'images, on revient au début
if self.current_frame >= len(self.frames):
self.current_frame = 0
return self.frames[self.current_frame]
def update_current_frame(self, delta: float):
"""Met à jour le delay de l'image courante avec le delta time."""
self.time += delta
def get_specific_frame(self, base: int):
"""Donne la {base} ème image apres l'image courante."""
# Si le delay entre deux images est écoulé, on incrémente le numéro de l'image et on remet le temps à 0
if self.time >= self.change_frame_time:
self.current_frame += 1
self.time = 0
# Si on sort de la liste d'images, on revient au début
if self.current_frame >= len(self.frames):
self.current_frame = 0
return self.frames[(self.current_frame + base) % len(self.frames)]
def load_animation_from_directory(self, path: str):
"""Récupère toutes les images au format png dans un dossier et les enregistre comme frames de l'animation."""
# On récupère tous les fichiers dans le dossier donné
files = os.listdir(path)
# Si ils sont bien en PNG, on les enregistre
for file in files:
if file.endswith(".png"):
self.add_frame(pygame.image.load(path + os.sep + file))

View file

@ -1,3 +1,6 @@
from src.entity import Entity
class Camera: class Camera:
def __init__(self): def __init__(self):
self.x = 0 self.x = 0
@ -11,8 +14,19 @@ class Camera:
self.smoothness = 20. self.smoothness = 20.
self.followed_entity: Entity | None = None
def update(self): def update(self):
"""Met à jour la caméra. Permet, par exemple, de faire le scrolling.""" """Met à jour la caméra. Permet, par exemple, de faire le scrolling."""
# Si on suit une entité, on met à jour les coordonnées de suivi
if self.followed_entity is not None:
self.target_x = self.followed_entity.x
self.target_y = self.followed_entity.y
self.x += (self.target_x - self.x) / self.smoothness self.x += (self.target_x - self.x) / self.smoothness
self.y += (self.target_y - self.y) / self.smoothness self.y += (self.target_y - self.y) / self.smoothness
self.zoom += (self.target_zoom - self.zoom) / self.smoothness self.zoom += (self.target_zoom - self.zoom) / self.smoothness
def follow_entity(self, entity: Entity):
self.followed_entity = entity

View file

@ -1,4 +1,6 @@
from src.animation import Anim
from src.camera import Camera from src.camera import Camera
from src.entity_manager import EntityManager
from src.event_handler import EventHandler from src.event_handler import EventHandler
from src.map_manager import MapManager from src.map_manager import MapManager
from src.renderer import Renderer from src.renderer import Renderer
@ -18,11 +20,19 @@ class Engine:
self.event_handler = EventHandler(self) self.event_handler = EventHandler(self)
self.map_manager = MapManager() self.map_manager = MapManager()
self.camera = Camera() self.camera = Camera()
self.entity_manager = EntityManager()
self.map_manager.load_new("maps/map1.tmj") self.map_manager.load_new("maps/map2.tmj")
self.renderer.load_tile_set("assets/tiles.png", 16) self.renderer.load_tile_set("assets/tiles.png", 16)
anim = Anim(0.5) # TODO : REMOVE (ONLY USED FOR TESTING)
anim.load_animation_from_directory("assets/entities/test/none")
self.renderer.register_animation(anim, "test_none")
test_entity = self.entity_manager.register_entity("test")
test_entity.link_animation("test_none")
def loop(self): def loop(self):
"""Fonction à lancer au début du programme et qui va lancer les updates dans une boucle. """Fonction à lancer au début du programme et qui va lancer les updates dans une boucle.
Attend jusqu'à la fin du jeu.""" Attend jusqu'à la fin du jeu."""
@ -34,9 +44,10 @@ class Engine:
def update(self): def update(self):
"""Fonction qui regroupe toutes les updates des composants. Elle permet de mettre à jour le jeu quand on """Fonction qui regroupe toutes les updates des composants. Elle permet de mettre à jour le jeu quand on
l'appelle.""" l'appelle."""
self.camera.update()
self.entity_manager.update(0.016666666)
self.renderer.update() self.renderer.update()
self.event_handler.update() self.event_handler.update()
self.camera.update()
def stop(self): def stop(self):
"""Arrête le programme.""" """Arrête le programme."""

View file

@ -1,4 +1,21 @@
class Entity: class Entity:
def __init__(self): """Classe permettant de gérer les entités. Créée automatiquement par `EntityManager.register_entity()`"""
def __init__(self, name: str):
self.x = 2 self.x = 2
self.y = 2 self.y = 2
# Time utilisé pour les IA
self.time = 0
self.name = name
self.animation_name = None
def update(self, delta: float):
"""Met à jour l'entité."""
self.x += 1
self.time += delta
def link_animation(self, name: str):
self.animation_name = name

22
src/entity_manager.py Normal file
View file

@ -0,0 +1,22 @@
from src.entity import Entity
class EntityManager:
"""Classe chargée de gérer les entités."""
def __init__(self):
self.entities: dict[str:Entity] = {}
def register_entity(self, name: str):
"""Crée une entité et l'enregistre dans un dictionnaire."""
entity = Entity(name)
self.entities[name] = entity
return entity
def update(self, delta: float):
"""Met à jour toutes les entités enregistrées."""
for entity in self.entities.values():
entity.update(delta)
def get_all_entities(self):
"""Donne la liste de toutes les entités enregistrées."""
return list(self.entities.values())

View file

@ -1,7 +1,10 @@
import math
from pygame import display, image, surface, transform from pygame import display, image, surface, transform
from pygame.locals import RESIZABLE from pygame.locals import RESIZABLE
import src.engine as engine import src.engine as engine
from src.animation import Anim
class Renderer: class Renderer:
@ -11,6 +14,7 @@ class Renderer:
self.window = display.set_mode((600, 600), RESIZABLE) self.window = display.set_mode((600, 600), RESIZABLE)
self.tiles = [] self.tiles = []
self.tile_size = 0 self.tile_size = 0
self.animations: dict[str: Anim] = {}
def load_tile_set(self, file_path: str, tile_size: int): def load_tile_set(self, file_path: str, tile_size: int):
"""Charge le jeu de tuiles en utilisant le fichier donné et la taille donnée.""" """Charge le jeu de tuiles en utilisant le fichier donné et la taille donnée."""
@ -28,12 +32,40 @@ class Renderer:
"""Fait le rendu du jeu.""" """Fait le rendu du jeu."""
self.window.fill((255, 255, 255)) self.window.fill((255, 255, 255))
self.render_map() # On crée une surface temporaire qui nous permettra de faire le rendu à l'échelle 1:1
rendered_surface_size = (display.get_window_size()[0] / self.engine.camera.zoom, display.get_window_size()[1] / self.engine.camera.zoom)
rendered_surface = surface.Surface(rendered_surface_size)
self.renderer_layer(0, rendered_surface)
self.render_entities(rendered_surface)
self.renderer_layer(1, rendered_surface)
self.renderer_layer(2, rendered_surface)
# Enfin, on redimensionne notre surface et on la colle sur la fenêtre principale
self.window.blit(transform.scale(rendered_surface, (math.ceil(rendered_surface_size[0] * self.engine.camera.zoom),
math.ceil(rendered_surface_size[1] * self.engine.camera.zoom))),
(0, 0))
# Apres avoir tout rendu, on met à jour l'écran # Apres avoir tout rendu, on met à jour l'écran
display.update() display.update()
def render_map(self): def register_animation(self, animation: Anim, name: str):
"""Enregistre une animation."""
self.animations[name] = animation
def render_entities(self, rendered_surface: surface.Surface):
"""Rend toutes les entités."""
# On calcule le décalage pour centrer la caméra
x_middle_offset = display.get_window_size()[0] / 2 / self.engine.camera.zoom
y_middle_offset = display.get_window_size()[1] / 2 / self.engine.camera.zoom
for entity in self.engine.entity_manager.get_all_entities():
anim: Anim = self.animations[entity.animation_name]
frame = anim.get_frame(0.01666667)
rendered_surface.blit(frame, (entity.x-self.engine.camera.x+x_middle_offset, entity.y-self.engine.camera.y+y_middle_offset))
def renderer_layer(self, layer_id: int, rendered_surface: surface.Surface):
"""Rend la map."""
# On calcule le nombre de tiles à mettre sur notre écran en prenant en compte le zoom # On calcule le nombre de tiles à mettre sur notre écran en prenant en compte le zoom
x_map_range = int(display.get_window_size()[0] / 16 / self.engine.camera.zoom) + 2 x_map_range = int(display.get_window_size()[0] / 16 / self.engine.camera.zoom) + 2
y_map_range = int(display.get_window_size()[1] / 16 / self.engine.camera.zoom) + 2 y_map_range = int(display.get_window_size()[1] / 16 / self.engine.camera.zoom) + 2
@ -46,27 +78,19 @@ class Renderer:
x_map_offset = int((self.engine.camera.x - x_middle_offset) / self.tile_size) x_map_offset = int((self.engine.camera.x - x_middle_offset) / self.tile_size)
y_map_offset = int((self.engine.camera.y - y_middle_offset) / self.tile_size) y_map_offset = int((self.engine.camera.y - y_middle_offset) / self.tile_size)
# On crée une surface temporaire qui nous permettra de la redimensionner
rendered_surface_size = (x_map_range * self.tile_size, y_map_range * self.tile_size)
rendered_surface = surface.Surface(rendered_surface_size)
# On itère pour chaque couche, toutes les tiles visibles par la caméra # On itère pour chaque couche, toutes les tiles visibles par la caméra
for i in range(len(self.engine.map_manager.map_layers)): for x in range(x_map_offset, x_map_offset + x_map_range):
for x in range(x_map_offset, x_map_offset + x_map_range): for y in range(y_map_offset, y_map_offset + y_map_range):
for y in range(y_map_offset, y_map_offset + y_map_range):
# On récupère l'id de la tile à la position donnée # On récupère l'id de la tile à la position donnée
tile_id = self.engine.map_manager.get_tile_at(x, y, i) tile_id = self.engine.map_manager.get_tile_at(x, y, layer_id)
# Si l'id est 0, il s'agit de vide donc on saute le rendu # Si l'id est 0, il s'agit de vide donc on saute le rendu
if tile_id == 0: if tile_id == 0:
continue continue
# Puis, on cherche à quelle image elle correspond et on la colle sur notre surface # Puis, on cherche à quelle image elle correspond et on la colle sur notre surface
rendered_surface.blit(self.tiles[tile_id-1], rendered_surface.blit(self.tiles[tile_id-1],
((x*self.tile_size-self.engine.camera.x+x_middle_offset), ((x*self.tile_size-self.engine.camera.x+x_middle_offset),
(y*self.tile_size-self.engine.camera.y+y_middle_offset))) (y*self.tile_size-self.engine.camera.y+y_middle_offset)))
# Enfin, on redimensionne notre surface et on la colle sur la fenêtre principale
self.window.blit(transform.scale(rendered_surface, (rendered_surface_size[0]*self.engine.camera.zoom,
rendered_surface_size[1]*self.engine.camera.zoom)), (0, 0))