diff --git a/GUI.py b/GUI.py index 3a94f52..bdddc13 100644 --- a/GUI.py +++ b/GUI.py @@ -1,6 +1,6 @@ import sys import pygame -import button +import components.button as button # init pygame. pygame.init() @@ -11,15 +11,15 @@ # set game name. pygame.display.set_caption("ʙᴀsᴋᴇᴛ ɢᴏ !") # define window's size. -window = pygame.display.set_mode((1024,640)) +window = pygame.display.set_mode((1024, 640)) fond = pygame.image.load('./img/bg.png') -window.blit(fond, (0,0)) -#load button images +window.blit(fond, (0, 0)) +# load button images start_img = pygame.image.load('./img/start_btn.png').convert_alpha() exit_img = pygame.image.load('./img/exit_btn.png').convert_alpha() start_img_hov = pygame.image.load('./img/start_btn_hover.png').convert_alpha() exit_img_hov = pygame.image.load('./img/exit_btn_hover.png').convert_alpha() -#create button instances +# create button instances start_button = button.Button(397, 390, start_img, 0.8) start2_button = button.Button(397, 390, start_img_hov, 0.8) exit_button = button.Button(412, 500, exit_img, 0.8) @@ -31,14 +31,14 @@ while is_running: if start_button.draw(window): - import main.py + import main if exit_button.draw(window): pygame.quit() sys.exit() MOUSE_POS = pygame.mouse.get_pos() - if 397 <= MOUSE_POS[0] < 622 and 390 <= MOUSE_POS[1] < 490 : + if 397 <= MOUSE_POS[0] < 622 and 390 <= MOUSE_POS[1] < 490: start2_button.draw(window) - if 412 <= MOUSE_POS[0] < 607 and 500 <= MOUSE_POS[1] < 600 : + if 412 <= MOUSE_POS[0] < 607 and 500 <= MOUSE_POS[1] < 600: exit2_button.draw(window) for event in pygame.event.get(): if event.type == pygame.QUIT: diff --git a/components/__init__.py b/components/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ball.py b/components/ball.py similarity index 100% rename from ball.py rename to components/ball.py diff --git a/button.py b/components/button.py similarity index 100% rename from button.py rename to components/button.py diff --git a/player.py b/components/player.py similarity index 96% rename from player.py rename to components/player.py index fec7542..4527fa8 100644 --- a/player.py +++ b/components/player.py @@ -1,36 +1,36 @@ -import pygame -from element import Element - -class Player(): - def __init__(self, game, name:str) -> None: - # set player's game. - self.__game = game - # set our player's name. - self.__name = name - # init our score. - self.__score = 0 - pass - def setup(self) -> None: - """ - Setup the player into the game. - """ - # game's default font. - font = pygame.font.Font('freesansbold.ttf', 32) - text = font.render(str(self.__score), True, (255, 255, 255), None) - # register the game element. - self.__game.get_window().register_element("player_" + self.__name + "_score", Element(text, 512, 10)) - def get_name(self) -> str: - """ - :return: str: player's name. - """ - return self.__name - def get_score(self) -> int: - """ - :return: int: player's score. - """ - def set_score(self, score:int) -> None: - """ - Define player's score. - :param: int score: the score to set. - """ +import pygame +from element import Element + +class Player(): + def __init__(self, game, name:str) -> None: + # set player's game. + self.__game = game + # set our player's name. + self.__name = name + # init our score. + self.__score = 0 + pass + def setup(self) -> None: + """ + Setup the player into the game. + """ + # game's default font. + font = pygame.font.Font('freesansbold.ttf', 32) + text = font.render(str(self.__score), True, (255, 255, 255), None) + # register the game element. + self.__game.get_window().register_element("player_" + self.__name + "_score", Element(text, 512, 10)) + def get_name(self) -> str: + """ + :return: str: player's name. + """ + return self.__name + def get_score(self) -> int: + """ + :return: int: player's score. + """ + def set_score(self, score:int) -> None: + """ + Define player's score. + :param: int score: the score to set. + """ self.__score = score \ No newline at end of file diff --git a/events/__init__.py b/events/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ball_release_event_listener.py b/events/ball_release_event_listener.py similarity index 86% rename from ball_release_event_listener.py rename to events/ball_release_event_listener.py index 481bcc2..0a9474c 100644 --- a/ball_release_event_listener.py +++ b/events/ball_release_event_listener.py @@ -1,74 +1,77 @@ -from event_listener import EventListener -from stoppable_thread import StoppableThread -from vector import Vector -from math import (atan, cos, sin) -from time import time -import pygame - -class BallReleaseEventListener(EventListener): - def __init__(self) -> None: - super().__init__() - - def run(self, event, game): - # retrieve the ball. - ball = game.get_window().get_element("ball") - # retrieve the placeholder ball. - placeholder_ball = game.get_window().get_element("placeholder_ball") - # check if the ball is at the same position - if abs(ball.get_x() - ball.get_initial_x()) < 15 and abs(ball.get_y() - ball.get_initial_y()) < 15 or ball.is_released(): - return - ball.set_released(True) - # disable ball. - placeholder_ball.set_visible(False) - # clear dots. - for i in range(20): - game.get_window().remove_element(("dot_",str(i))) - # launch new thread. - self.__t = StoppableThread(target=self.__move_ball, args=(game,)) - # register the thread in order to be able to kill it. - game.register_thread(self.__t) - # start the thread execution. - self.__t.start() - def __move_ball(self, game): - """ - Makes the ball move. - """ - # retrieve the ball. - ball = game.get_window().get_element("ball") - # define a delta time. - delta_time = 0.01 - # get the width and the height of the window. - w, h = pygame.display.get_surface().get_size() - # get the with and the height of the ball. - bw, bh = ball.get_surface().get_size() - # define the x and y values. - x, y = ball.get_x(), ball.get_y() - # define gravitation. - g = 9.81 - vx, vy = 0.1 * (ball.get_initial_x() - ball.get_x()), 0.1 * (ball.get_initial_y() - ball.get_y()) - # define our vector - v = Vector(vx, vy) - # define our reference time. - tr = time() - # update x and y position. - while True: - if self.__t.stopped(): - break - ts = time() - tr - if ts >= delta_time: - v.set_y(v.get_y() + g * delta_time) - # update the ball's current coordinates. - x += v.get_x() * delta_time * 60 - y += v.get_y() * delta_time * 60 - # display the ball. - ball.set_x(x) - ball.set_y(y) - if y + bh >= h: - # calculate alpha. - alpha = atan(v.get_y() / v.get_x()) - # re-calculate alpha. - alpha = -alpha - # update vector. - v.set_x(v.normalize() * cos(alpha) * 0.8) - v.set_y(v.normalize() * sin(alpha) * 0.8) - tr += delta_time \ No newline at end of file +from events.event_listener import EventListener +from utils.stoppable_thread import StoppableThread +from utils.vector import Vector +from math import (atan, cos, sin) +from time import time +import pygame + + +class BallReleaseEventListener(EventListener): + def __init__(self) -> None: + super().__init__() + + def run(self, event, game): + # retrieve the ball. + ball = game.get_window().get_element("ball") + # retrieve the placeholder ball. + placeholder_ball = game.get_window().get_element("placeholder_ball") + # check if the ball is at the same position + if abs(ball.get_x() - ball.get_initial_x()) < 15 and abs(ball.get_y() - ball.get_initial_y()) < 15 or ball.is_released(): + return + ball.set_released(True) + # disable ball. + placeholder_ball.set_visible(False) + # clear dots. + for i in range(20): + game.get_window().remove_element(("dot_", str(i))) + # launch new thread. + self.__t = StoppableThread(target=self.__move_ball, args=(game,)) + # register the thread in order to be able to kill it. + game.register_thread(self.__t) + # start the thread execution. + self.__t.start() + + def __move_ball(self, game): + """ + Makes the ball move. + """ + # retrieve the ball. + ball = game.get_window().get_element("ball") + # define a delta time. + delta_time = 0.01 + # get the width and the height of the window. + w, h = pygame.display.get_surface().get_size() + # get the with and the height of the ball. + bw, bh = ball.get_surface().get_size() + # define the x and y values. + x, y = ball.get_x(), ball.get_y() + # define gravitation. + g = 9.81 + vx, vy = 0.1 * (ball.get_initial_x() - ball.get_x() + ), 0.1 * (ball.get_initial_y() - ball.get_y()) + # define our vector + v = Vector(vx, vy) + # define our reference time. + tr = time() + # update x and y position. + while True: + if self.__t.stopped(): + break + ts = time() - tr + if ts >= delta_time: + v.set_y(v.get_y() + g * delta_time) + # update the ball's current coordinates. + x += v.get_x() * delta_time * 60 + y += v.get_y() * delta_time * 60 + # display the ball. + ball.set_x(x) + ball.set_y(y) + if y + bh >= h: + # calculate alpha. + alpha = atan(v.get_y() / v.get_x()) + # re-calculate alpha. + alpha = -alpha + # update vector. + v.set_x(v.normalize() * cos(alpha) * 0.8) + v.set_y(v.normalize() * sin(alpha) * 0.8) + tr += delta_time diff --git a/drag_event_listener.py b/events/drag_event_listener.py similarity index 83% rename from drag_event_listener.py rename to events/drag_event_listener.py index 5a7e4c7..a9366f7 100644 --- a/drag_event_listener.py +++ b/events/drag_event_listener.py @@ -1,35 +1,37 @@ -from event_listener import EventListener -from element import Element -import pygame - -class DragEventListener(EventListener): - def __init__(self) -> None: - super().__init__() - self.__white_dot = pygame.image.load("img/white_dot.png") - self.__white_dot = pygame.transform.scale(self.__white_dot, (20, 20)) - - def run(self, event, game): - # pre-register the dots. - for i in range(0, 20): - game.get_window().register_element(("dot_",str(i)), Element(self.__white_dot, 0, 0, False, False)) - # retrieve the ball. - ball = game.get_window().get_element("ball") - if pygame.mouse.get_pressed()[0] == True and not ball.is_released(): - # update x and y position. - ball.set_x(pygame.mouse.get_pos()[0] - 30) - ball.set_y(pygame.mouse.get_pos()[1] - 30) - # get the initial x and y value. - ix, iy = ball.get_initial_x(), ball.get_initial_y() - vx, vy = 1.1 * (ix - ball.get_x()), 1.1 * (iy - ball.get_y()) - for t in range(20): - # get the inital vector values. - x = (ball.get_x() + 30 + vx * t) - y = (9.81 * t**2 + vy * t + ball.get_y() + 30) - if t != 0: - # retrieve the dot. - dot = game.get_window().get_element(("dot_",str(t))) - # update x and y coordinates. - dot.set_x(x) - dot.set_y(y) - # set the dot visible. - dot.set_visible(True) \ No newline at end of file +from events.event_listener import EventListener +from element import Element +import pygame + + +class DragEventListener(EventListener): + def __init__(self) -> None: + super().__init__() + self.__white_dot = pygame.image.load("img/white_dot.png") + self.__white_dot = pygame.transform.scale(self.__white_dot, (20, 20)) + + def run(self, event, game): + # pre-register the dots. + for i in range(0, 20): + game.get_window().register_element(("dot_", str(i)), + Element(self.__white_dot, 0, 0, False, False)) + # retrieve the ball. + ball = game.get_window().get_element("ball") + if pygame.mouse.get_pressed()[0] == True and not ball.is_released(): + # update x and y position. + ball.set_x(pygame.mouse.get_pos()[0] - 30) + ball.set_y(pygame.mouse.get_pos()[1] - 30) + # get the initial x and y value. + ix, iy = ball.get_initial_x(), ball.get_initial_y() + vx, vy = 1.1 * (ix - ball.get_x()), 1.1 * (iy - ball.get_y()) + for t in range(20): + # get the inital vector values. + x = (ball.get_x() + 30 + vx * t) + y = (9.81 * t**2 + vy * t + ball.get_y() + 30) + if t != 0: + # retrieve the dot. + dot = game.get_window().get_element(("dot_", str(t))) + # update x and y coordinates. + dot.set_x(x) + dot.set_y(y) + # set the dot visible. + dot.set_visible(True) diff --git a/event_listener.py b/events/event_listener.py similarity index 96% rename from event_listener.py rename to events/event_listener.py index bf9e0b1..0ea1205 100644 --- a/event_listener.py +++ b/events/event_listener.py @@ -1,10 +1,10 @@ -class EventListener(): - def __init__(self) -> None: - pass - def run(self, event, game): - """ - Oriented Object Observer - :param Event event: the triggered event. - :param Game game: the game instance. - """ +class EventListener(): + def __init__(self) -> None: + pass + def run(self, event, game): + """ + Oriented Object Observer + :param Event event: the triggered event. + :param Game game: the game instance. + """ pass \ No newline at end of file diff --git a/game.py b/game.py index 5a51469..2f20cbe 100644 --- a/game.py +++ b/game.py @@ -1,17 +1,18 @@ import pygame import sys -from player import Player +from components.player import Player from element import Element -from window import Window -from ball import Ball +from interface.window import Window +from components.ball import Ball + +from events.event_listener import EventListener +from events.drag_event_listener import DragEventListener +from events.ball_release_event_listener import BallReleaseEventListener -from event_listener import EventListener -from drag_event_listener import DragEventListener -from ball_release_event_listener import BallReleaseEventListener class Game(): - def __init__(self, screen:pygame.Surface, img_location:str, img_name: str, sound_location:str) -> None: + def __init__(self, screen: pygame.Surface, img_location: str, img_name: str, sound_location: str) -> None: # get the actual display screen. self.__screen = screen # set up playing players. @@ -23,27 +24,34 @@ def __init__(self, screen:pygame.Surface, img_location:str, img_name: str, sound # game threads. self.__threads = [] # get the actual basket ball field. - field = pygame.image.load(img_location + "terrain_basket_sans_public.png") + field = pygame.image.load( + img_location + "terrain_basket_sans_public.png") field = pygame.transform.scale(field, (1024, 640)) # get the actual ball. ball = pygame.image.load(img_location + img_name+".png") # get the placeholder ball. - placeholder_ball = pygame.image.load(img_location + img_name+"-placeholder.png") + placeholder_ball = pygame.image.load( + img_location + img_name+"-placeholder.png") # register the field without public. self.get_window().register_element("field", Element(field, 0, 0)) # register the ball. self.get_window().register_element("ball", Ball(ball, 170, 450)) # register the placeholder ball. - self.get_window().register_element("placeholder_ball", Element(placeholder_ball, 170, 450)) + self.get_window().register_element("placeholder_ball", + Element(placeholder_ball, 170, 450)) # listen to events. - self.listen(pygame.MOUSEMOTION, DragEventListener()) - self.listen(pygame.MOUSEBUTTONUP, BallReleaseEventListener()) + self.listen(pygame.MOUSEMOTION, + DragEventListener()) + self.listen(pygame.MOUSEBUTTONUP, + BallReleaseEventListener()) + def get_window(self) -> Window: """ :return: the game's window. """ return self.__window - def register_player(self, player_name:str) -> None: + + def register_player(self, player_name: str) -> None: """ Register a player by its name. :param: str player_name: the player's name. @@ -54,11 +62,13 @@ def register_player(self, player_name:str) -> None: player.setup() # register the player. self.__players.append(player) + def register_thread(self, thread) -> None: """ Register the given thread into a list. """ self.__threads.append(thread) + def setup(self): """ Setup the ressources (background image, audio, etc.) @@ -85,7 +95,8 @@ def setup(self): # run the event. event_pair[1].run(event, self) pygame.display.update() - def listen(self, event_type:int, event_listener:EventListener) -> None: + + def listen(self, event_type: int, event_listener: EventListener) -> None: """ Takes in parameter an event to call and an event type to listen to. Bind in pair the event type and the event to call. @@ -93,11 +104,12 @@ def listen(self, event_type:int, event_listener:EventListener) -> None: :param EventListener event_listener: the event listener that will be called. """ self.__events.append([event_type, event_listener]) + def end(self): pass + def get_screen(self): """ Return game's screen. """ return self.__screen - diff --git a/window.py b/interface/window.py similarity index 100% rename from window.py rename to interface/window.py diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stoppable_thread.py b/utils/stoppable_thread.py similarity index 96% rename from stoppable_thread.py rename to utils/stoppable_thread.py index 8969345..08989f7 100644 --- a/stoppable_thread.py +++ b/utils/stoppable_thread.py @@ -1,36 +1,36 @@ -from threading import (Thread, Event) - -class StoppableThread(Thread): - """Thread class with a stop() method. The thread itself has to check - regularly for the stopped() condition.""" - - def __init__(self, *args, **kwargs): - super(StoppableThread, self).__init__(*args, **kwargs) - self._stop_event = Event() - self.__return_value = None - - def stop(self): - """ - Mark the thread as stopping. - """ - self._stop_event.set() - - def set_return_value(self, return_value) -> None: - """ - For in thread use only. - :param: object return_value: the value to return at the end of the thread execution. - """ - self.__return_value = return_value - - def get_return_value(self) -> object: - """ - :return: the value returned by the thread. - """ - return self.__return_value - - def stopped(self) -> bool: - """ - For in thread use only. - :return: whether the thread is marked as stopped or not. - """ +from threading import (Thread, Event) + +class StoppableThread(Thread): + """Thread class with a stop() method. The thread itself has to check + regularly for the stopped() condition.""" + + def __init__(self, *args, **kwargs): + super(StoppableThread, self).__init__(*args, **kwargs) + self._stop_event = Event() + self.__return_value = None + + def stop(self): + """ + Mark the thread as stopping. + """ + self._stop_event.set() + + def set_return_value(self, return_value) -> None: + """ + For in thread use only. + :param: object return_value: the value to return at the end of the thread execution. + """ + self.__return_value = return_value + + def get_return_value(self) -> object: + """ + :return: the value returned by the thread. + """ + return self.__return_value + + def stopped(self) -> bool: + """ + For in thread use only. + :return: whether the thread is marked as stopped or not. + """ return self._stop_event.is_set() \ No newline at end of file diff --git a/vector.py b/utils/vector.py similarity index 96% rename from vector.py rename to utils/vector.py index 0d4988d..8a3a623 100644 --- a/vector.py +++ b/utils/vector.py @@ -1,34 +1,34 @@ -from math import sqrt - -class Vector: - def __init__(self, x:int, y:int) -> None: - self.__x = x - self.__y = y - pass - def get_x(self) -> int: - """ - :return: the x coordinate of the vector. - """ - return self.__x - def get_y(self) -> int: - """ - :return: the y coordinate of the vector. - """ - return self.__y - def set_x(self, x:int) -> None: - """ - Define vector's x coordinate. - :param: int x: the x coordinate to update. - """ - self.__x = x - def set_y(self, y:int) -> None: - """ - Define vector's y coordinate. - :param: int y: the y coordinate to update. - """ - self.__y = y - def normalize(self) -> int: - """ - :return: the normalized vector. - """ +from math import sqrt + +class Vector: + def __init__(self, x:int, y:int) -> None: + self.__x = x + self.__y = y + pass + def get_x(self) -> int: + """ + :return: the x coordinate of the vector. + """ + return self.__x + def get_y(self) -> int: + """ + :return: the y coordinate of the vector. + """ + return self.__y + def set_x(self, x:int) -> None: + """ + Define vector's x coordinate. + :param: int x: the x coordinate to update. + """ + self.__x = x + def set_y(self, y:int) -> None: + """ + Define vector's y coordinate. + :param: int y: the y coordinate to update. + """ + self.__y = y + def normalize(self) -> int: + """ + :return: the normalized vector. + """ return sqrt(self.__x**2 + self.__y**2) \ No newline at end of file