Compare commits

..

8 commits

15 changed files with 304 additions and 568 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -32,23 +32,23 @@ class WolfAI(MobAI):
# On vérifie que le loup peut voir le joueur
if player_distance <= self.ATTACK_DISTANCE:
# On rétablit la vitesse du loup à 60
self.entity.max_speed = 60.
# On rétablit la vitesse du loup à 1
self.entity.max_speed = 1.
# Si le loup touche le joueur, il lui inflige des dégats
if player.get_collisions_with_entity(self.entity):
player.take_damages(1)
# Si le loup n'est pas déja sur le joueur, on le fait s'en raprocher
if player_distance > 1.:
if player_distance > self.entity.max_speed:
self.entity.move(x_distance / player_distance*self.entity.max_speed,
y_distance / player_distance*self.entity.max_speed, self.map_manager, delta)
y_distance / player_distance*self.entity.max_speed, self.map_manager)
else:
# Comportement d'attente
# On diminue la vitesse
self.entity.max_speed = 30.
self.entity.max_speed = 0.5
self.timer -= delta
# Si le timer est fini et que le loup était en train d'attendre, il commence à marcher
@ -66,4 +66,4 @@ class WolfAI(MobAI):
# On fait avancer le loup quand il le doit
if self.comportment == 1:
self.entity.move(self.walk_x, self.walk_y, self.map_manager, delta)
self.entity.move(self.walk_x, self.walk_y, self.map_manager)

View file

@ -1,7 +1,6 @@
# Classe animation adaptée depuis ESG Engine :
# https://github.com/yannis300307/ESG_Engine/blob/main/ESG_Engine/client/animation.py
import os
import pygame

View file

@ -5,7 +5,7 @@ class Camera:
def __init__(self):
self.x = 0
self.y = 0
self.zoom = 1.75
self.zoom = 1.
# Décalage lors du mouvement du joueur
self.player_moving_offset = 100
@ -15,11 +15,11 @@ class Camera:
self.target_y = self.y
self.target_zoom = self.zoom
self.smoothness = 0.5
self.smoothness = 20.
self.followed_entity: Entity | None = None
def update(self, delta: float):
def update(self):
"""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
@ -29,9 +29,9 @@ class Camera:
self.target_y = (self.followed_entity.y + self.followed_entity.mouvements[1] *
self.player_moving_offset / self.zoom)
self.x += (self.target_x - self.x)*delta / self.smoothness
self.y += (self.target_y - self.y)*delta / self.smoothness
self.zoom += (self.target_zoom - self.zoom)*delta / self.smoothness
self.x += (self.target_x - self.x) / self.smoothness
self.y += (self.target_y - self.y) / self.smoothness
self.zoom += (self.target_zoom - self.zoom) / self.smoothness
def follow_entity(self, entity: Entity | None):
"""Active le suivit de l'entité donnée. Mettre `None` pour retirer le suivit."""

View file

@ -1,11 +1,14 @@
import json
from types import FunctionType
import src.engine.engine as engine
from src.engine.event_handler import EventHandler
class DialogsManager:
"""Classe qui gère la lecture des dialogues."""
def __init__(self, engine: 'engine.Engine'):
self.engine = engine
def __init__(self, event_handler: EventHandler):
self.event_handler = event_handler
self.current_dialogs = []
self.current_dialog_id = -1
self.dialogs = {}
@ -28,6 +31,8 @@ class DialogsManager:
else:
self.next_dialog()
print("next")
def next_dialog(self):
"""Passe au dialogue suivant. Appelle le callback si le dialogue est fini."""
self.current_dialog_id += 1
@ -38,8 +43,7 @@ class DialogsManager:
self.current_dialog_id = -1
self.writing_dialog = False
self.reading_dialog = False
self.engine.entity_manager.resume()
self.engine.event_handler.remove_button_area("next_dialog")
self.event_handler.remove_button_area("next_dialog")
if self.dialogue_finished_callback is not None:
self.dialogue_finished_callback()
@ -48,9 +52,7 @@ class DialogsManager:
# Si un dialogue n'est pas déja lancé, on lance le dialogue au nom donné
if not self.reading_dialog:
self.engine.entity_manager.pause()
self.engine.event_handler.register_button_area((0, 0, 1, 1), self.next_signal, "next_dialog", 2)
self.event_handler.register_button_area((0, 0, 1, 1), self.next_signal, "next_dialog", 2)
self.current_dialogs = self.dialogs[name]
self.current_dialog_id = 0

View file

@ -1,5 +1,3 @@
import time
from src.engine.boss_fight_manager import BossFightManager
from src.engine.camera import Camera
from src.engine.dialogs_manager import DialogsManager
@ -26,6 +24,8 @@ class Engine:
# Etat courant du jeu
self.game_state = GameState.NORMAL
self.clock = pygame.time.Clock()
self.running = False
# Composants du moteur de jeu
@ -36,34 +36,28 @@ class Engine:
self.entity_manager = EntityManager(self.map_manager)
self.boss_fight_manager = BossFightManager(self)
self.event_sheduler = EventSheduler(self)
self.dialogs_manager = DialogsManager(self)
self.dialogs_manager = DialogsManager(self.event_handler)
self.menu_manager = MenuManager(self)
self.sound_manager = SoundManager(60)
self.sound_manager = SoundManager()
def loop(self):
"""Fonction à lancer au début du programme et qui va lancer les updates dans une boucle.
Attend jusqu'à la fin du jeu."""
self.running = True
delta = 1. # Le delta est le temps depuis la dernière image
last_time = time.time_ns()/10E8
while self.running:
self.update(delta)
self.update()
self.clock.tick(60.)
new_time = time.time_ns()/10E8
delta = new_time-last_time
last_time = new_time
def update(self, delta: float):
def update(self):
"""Fonction qui regroupe toutes les updates des composants. Elle permet de mettre à jour le jeu quand on
l'appelle."""
self.camera.update(delta)
self.entity_manager.update(delta)
self.renderer.update(delta)
self.event_handler.update(delta)
self.camera.update()
self.entity_manager.update(0.016666666)
self.renderer.update(0.016666666)
self.event_handler.update()
self.event_sheduler.update()
self.dialogs_manager.update(delta)
self.sound_manager.update(delta)
self.dialogs_manager.update(0.016666666)
self.sound_manager.update(1/60, 100, 100, 100)
def stop(self):
"""Arrête le programme."""

View file

@ -11,8 +11,6 @@ class Entity:
self.x = 8
self.y = 8
self.locked = False # Variable définissant si l'entité est bloqué ou non (.lock() et .unlock())
self.direction = 0 # 0 : tourné vers la droite (ou sens par défaut), 1 : tourné vers la gauche (ou retourné)
# Variables utilisées pour détecter les mouvements
@ -65,23 +63,8 @@ class Entity:
# Si les coordonnées ont changé, l'entité a bougé
x_motion = (self.x - self.last_x)
if x_motion > 0:
self.mouvements[0] = 1
elif x_motion < 0:
self.mouvements[0] = -1
else:
self.mouvements[0] = 0
y_motion = (self.y - self.last_y)
if y_motion > 0:
self.mouvements[1] = 1
elif y_motion < 0:
self.mouvements[1] = -1
else:
self.mouvements[1] = 0
self.mouvements[0] = (self.x - self.last_x) / self.max_speed
self.mouvements[1] = (self.y - self.last_y) / self.max_speed
self.last_x = self.x
self.last_y = self.y
@ -131,67 +114,55 @@ class Entity:
return collision
def move(self, x: float, y: float, map_manager: MapManager, delta: float):
def move(self, x: float, y: float, map_manager: MapManager):
"""Fait bouger l'entité en tenant compte des collisions."""
if not self.locked: # Si l'entité n'est pas verrouillée on calcul le mouvement
# On vérifie le sens du mouvement pour changer self.direction
# On vérifie le sens du mouvement pour changer self.direction
if x > 0:
self.direction = 0
elif x < 0:
self.direction = 1
# On ne met pas de else car si x = 0, on ne change pas de direction
# On normalise la vitesse
initial_speed = math.sqrt(x**2+y**2)
x = x/initial_speed*self.max_speed
y = y/initial_speed*self.max_speed
# On simule le mouvement. Si on ne rencontre pas de collision, on applique le mouvement
if not self.get_collisions(self.x + x, self.y, map_manager):
self.x += x
else:
# Si on a une collision, on avance pixel par pixel jusqu'à atteindre la collision
i = 0
if x > 0:
self.direction = 0
elif x < 0:
self.direction = 1
# On ne met pas de else car si x = 0, on ne change pas de direction
# On normalise la vitesse
initial_speed = math.sqrt(x**2+y**2)
x = x*delta/initial_speed*self.max_speed
y = y*delta/initial_speed*self.max_speed
# On simule le mouvement. Si on ne rencontre pas de collision, on applique le mouvement
if not self.get_collisions(self.x + x, self.y, map_manager):
self.x += x
else:
# Si on a une collision, on avance pixel par pixel jusqu'à atteindre la collision
i = 0
if x > 0:
while not self.get_collisions(self.x + i, self.y, map_manager):
i += 1
i -= 1
else:
while not self.get_collisions(self.x + i, self.y, map_manager):
i -= 1
while not self.get_collisions(self.x + i, self.y, map_manager):
i += 1
self.x += i
# On répète le procédé avec l'ordonnée
if not self.get_collisions(self.x, self.y + y, map_manager):
self.y += y
i -= 1
else:
i = 0
if y > 0:
while not self.get_collisions(self.x, self.y + i, map_manager):
i += 1
while not self.get_collisions(self.x + i, self.y, map_manager):
i -= 1
else:
while not self.get_collisions(self.x, self.y + i, map_manager):
i -= 1
i += 1
i += 1
self.y += i
self.x += i
# On répète le procédé avec l'ordonnée
if not self.get_collisions(self.x, self.y + y, map_manager):
self.y += y
else:
i = 0
if y > 0:
while not self.get_collisions(self.x, self.y + i, map_manager):
i += 1
i -= 1
else:
while not self.get_collisions(self.x, self.y + i, map_manager):
i -= 1
i += 1
self.y += i
def link_animation(self, name: str):
"""Met à jour l'animation en cours de l'entitée."""
"""Met à jour l'animation en cours de l'entité."""
self.animation_name = name
def lock(self):
"""Bloque tout les mouvements de l'entitée"""
self.locked = True
def unlock(self):
"""Débloque tout les mouvements de l'entitée"""
self.locked = False

View file

@ -8,8 +8,6 @@ class EntityManager:
self.entities: dict[str:Entity] = {}
self.player_entity_name = ""
self.map_manager = map_manager
self.locked_before_pause: list[Entity] = []
self.paused = False
def register_entity(self, name: str) -> Entity:
"""Crée une entité et l'enregistre dans un dictionnaire."""
@ -21,10 +19,10 @@ class EntityManager:
"""Définit l'entité donnée comme le joueur. Elle peut donc être controlée."""
self.player_entity_name = name
def move_player_controls(self, x: float, y: float, delta: float):
def move_player_controls(self, x: float, y: float):
"""Bouge le joueur. X et y doivent être compris entre 0 et 1"""
player: Entity = self.get_by_name(self.player_entity_name)
player.move(x, y, self.map_manager, delta)
player.move(x, y, self.map_manager)
def update(self, delta: float):
"""Met à jour toutes les entités enregistrées."""
@ -34,7 +32,7 @@ class EntityManager:
if entity.life_points == 0:
self.entities.pop(entity_name)
if entity.brain is not None and not self.paused:
if entity.brain is not None:
entity.brain.update(delta)
if self.player_entity_name:
@ -51,21 +49,3 @@ class EntityManager:
def get_by_name(self, name: str) -> Entity:
"""Donne l'entité avec le nom donné."""
return self.entities[name]
def pause(self):
"""Met en pause tout les mouvements de toutes les entitées"""
for e in self.get_all_entities():
if e.locked:
self.locked_before_pause.append(e)
else:
e.lock()
self.paused = True
def resume(self):
"""Reprend les mouvement de toutes les entitées qui n'étaient pas lock avant l'appel de .pause()"""
for e in self.get_all_entities():
if not e in self.locked_before_pause:
e.unlock()
self.paused = False
self.locked_before_pause = []

View file

@ -14,9 +14,7 @@ class EventHandler:
self.engine = core
self.key_pressed = []
self.buttons_area = []
self.hovered_buttons_area = []
self.hovered_sliders_area = []
self.sliders_area = []
self.hovered_area = []
@staticmethod
def get_click_collision(rect: tuple[float | int, float | int, float | int, float | int], point: tuple[int, int],
@ -42,7 +40,7 @@ class EventHandler:
callback: FunctionType | classmethod | staticmethod, name: str,
is_window_relative: int = -1,
hover_callback: FunctionType | classmethod | staticmethod = None):
"""Enregistre une zone comme bouton. La fonction donnée sera donc executée lorsque la zone sur la fenêtre
"""Enregistre une zone comme bouton. La fonction donnée sera donc executé lorsque la zone sur la fenêtre
sera cliqué. is_window_relative doit être 0 pour que le rect soit multipliée par la largeur de la fenêtre et 1
pour qu'elle soit multipliée par la hauteur"""
self.buttons_area.append((rect, callback, is_window_relative, name, hover_callback))
@ -58,51 +56,9 @@ class EventHandler:
self.buttons_area = cleared_list
def register_slider_area(self, size: tuple[float | int, float | int],
motion_rect: tuple[float | int, float | int, float | int, float | int],
motion_axes: tuple[bool, bool],
name: str,
is_window_relative: int = -1,
clicked_callback: FunctionType | classmethod | staticmethod = None,
released_callback: FunctionType | classmethod | staticmethod = None,
motion_callback: FunctionType | classmethod | staticmethod = None,
hover_callback: FunctionType | classmethod | staticmethod = None):
"""Enregistre une zone comme une zone déplaçable à l'écran."""
self.sliders_area.append([[motion_rect[0], motion_rect[1], *size], is_window_relative, False, (0, 0),
motion_axes, motion_rect,
clicked_callback, released_callback, hover_callback, motion_callback, name])
# Le premier booléen correspond à l'état de suivi de la souris
def remove_slider_area(self, name: str):
"""Supprime les sliders aux noms donnés."""
# On itère dans toute la liste et on ne garde que les éléments ne portant pas le nom cherché
cleared_list = []
for area in self.sliders_area:
if area[10] != name:
cleared_list.append(area)
self.sliders_area = cleared_list
@staticmethod
def get_slider_area_values(slider: list):
"""Donne la valeur de la zone de slider donnée."""
if slider[5][2]:
x_value = round((slider[0][0]-slider[5][0])/slider[5][2], 5)
else:
x_value = -1
if slider[5][3]:
y_value = round((slider[0][1]-slider[5][1])/slider[5][3], 5)
else:
y_value = -1
return x_value, y_value
def update(self, delta: float):
def update(self):
"""Vérifie s'il y a de nouvelles interactions et les traites."""
window_size = display.get_window_size()
# Récupère les événements
for e in event.get():
if e.type == QUIT:
@ -118,98 +74,27 @@ class EventHandler:
for area in self.buttons_area:
if self.get_click_collision(area[0], e.pos, area[2]):
area[1]()
for area in self.sliders_area:
if self.get_click_collision(
(area[0][0]-area[0][2]/2, area[0][1]-area[0][3]/2, area[0][2], area[0][3]),
e.pos, area[1]):
area[2] = True
if area[1] == 0:
area[3] = (e.pos[0]/window_size[0] - area[0][0], e.pos[1]/window_size[0] - area[0][1])
elif area[1] == 1:
area[3] = (e.pos[0]/window_size[1] - area[0][0], e.pos[1]/window_size[1] - area[0][1])
elif area[1] == 2:
area[3] = (e.pos[0]/window_size[0] - area[0][0], e.pos[1]/window_size[1] - area[0][1])
else:
area[3] = (e.pos[0] - area[0][0], e.pos[1] - area[0][1])
if area[6] is not None:
area[6](self.get_slider_area_values(area))
elif e.type == MOUSEBUTTONUP:
for area in self.sliders_area:
if area[2]:
area[2] = False
if area[7] is not None:
area[7](self.get_slider_area_values(area))
elif e.type == MOUSEMOTION:
for area in self.buttons_area:
if area[4] is not None:
if self.get_click_collision(area[0], e.pos, area[2]):
if area not in self.hovered_buttons_area:
if area not in self.hovered_area:
area[4](True)
self.hovered_buttons_area.append(area)
self.hovered_area.append(area)
else:
if area in self.hovered_buttons_area:
if area in self.hovered_area:
area[4](False)
self.hovered_buttons_area.remove(area)
for area in self.sliders_area:
if area[2]:
if area[4][0]:
if area[1] == 0:
area[0][0] = e.pos[0]/window_size[0]-area[3][0]
elif area[1] == 1:
area[0][0] = e.pos[0]/window_size[1]-area[3][0]
elif area[1] == 2:
area[0][0] = e.pos[0]/window_size[0]-area[3][0]
else:
area[0][0] = e.pos[0] - area[3][0]
if area[4][1]:
if area[1] == 0:
area[0][1] = e.pos[1]/window_size[0]-area[3][1]
elif area[1] == 1:
area[0][1] = e.pos[1]/window_size[1]-area[3][1]
elif area[1] == 2:
area[0][1] = e.pos[1]/window_size[1]-area[3][1]
else:
area[0][1] = e.pos[1]-area[3][1]
if area[0][0] < area[5][0]:
area[0][0] = area[5][0]
if area[0][0] > area[5][0]+area[5][2]:
area[0][0] = area[5][0]+area[5][2]
if area[0][1] < area[5][1]:
area[0][1] = area[5][1]
if area[0][1] > area[5][1]+area[5][3]:
area[0][1] = area[5][1]+area[5][3]
if area[9] is not None:
area[9](self.get_slider_area_values(area))
if area[8] is not None:
if self.get_click_collision(
(area[0][0] - area[0][2] / 2, area[0][1] - area[0][3] / 2, area[0][2], area[0][3]),
e.pos, area[1]):
if area not in self.hovered_sliders_area:
area[8](True)
self.hovered_sliders_area.append(area)
else:
if area in self.hovered_sliders_area:
area[8](False)
self.hovered_sliders_area.remove(area)
self.hovered_area.remove(area)
if self.engine.entity_manager.player_entity_name:
if K_RIGHT in self.key_pressed:
self.engine.entity_manager.move_player_controls(1, 0, delta)
self.engine.entity_manager.move_player_controls(1, 0)
if K_LEFT in self.key_pressed:
self.engine.entity_manager.move_player_controls(-1, 0, delta)
self.engine.entity_manager.move_player_controls(-1, 0)
if K_UP in self.key_pressed:
self.engine.entity_manager.move_player_controls(0, -1, delta)
self.engine.entity_manager.move_player_controls(0, -1)
if K_DOWN in self.key_pressed:
self.engine.entity_manager.move_player_controls(0, 1, delta)
self.engine.entity_manager.move_player_controls(0, 1)
if K_SPACE in self.key_pressed:
self.engine.dialogs_manager.next_signal()
@ -221,13 +106,12 @@ class EventHandler:
if K_p in self.key_pressed:
self.engine.renderer.emit_particles(math.floor(self.engine.entity_manager.get_by_name("player").x),
math.floor(self.engine.entity_manager.get_by_name("player").y),
16, 16, 16, 1, 8, 0, 60., 0.2, 1., (0, 200, 200))
16, 16, 16, 1, 8, 0, 1, 0.2, 1., (0, 200, 200))
if K_o in self.key_pressed:
print(f"Player pos: X = {self.engine.entity_manager.get_by_name('player').x} "
f"Y = {self.engine.entity_manager.get_by_name('player').y}")
if K_x in self.key_pressed:
self.engine.camera.target_zoom *= 1.01
if K_c in self.key_pressed:
self.engine.camera.target_zoom *= 0.99
if K_x in self.key_pressed:
self.engine.camera.target_zoom *= 1.01
if K_c in self.key_pressed:
self.engine.camera.target_zoom *= 0.99

View file

@ -7,74 +7,30 @@ import src.engine.engine
class Widget:
"""Classe parente des widgets de menu."""
def __init__(self, x, y, is_window_relative, widget_name):
def __init__(self, x, y, is_window_relative):
self.x = x
self.y = y
self.is_window_relative = is_window_relative
self.widget_name = widget_name
class Label(Widget):
"""Un widget de texte."""
def __init__(self, x: int | float, y: int | float, text: str, size: int | float, color: tuple[int, int, int],
widget_name: str, centered: bool = False, is_window_relative: int = -1):
super().__init__(x, y, is_window_relative, widget_name)
centered: bool = False, is_window_relative: int = -1):
super().__init__(x, y, is_window_relative)
self.text = text
self.size = size
self.centered = centered
self.color = color
class Slider(Widget):
"""Un widget pouvant être glissé pour récupérer une valeur."""
def __init__(self, cursor_size: tuple[int | float, int | float],
area_rect: tuple[int | float, int | float],
width: int | float,
base_image: pygame.Surface,
hover_image: pygame.Surface,
rail_image: pygame.Surface,
widget_name: str,
value_changed_callback: FunctionType | classmethod | staticmethod | None = None,
is_window_relative: int = -1,
area_name: str = "menu_slider"):
super().__init__(area_rect[0], area_rect[1], is_window_relative, widget_name)
self.base_image = base_image
self.hover_image = hover_image
self.rail_image = rail_image
self.area_name = area_name
self.value_changed_callback = value_changed_callback
self.hovered = False
self.follow_mouse = False
self.cursor_size = cursor_size
self.value = 0.
self.width = width
def set_hover_state(self, state: bool):
"""Modifie la valeur du hover."""
self.hovered = state
def set_value(self, values: tuple[float, float]):
"""Appelée lorsque la valeur du slider est modifiée."""
new_value = values[0]
if new_value != self.value:
self.value = new_value
if self.value_changed_callback is not None:
self.value_changed_callback(self.value)
def get_value(self):
"""Retourne la valeur entre 0.0 et 1.0 du slider."""
return self.value
class Button(Widget):
"""Un widget de bouton."""
def __init__(self, x: int | float, y: int | float, text: str, size: int | float, color: tuple[int, int, int],
callback: FunctionType | classmethod | staticmethod, base_image: pygame.Surface,
hover_image: pygame.Surface, widget_name: str, centered: bool = False, is_window_relative: int = -1,
hover_image: pygame.Surface, centered: bool = False, is_window_relative: int = -1,
area_name: str = "menu_button"):
super().__init__(x, y, is_window_relative, widget_name)
super().__init__(x, y, is_window_relative)
self.text = text
self.size = size
self.color = color
@ -112,48 +68,28 @@ class MenuManager:
"""Ajoute le menu donné au manager de menu avec le nom donné."""
self.menus[name] = menu
def get_widgets_at_name(self, menu_name: str, widget_name: str):
"""Donne le widget au nom donné dans le menu au nom donné."""
menu = self.menus[menu_name]
found_sliders = []
for widget in menu.widgets:
if widget.widget_name == widget_name:
found_sliders.append(widget)
return found_sliders
def show(self, name: str):
"""Affiche le menu au nom donné."""
self.active_menu = self.menus[name]
# On itère dans tous les bouttons pour leur ajouter une interaction
for widget in self.active_menu.widgets:
if isinstance(widget, Button):
width = widget.base_image.get_width() / self.engine.renderer.window_size[0]
height = widget.base_image.get_height() / self.engine.renderer.window_size[1]
area_x = widget.x
area_y = widget.y
if widget.centered:
for btn in self.active_menu.widgets:
if isinstance(btn, Button):
width = btn.base_image.get_width() / self.engine.renderer.window_size[0]
height = btn.base_image.get_height() / self.engine.renderer.window_size[1]
area_x = btn.x
area_y = btn.y
if btn.centered:
area_x -= width / 2
area_y -= height / 2
self.engine.event_handler.register_button_area((area_x, area_y, width, height), widget.callback,
widget.area_name,
widget.is_window_relative, widget.set_hover_state)
elif isinstance(widget, Slider):
self.engine.event_handler.register_slider_area(widget.cursor_size,
(widget.x, widget.y, widget.width, 0), (True, False),
widget.area_name,
widget.is_window_relative,
hover_callback=widget.set_hover_state,
motion_callback=widget.set_value)
self.engine.event_handler.register_button_area((area_x, area_y, width, height), btn.callback,
btn.area_name,
btn.is_window_relative, btn.set_hover_state)
def hide(self):
"""Cache le menu actuelement à l'écran."""
"""Affiche le menu actuelement à l'écran."""
# On itère dans tous les bouttons pour retirer l'interaction
for widget in self.active_menu.widgets:
if isinstance(widget, Button):
self.engine.event_handler.remove_button_area(widget.area_name)
if isinstance(widget, Slider):
self.engine.event_handler.remove_slider_area(widget.area_name)
for btn in self.active_menu.widgets:
if isinstance(btn, Button):
self.engine.event_handler.remove_button_area(btn.area_name)
self.active_menu = None

View file

@ -1,15 +1,13 @@
import math
import random
from time import time_ns
from types import FunctionType
from pygame import display, image, surface, transform, draw, font
from pygame.locals import RESIZABLE, SRCALPHA, FULLSCREEN
import src.engine.engine as engine
from src.engine.animation import Anim
from src.engine.enums import GameState
from src.engine.menu_manager import Label, Button, Slider
from src.engine.menu_manager import Label, Button
class Renderer:
@ -17,7 +15,6 @@ class Renderer:
def __init__(self, core: 'engine.Engine'):
self.engine = core
self.timer = 0 # Timer local
self.window_type = RESIZABLE
self.window_size = (display.Info().current_w, display.Info().current_h) if self.window_type == FULLSCREEN else (
600, 600)
@ -40,24 +37,6 @@ class Renderer:
# Particules affichées
self.particles = []
# Varialbes du fadeout
self.fadeout_timer = 0 # Timer de fadeout
self.fadeout_is_fading = False
self.fadeout_fade_in_s = 0
self.fadeout_fade_color = (255, 255, 255)
self.fadeout_fade_opacity = 100
self.fadeout_pause = False
self.fadeout_fade_callback = None
# Variables du fadein
self.fadein_timer = 0 # Timer de fadein
self.fadein_is_fading = False
self.fadein_fade_in_s = 0
self.fadein_fade_color = (255, 255, 255)
self.fadein_fade_opacity = 100
self.fadein_pause = False
self.fadein_fade_callback = None
def emit_particles(self, x: int, y: int, w: int, h: int, count: int, min_size: int, max_size: int,
min_speed: float, max_speed: float, min_life_time: float, max_life_time: float,
color: tuple[int, int, int]):
@ -101,40 +80,18 @@ class Renderer:
def update(self, delta: float):
"""Fait le rendu du jeu."""
self.timer -= delta
self.fadeout_timer -= delta
self.fadein_timer -= delta
if self.fadeout_timer < 0:
if self.fadeout_is_fading:
self.fadeout_is_fading = False
if self.fadeout_pause:
self.engine.entity_manager.resume()
self.fadeout_pause = False
if self.fadeout_fade_callback is not None:
self.fadeout_fade_callback()
if self.fadein_timer < 0:
if self.fadein_is_fading:
self.fadein_is_fading = False
if self.fadein_pause:
self.engine.entity_manager.resume()
self.fadein_pause = False
if self.fadein_fade_callback is not None:
self.fadein_fade_callback()
self.window.fill((255, 255, 255))
# On crée une surface qui sera ajoutée à la fenêtre apres rendered_surface pour pouvoir mettre des GUI
gui_surface = surface.Surface(display.get_window_size(), SRCALPHA)
gui_surface.fill((0, 0, 0, 0))
if self.engine.game_state == GameState.NORMAL:
# 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)
# On crée une surface qui sera ajoutée à la fenêtre apres rendered_surface pour pouvoir mettre des GUI
gui_surface = surface.Surface(display.get_window_size(), SRCALPHA)
gui_surface.fill((0, 0, 0, 0))
self.render_layer(0, rendered_surface)
self.render_layer(1, rendered_surface)
self.render_entities(rendered_surface, gui_surface, delta)
@ -148,6 +105,7 @@ class Renderer:
math.ceil(rendered_surface_size[1] * self.engine.camera.zoom))),
(0, 0))
self.window.blit(gui_surface, (0, 0))
elif self.engine.game_state == GameState.BOSS_FIGHT:
self.window.fill((255, 230, 230))
@ -157,37 +115,23 @@ class Renderer:
# Rend les menus
self.render_menus()
if self.fadeout_is_fading != self.fadein_is_fading:
if self.fadeout_is_fading:
r, g, b = self.fadeout_fade_color
a = (1 - self.fadeout_timer / self.fadeout_fade_in_s) * self.fadeout_fade_opacity
gui_surface.fill((r, g, b, a))
if self.fadein_is_fading:
r, g, b = self.fadein_fade_color
a = self.fadein_timer / self.fadein_fade_in_s * self.fadein_fade_opacity
gui_surface.fill((r, g, b, a))
self.window.blit(gui_surface, (0, 0))
# Conteur de FPS en mode DEBUG
if self.engine.DEBUG_MODE:
self.window.blit(font.SysFont("Arial", 20).render(f"FPS: {round(1/delta if delta else 1)}, Game Status: {'Paused' if self.engine.entity_manager.paused else 'Playing'}", True, (255, 0, 0)),
self.window.blit(font.SysFont("Arial", 20).render(f"FPS: {round(self.engine.clock.get_fps())}", True, (255, 0, 0)),
(0, 0))
player = self.engine.entity_manager.get_by_name('player')
self.window.blit(font.SysFont("Arial", 20).render(f"X: {round(player.x, 2)} Y:{round(player.y, 2)}",
True, (255, 0, 0)), (0, 30))
self.window.blit(font.SysFont("Arial", 20).render(f"Zoom: {round(self.engine.camera.zoom, 2)}",
True, (255, 0, 0)), (0, 60))
self.window.blit(font.SysFont("Arial", 20).render(f"Volume: {self.engine.sound_manager.music_get_volume()}, Pos: {self.engine.sound_manager.music_get_current_song_pos()}s, Index: {self.engine.sound_manager.music_current_index}, Paused: {self.engine.sound_manager.music_is_paused}",
self.window.blit(font.SysFont("Arial", 20).render(f"Volume: {self.engine.sound_manager.music_get_volume()}, Pos: {self.engine.sound_manager.music_get_current_song_pos()}s, Index: {self.engine.sound_manager.music_current_index}, Paused: {self.engine.sound_manager.music_is_paused}, Shuffled: {self.engine.sound_manager.music_shuffle_playlist}",
True, (255, 0, 0)), (0, 90))
self.window.blit(font.SysFont("Arial", 20).render(f"Track: {self.engine.sound_manager.music_current_song}",
True, (255, 0, 0)), (0, 120))
window_size = display.get_window_size()
# On rend maintenant toutes les zones de détection de la fenêtre
for area in self.engine.event_handler.buttons_area:
window_size = display.get_window_size()
if area[2] == 0:
draw.rect(self.window, (255, 255, 0),
(area[0][0] * window_size[0], area[0][1] * window_size[0],
@ -204,58 +148,6 @@ class Renderer:
draw.rect(self.window, (255, 255, 0),
area[0], width=1)
for area in self.engine.event_handler.sliders_area:
if area[1] == 0:
draw.rect(self.window, (0, 255, 20),
((area[0][0]-area[0][2]/2) * window_size[0], (area[0][1]-area[0][3]/2) * window_size[0],
area[0][2] * window_size[0], area[0][3] * window_size[0]), width=1)
draw.rect(self.window, (0, 255, 200),
(area[5][0] * window_size[0], area[5][1] * window_size[0],
area[5][2] * window_size[0], area[5][3] * window_size[0]), width=1)
draw.line(self.window, (255, 0, 0),
(area[0][0] * window_size[0] - 2, area[0][1] * window_size[0]),
(area[0][0] * window_size[0] + 2, area[0][1] * window_size[0]))
draw.line(self.window, (255, 0, 0),
(area[0][0] * window_size[0], area[0][1] * window_size[0] - 2),
(area[0][0] * window_size[0], area[0][1] * window_size[0] + 2))
elif area[1] == 1:
draw.rect(self.window, (0, 255, 20),
((area[0][0]-area[0][2]/2) * window_size[1], (area[0][1]-area[0][3]/2) * window_size[1],
area[0][2] * window_size[1], area[0][3] * window_size[1]), width=1)
draw.rect(self.window, (0, 255, 200),
(area[5][0] * window_size[1], area[5][1] * window_size[1],
area[5][2] * window_size[1], area[5][3] * window_size[1]), width=1)
draw.line(self.window, (255, 0, 0),
(area[0][0] * window_size[1] - 2, area[0][1] * window_size[1]),
(area[0][0] * window_size[1] + 2, area[0][1] * window_size[1]))
draw.line(self.window, (255, 0, 0),
(area[0][0] * window_size[1], area[0][1] * window_size[1] - 2),
(area[0][0] * window_size[1], area[0][1] * window_size[1] + 2))
elif area[1] == 2:
draw.rect(self.window, (0, 255, 20),
((area[0][0]-area[0][2]/2) * window_size[0], (area[0][1]-area[0][3]/2) * window_size[1],
area[0][2] * window_size[0], area[0][3] * window_size[1]), width=1)
draw.rect(self.window, (0, 255, 200),
(area[5][0] * window_size[0], area[5][1] * window_size[1],
area[5][2] * window_size[0], area[5][3] * window_size[1]), width=1)
draw.line(self.window, (255, 0, 0),
(area[0][0]*window_size[0] - 2, area[0][1]*window_size[1]),
(area[0][0]*window_size[0] + 2, area[0][1]*window_size[1]))
draw.line(self.window, (255, 0, 0),
(area[0][0]*window_size[0], area[0][1]*window_size[1] - 2),
(area[0][0]*window_size[0], area[0][1]*window_size[1] + 2))
else:
draw.rect(self.window, (0, 255, 20),
(area[0][0]-area[0][2]//2, area[0][1]-area[0][3]//2, area[0][2], area[0][3]), width=1)
draw.rect(self.window, (0, 255, 200),
area[5], width=1)
draw.line(self.window, (255, 0, 0),
(area[0][0]-2, area[0][1]),
(area[0][0]+2, area[0][1]))
draw.line(self.window, (255, 0, 0),
(area[0][0], area[0][1]-2),
(area[0][0], area[0][1]+2))
# Rendu présent dans tous les types de jeu
self.render_dialogs_box()
@ -344,46 +236,6 @@ class Renderer:
self.window.blit(btn_image, (x, y))
self.window.blit(rendered_text, (x, y))
elif isinstance(widget, Slider):
if widget.hovered:
slider_image = widget.hover_image
else:
slider_image = widget.base_image
rail_image = widget.rail_image
if widget.is_window_relative == 0:
slider_image = transform.scale(slider_image,
(slider_image.get_width()*window_size[0]/self.window_size[0],
slider_image.get_height()*window_size[0]/self.window_size[0]))
rail_image = transform.scale(rail_image,
(rail_image.get_width() * window_size[0] / self.window_size[0],
rail_image.get_height() * window_size[0] / self.window_size[
0]))
width = widget.width*window_size[0]
elif widget.is_window_relative == 1:
slider_image = transform.scale(slider_image,
(slider_image.get_width()*window_size[1]/self.window_size[1],
slider_image.get_height()*window_size[1]/self.window_size[1]))
rail_image = transform.scale(rail_image,
(rail_image.get_width() * window_size[1] / self.window_size[1],
rail_image.get_height() * window_size[1] / self.window_size[
1]))
width = widget.width * window_size[1]
elif widget.is_window_relative == 2:
slider_image = transform.scale(slider_image,
(slider_image.get_width()*window_size[0]/self.window_size[0],
slider_image.get_height()*window_size[1]/self.window_size[1]))
rail_image = transform.scale(rail_image,
(rail_image.get_width() * window_size[0] / self.window_size[0],
rail_image.get_height() * window_size[1] / self.window_size[
1]))
width = widget.width * min(window_size[0], window_size[1])
self.window.blit(rail_image, (x+(width-rail_image.get_width()) // 2,
y - rail_image.get_height() // 2))
self.window.blit(slider_image, (x+widget.value*width-slider_image.get_width()//2,
y-slider_image.get_height()//2))
def render_dialogs_box(self):
"""Rend la boite de dialogue lorsqu'un dialogue est lancé."""
@ -480,8 +332,8 @@ class Renderer:
draw.rect(rendered_surface, part[7], part_dest + (part[2], part[2]))
part[5] += delta
part[0] += part[3]*delta
part[1] += part[4]*delta
part[0] += part[3]
part[1] += part[4]
if part[5] > part[6]:
self.particles.remove(part)
@ -636,27 +488,3 @@ class Renderer:
(math.floor(x * self.tile_size - self.engine.camera.x + x_middle_offset),
math.floor(y * self.tile_size - self.engine.camera.y + y_middle_offset),
self.tile_size, self.tile_size), width=1)
def fadeout(self, fade_s: float, fade_color: tuple[int, int, int] = (0, 0, 0), fade_opacity: int = 100, pause_world: bool = True, callback: FunctionType = None):
"""Fait un fondu vers la couleur au format : (255, 255, 255) et a l'opacité max spécifié, et dans le temps spécifié, appelle la fonction callback une fois le fadout terminé"""
self.fadein_is_fading = False
self.fadeout_timer = fade_s
self.fadeout_fade_in_s = fade_s
self.fadeout_is_fading = True
self.fadeout_fade_color = fade_color
self.fadeout_fade_opacity = round(fade_opacity * 255 / 100)
self.fadeout_pause = pause_world
self.fadeout_fade_callback = callback
self.engine.entity_manager.pause()
def fadein(self, fade_s: float, fade_color: tuple[int, int, int] = (0, 0, 0), fade_opacity: int = 100, pause_world: bool = True, callback: FunctionType = None):
"""Fait un fondu depuis la couleur au format : (255, 255, 255) et depuis l'opacité spécifié, et dans le temps spécifié, appelle la fonction callback une fois le fadout terminé"""
self.fadeout_is_fading = False
self.fadein_timer = fade_s
self.fadein_fade_in_s = fade_s
self.fadein_is_fading = True
self.fadein_fade_color = fade_color
self.fadein_fade_opacity = round(fade_opacity * 255 / 100)
self.fadein_pause = pause_world
self.fadein_fade_callback = callback
self.engine.entity_manager.pause()

View file

@ -1,54 +1,145 @@
from src.engine.entity import Entity
from random import randint
from pygame import mixer
from math import sqrt
from time import time
class SoundManager:
def __init__(self, music_base_volume: float):
self.__tick = 0 # Compteur de la valeur d'un tick sur 1 (Utilisé pour le comptage de tick)
def __init__(self):
self.__tick = 0 # Compteur de la valeur d'un tick (Utilisé pour le comptage de tick)
self.tick = 0 # Compteur de tick
self.time = 0 # Temps local a la class (en s)
self.music_master_volume = 60
self.sound_global_master_volume = 100
self.sound_master_volume = 100
self.music_playlist = []
self.music_current_song = ""
self.music_play_playlist = False
self.music_current_index = 0
self.music_shuffle_playlist = True
self.music_next_request = False
self.music_set_volume(self.music_master_volume)
self.music_before_pause_pos = 0
self.music_before_pause_song = ""
self.music_is_paused = False
self.music_pos_delay = 0
self.music_set_volume(music_base_volume)
self.sound_currently_playing: dict[float: list[mixer.Sound, float, list[float, float], float]] = {} # Format {unique_id : [Sound, max_volume, [pos_x, pos_y], stop_at]}
self.sound_loaded: dict[str: mixer.Sound] = {}# Format : {name: mixer.Sound}
self.sound_global_currently_playing: dict[float: list[mixer.Sound, float, float]] = {} # Format {unique_id: [Sound, volume, stop_at]}
self.sound_hears_anchor = None
def update(self, delta: float):
self.do1 = True
self.do2 = True
self.do3 = True
self.do4 = True
self.music_add_to_playlist(".\\assets\\OST\\Vampire Killer.mp3")
self.music_add_to_playlist(".\\assets\\OST\\Hyrule Field - The Legend of Zelda Ocarina of Time.mp3")
self.music_add_to_playlist(".\\assets\\OST\\Lost Woods - The Legend of Zelda Ocarina of Time.mp3")
self.music_add_to_playlist(".\\assets\\OST\\Title Theme - The Legend of Zelda Ocarina of Time.mp3")
self.music_start_playlist()
def update(self, delta: float, music_master_volume: float, sound_global_master_volume: float, sound_master_volume: float):
self.__tick += delta
self.tick = int(self.__tick / delta)
self.time = self.tick * delta
self.music_master_volume = music_master_volume
self.sound_global_master_volume = sound_global_master_volume
self.sound_master_volume = sound_master_volume
if self.sound_hears_anchor: # Update la position des "Oreilles" du joueur (Ou de l'entité séléctionné comme ancre pour les oreilles)
self.sound_hears_x = self.sound_hears_anchor.x
self.sound_hears_y = self.sound_hears_anchor.y
for key in self.sound_global_currently_playing.keys(): # Son globaux
sound_container = self.sound_global_currently_playing[key]
if sound_container[2] > self.time:
self.sound_global_currently_playing.pop(key)
else:
sound_container[0].set_volume(round(sound_global_master_volume / 100 * sound_container[1] / 100, 3))
for key in self.sound_currently_playing.keys(): # Son locaux
sound_container = self.sound_currently_playing[key]
if sound_container[3] > self.time: # Timeout des sons
self.sound_currently_playing.pop(key)
else: # Gère le volume en fonction de la position
sound = sound_container[0]
max_volume = sound_container[1]
pos_x, pos_y = sound_container[2]
sound.set_volume(max(0, int((round(sound_master_volume / 100 * max_volume / 100, 3)) - sqrt((pos_x - self.sound_hears_x) ** 2 + (pos_y - self.sound_hears_y) ** 2))) / (round(sound_master_volume / 100 * max_volume / 100, 3)))
if self.music_play_playlist and not self.music_is_paused: # Musique de fond
if not mixer.music.get_busy():
if len(self.music_playlist) == 0:
pass
elif self.music_current_song == "":
self.__music_play(self.music_playlist[0])
else:
just_played_index = self.music_playlist.index(self.music_current_song)
if len(self.music_playlist) - 1 <= just_played_index: # Dernier son de la playlist / la playlist a rétréci entre temps
self.music_current_index = 0
self.__music_play(self.music_playlist[0]) # Recommence depuis le début de la playlist
else:
self.music_current_index = just_played_index + 1
self.__music_play(self.music_playlist[self.music_current_index]) # Joue la musique suivante dans la playlist
else:
if self.music_current_song in self.music_playlist:
just_played_index = self.music_playlist.index(self.music_current_song)
if self.music_shuffle_playlist and len(self.music_playlist) != 1:
while True:
new_index = randint(0, len(self.music_playlist) - 1)
if new_index != just_played_index:
break
self.music_current_index = new_index
self.__music_play(self.music_playlist[new_index])
elif len(self.music_playlist) - 1 <= just_played_index: # Dernier son de la playlist / la playlist a rétréci entre temps
self.music_current_index = 0
self.__music_play(self.music_playlist[0]) # Recommence depuis le début de la playlist
else:
self.music_current_index = just_played_index + 1
self.__music_play(self.music_playlist[self.music_current_index]) # Joue la musique suivante dans la playlist
else: # Song removed from playlist, no idea what was the index, starting again from start or from random index if playlist_shuffle = True
new_index = randint(0, len(self.music_playlist) - 1)
self.music_current_index = new_index
self.__music_play(self.music_playlist[new_index])
if self.music_next_request:
self.music_next_request = False
mixer.music.fadeout(1)
if self.do1 and self.time > 5:
self.music_playlist_set_shuffle(False)
self.do1 = False
if self.do2 and self.time > 15:
self.music_next()
self.do2 = False
if self.do3 and self.time > 25:
self.music_pause()
self.do3 = False
if self.do4 and self.time > 35:
self.music_next()
self.do4 = False
def music_get_volume(self):
return mixer.music.get_volume() * 100
def music_set_volume(self, new_volume: float):
"""Définit le nouveau volume de la musique"""
mixer.music.set_volume((round(new_volume / 100, 3)))
mixer.music.set_volume((round(self.music_master_volume / 100 * new_volume / 100, 3)))
def music_pause(self, fade_s: float, restart_tolerance: float = 33):
def music_pause(self, fade_s: float = 0, restart_tolerance: float = 33):
"""Met en pause la musique, la musique reprendra à la fin de la musique moin la tolérance (en pourcentage)"""
self.music_is_paused = True
self.music_before_pause_pos = self.music_get_current_song_pos() + fade_s * restart_tolerance / 100 # Récupère la position a laquelle le son doit reprendre lors du .resume()
@ -56,19 +147,17 @@ class SoundManager:
mixer.music.fadeout(fade_s * 1000)
def music_resume(self, fade_s: float):
def music_resume(self, fade_s: float = 0):
self.__music_play(self.music_before_pause_song, fade_s, self.music_before_pause_pos)
self.music_before_pause_pos = 0
self.music_before_pause_song = ""
def music_get_current_song_pos(self):
if mixer.music.get_busy():
return round(mixer.music.get_pos() /1000 + self.music_pos_delay, 3)
else:
return round(self.music_before_pause_pos, 3)
def __music_play(self, song: str, fade_s: float = 0, start_at: float = 0):
mixer.music.unload()
mixer.music.load(song)
@ -92,3 +181,65 @@ class SoundManager:
def music_stop_playlist(self):
self.music_play_playlist = False
def music_playlist_set_shuffle(self, shuffle: bool):
self.music_shuffle_playlist = shuffle
def music_next(self):
self.music_next_request = True
def sound_link_hears(self, entity: Entity):
self.sound_hears_anchor = entity
def create_unique_id(self):
return time()*10e99999
def sound_load(self, file_path: str, name: str):
self.sound_loaded[name] = mixer.Sound(file_path)
def sound_play(self, name: str, max_volume: float, pos_x: float, pos_y: float):
sound = self.sound_loaded[name]
stop_at = stop_at = self.time + sound.get_length()
unique_id = self.create_unique_id()
self.sound_currently_playing[unique_id] = [sound, max_volume, [pos_x, pos_y], stop_at] # Format {unique_id : [Sound, max_volume, [pos_x, pos_y], stop_at]
return unique_id
def sound_stop(self, name: str, unique_id: float = None, all: bool = False):
if all:
for key in self.sound_currently_playing.keys():
self.sound_currently_playing.pop(key)[0].stop()
elif unique_id:
sound_container = self.sound_currently_playing.get(unique_id, None)
if sound_container:
self.sound_currently_playing.pop(unique_id)[0].stop()
else:
for key in self.sound_currently_playing.keys():
if self.sound_loaded[name] == self.sound_currently_playing[key][0]:
self.sound_currently_playing.pop(key)[0].stop()
def sound_global_play(self, name: str, volume: float):
"""Joue un son avec le même son dans tout le monde"""
sound = self.sound_loaded[name]
stop_at = self.time + sound.get_length()
unique_id = self.create_unique_id()
self.sound_global_currently_playing[unique_id] = [sound, volume, stop_at]
sound.play()
return unique_id
def sound_global_stop(self, name: str, unique_id: float = None, all: bool = False):
if all:
for key in self.sound_global_currently_playing.keys():
self.sound_global_currently_playing.pop(key)[0].stop()
elif unique_id:
sound_container = self.sound_global_currently_playing.get(unique_id, None)
if sound_container:
self.sound_global_currently_playing.pop(unique_id)[0].stop()
else:
for key in self.sound_global_currently_playing.keys():
if self.sound_loaded[name] == self.sound_global_currently_playing[key][0]:
self.sound_global_currently_playing.pop(key)[0].stop()

View file

@ -33,26 +33,17 @@ class Game(Engine):
def start_game(self):
self.game_state = GameState.NORMAL
self.renderer.fadein(1, (0, 0, 0), 100, True)
def play_button_callback(self):
self.renderer.fadeout(1, (0, 0, 0), 100, True, self.start_game)
self.menu_manager.hide()
def setup_main_menu(self):
"""Crée les éléments du menu principal."""
menu = Menu()
menu.add_widget(Label(0.5, 0.1, "The Forest's Secret", 0.1, (0, 0, 0), "game_title", True, 0))
menu.add_widget(Label(0.5, 0.1, "The Forest's Secret", 0.1, (0, 0, 0), True, 0))
btn_base_image = pygame.image.load("assets/textures/GUI/button_1.png").convert_alpha()
btn_hover_image = pygame.image.load("assets/textures/GUI/button_2.png").convert_alpha()
slider_base_image = pygame.image.load("assets/textures/GUI/slider_cursor_1.png").convert_alpha()
slider_hover_image = pygame.image.load("assets/textures/GUI/slider_cursor_2.png").convert_alpha()
slider_rail_image = pygame.image.load("assets/textures/GUI/slider_rail_1.png").convert_alpha()
menu.add_widget(Button(0.5, 0.3, "play", 0.08, (0, 0, 0), self.play_button_callback, btn_base_image, btn_hover_image, "play_button", True, 0))
base_image = pygame.image.load("assets/textures/GUI/button_1.png").convert_alpha()
hover_image = pygame.image.load("assets/textures/GUI/button_2.png").convert_alpha()
menu.add_widget(Button(0.5, 0.3, "play", 0.08, (0, 0, 0), self.start_game, base_image, hover_image, True, 0))
self.menu_manager.register_menu(menu, "main")
self.menu_manager.show("main")
@ -72,7 +63,7 @@ class Game(Engine):
player.collision_rect = [-6, -7, 6, 16]
player.set_default_life(15)
player.max_speed = 64.0
player.max_speed = 1.1
self.entity_manager.set_player_entity("player")
@ -95,7 +86,7 @@ class Game(Engine):
mob.collision_rect = [-15, -7, 12, 7]
mob.set_default_life(5)
mob.max_speed = 60.
mob.max_speed = 1.
mob.x, mob.y = 1600, 16