sliders #32

Merged
yannis merged 19 commits from sliders into main 2024-01-12 22:29:33 +00:00
7 changed files with 307 additions and 31 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -14,7 +14,9 @@ class EventHandler:
self.engine = core
self.key_pressed = []
self.buttons_area = []
self.hovered_area = []
self.hovered_buttons_area = []
self.hovered_sliders_area = []
self.sliders_area = []
@staticmethod
def get_click_collision(rect: tuple[float | int, float | int, float | int, float | int], point: tuple[int, int],
@ -40,7 +42,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é lorsque la zone sur la fenêtre
"""Enregistre une zone comme bouton. La fonction donnée sera donc executée 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))
@ -56,9 +58,51 @@ 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):
"""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:
@ -75,17 +119,87 @@ class EventHandler:
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_area:
if area not in self.hovered_buttons_area:
area[4](True)
self.hovered_area.append(area)
self.hovered_buttons_area.append(area)
else:
if area in self.hovered_area:
if area in self.hovered_buttons_area:
area[4](False)
self.hovered_area.remove(area)
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)
if self.engine.entity_manager.player_entity_name:
if K_RIGHT in self.key_pressed:
@ -116,4 +230,4 @@ class EventHandler:
self.engine.camera.target_zoom *= 1.01
if K_c in self.key_pressed:
self.engine.camera.target_zoom *= 0.99

View file

@ -7,30 +7,74 @@ import src.engine.engine
class Widget:
"""Classe parente des widgets de menu."""
def __init__(self, x, y, is_window_relative):
def __init__(self, x, y, is_window_relative, widget_name):
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],
centered: bool = False, is_window_relative: int = -1):
super().__init__(x, y, is_window_relative)
widget_name: str, centered: bool = False, is_window_relative: int = -1):
super().__init__(x, y, is_window_relative, widget_name)
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, centered: bool = False, is_window_relative: int = -1,
hover_image: pygame.Surface, widget_name: str, centered: bool = False, is_window_relative: int = -1,
area_name: str = "menu_button"):
super().__init__(x, y, is_window_relative)
super().__init__(x, y, is_window_relative, widget_name)
self.text = text
self.size = size
self.color = color
@ -68,28 +112,48 @@ 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 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:
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:
area_x -= width / 2
area_y -= height / 2
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)
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)
def hide(self):
"""Affiche le menu actuelement à l'écran."""
# On itère dans tous les bouttons pour retirer l'interaction
for btn in self.active_menu.widgets:
if isinstance(btn, Button):
self.engine.event_handler.remove_button_area(btn.area_name)
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)
self.active_menu = None

View file

@ -7,7 +7,7 @@ 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
from src.engine.menu_manager import Label, Button, Slider
class Renderer:
@ -129,9 +129,10 @@ class Renderer:
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],
@ -148,6 +149,58 @@ 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()
@ -236,6 +289,46 @@ 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é."""

View file

@ -38,12 +38,17 @@ class Game(Engine):
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), True, 0))
menu.add_widget(Label(0.5, 0.1, "The Forest's Secret", 0.1, (0, 0, 0), "game_title", 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()
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.start_game, btn_base_image, btn_hover_image, "play_button", True, 0))
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")