From b2c54a83659c56c329c8f5fcd3492bf65d7b1db2 Mon Sep 17 00:00:00 2001 From: ennucore Date: Mon, 11 Oct 2021 12:20:35 +0300 Subject: [PATCH] basics of the game (lab4) --- lab4/main.py | 171 +++++++++++++++++++++++++++++++++++++++ lab4/orbitron-medium.otf | Bin 0 -> 19256 bytes 2 files changed, 171 insertions(+) create mode 100644 lab4/main.py create mode 100644 lab4/orbitron-medium.otf diff --git a/lab4/main.py b/lab4/main.py new file mode 100644 index 0000000..a885203 --- /dev/null +++ b/lab4/main.py @@ -0,0 +1,171 @@ +from __future__ import annotations +import pygame +import sys +from dataclasses import dataclass, field +import time +import numpy as np +import typing +from random import uniform, choice +if typing.TYPE_CHECKING: + from nptyping import NDArray + + +pygame.init() +# width and height of the screen +W, H = 900, 800 +# ball's lifetime (seconds) +DEFAULT_TTL = 3 +# ball's radius (default) +DEFAULT_RADIUS = 20 +# time delta, ms +dt = 10 +# load the font +font = pygame.font.Font('orbitron-medium.otf', 15) +N_BALLS = 18 + +# List of ball types: [(radius, speed, color, points)] +BALL_TYPES = [(25, 0.035, (185, 20, 50), 10), (18, 0.055, (220, 0, 0), 30), (20, 0.05, (60, 0, 160), 20)] + + +@dataclass +class Game: + """ + Class for everything connected to the game + + :param score: user's score + :param balls: the list of balls + :param target_ball_number: the target number of balls on screen at a given time + """ + score: int = 0 + balls: typing.List[Ball] = field(default_factory=lambda: [Ball.new_random() for _ in range(N_BALLS)]) + target_ball_number: int = N_BALLS + + def draw_everything(self, screen: pygame.Surface): + """ + Draw everything (for example, balls) on screen + :param screen: the screen + """ + for ball in self.balls: + ball.draw_the_ball(screen) + + def process_click(self, x: float, y: float): + """ + Process mouse click, check if the user clicked on a ball and process it + :param x: click's x coordinate + :param y: click's y coordinate + """ + for i in range(len(self.balls)): + if self.balls[i].is_point_inside(x, y): + self.score += self.balls[i].points + self.balls.pop(i) + break + + def tick(self, dt: float): + """ + Progress for some time + + dt: time period, ms + """ + if len(self.balls) < self.target_ball_number: + n_balls = round((self.target_ball_number - len(self.balls))) # * (1 - 0.9 ** dt)) + self.balls.extend([Ball.new_random() for _ in range(n_balls)]) + for ball in self.balls: + ball.tick(dt) + + +@dataclass +class Ball: + """ + The ball the user has to catch + + :param points: the number of points the user gets for catching this ball + :param position: ball's position on the screen + :param velocity: ball's velocity, px/ms + :param time_of_death: the time when this ball will be destroyed. Its death is pre-determined, which is probably + very sad and depressing + :param radius: the radius of the ball on screen in pixels + :param color: ball's color + """ + points: int = 10 + position: NDArray[(2,), float] = field(default_factory=lambda: np.array([uniform(0, W), uniform(0, H)])) + velocity: NDArray[(2,), float] = field(default_factory=lambda: np.array([0, 0])) + time_of_death: float = field(default_factory=lambda: time.time() + DEFAULT_TTL) + radius: float = DEFAULT_RADIUS + color: typing.Tuple[int, int, int] = (255, 0, 0) + + @classmethod + def new_random(cls) -> Ball: + radius, speed, color, points = choice(BALL_TYPES) + angle = uniform(0, np.pi) + return cls(points, radius=radius, color=color, velocity=speed * np.array([np.sin(angle), np.cos(angle)])) + + def draw_the_ball(self, screen: pygame.Surface): + """ + Just draw the ball on the screen + :param screen: the screen, obviously + """ + pygame.draw.circle(screen, self.color, self.position.tolist(), self.radius) + text_surface = font.render(str(self.points), False, (0, 0, 0)) + screen.blit(text_surface, ( + round(self.position[0] - text_surface.get_rect().width / 2), + round(self.position[1] - text_surface.get_rect().height / 2) + )) + + def is_point_inside(self, x: float, y: float) -> bool: + """ + Check if a given point is inside this circle (to handle click events) + :param x: point's x coordinate + :param y: point's y coordinate + """ + return (self.position[0] - x) ** 2 + (self.position[1] - y) ** 2 <= self.radius ** 2 + + def tick(self, dt: float): + """ + Progress for time dt (move the ball according to speed) + dt: time in ms + """ + self.position += self.velocity * dt + if self.position[0] - self.radius < 0: + self.position[0] = self.radius + self.velocity[0] *= -1 + if self.position[0] >= W - self.radius: + self.position[0] = W - self.radius + self.velocity[0] *= -1 + if self.position[1] < self.radius: + self.position[1] = self.radius + self.velocity[1] *= -1 + if self.position[1] >= H - self.radius: + self.position[1] = H - self.radius + self.velocity[1] *= -1 + + +# create the display surface object +screen = pygame.display.set_mode((W, H)) + +game = Game() + +# set the window name +pygame.display.set_caption("Capture the ball!") + +# the main loop +while True: + # time delay of dt + pygame.time.delay(dt) + + # iterate over the list of Event objects + # that was returned by pygame.event.get() method. + for event in pygame.event.get(): + if event.type == pygame.QUIT: + pygame.quit() + sys.exit(0) + if event.type == pygame.MOUSEBUTTONUP: + game.process_click(*pygame.mouse.get_pos()) + + # clear the screen + screen.fill((40, 46, 70)) + game.tick(dt) + game.draw_everything(screen) + + pygame.display.update() + + diff --git a/lab4/orbitron-medium.otf b/lab4/orbitron-medium.otf new file mode 100644 index 0000000000000000000000000000000000000000..c16ae86d15b899d6fbd00a35e8ba02ad62c1ad53 GIT binary patch literal 19256 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 Uo+;*1eNQ?P?*#iXv|;T30iE@oF#rGn literal 0 HcmV?d00001