diff --git a/lab5/gun.py b/lab5/gun.py new file mode 100644 index 0000000..b72ded6 --- /dev/null +++ b/lab5/gun.py @@ -0,0 +1,244 @@ +import math +from random import choice, randrange as rnd + +import pygame +import typing + + +FPS = 30 + +RED = 0xFF0000 +BLUE = 0x0000FF +YELLOW = 0xFFC91F +GREEN = 0x00FF00 +MAGENTA = 0xFF03B8 +CYAN = 0x00FFCC +BLACK = (0, 0, 0) +WHITE = 0xFFFFFF +GREY = 0x7D7D7D +GAME_COLORS = [RED, BLUE, YELLOW, GREEN, MAGENTA, CYAN] + +WIDTH = 800 +HEIGHT = 600 +global balls, bullet +balls, bullet = [], 0 + + +class Ball: + def __init__(self, screen: pygame.Surface, x: int = 40, y: int = 450): + """ Конструктор класса ball + + Args: + x - начальное положение мяча по горизонтали + y - начальное положение мяча по вертикали + """ + self.screen = screen + self.x = x + self.y = y + self.r = 10 + self.vx = 0 + self.vy = 0 + self.color = choice(GAME_COLORS) + self.live = 30 + + def move(self): + """Переместить мяч по прошествии единицы времени. + + Метод описывает перемещение мяча за один кадр перерисовки. То есть, обновляет значения + self.x и self.y с учетом скоростей self.vx и self.vy, силы гравитации, действующей на мяч, + и стен по краям окна (размер окна 800х600). + """ + if self.y <= 500: + self.vy -= 1.2 + self.y -= self.vy + self.x += self.vx + self.vx *= 0.99 + else: + if self.vx ** 2 + self.vy ** 2 > 10: + self.vy =- self.vy / 2 + self.vx = self.vx / 2 + self.y = 499 + if self.live < 0: + balls.pop(balls.index(self)) + else: + self.live -= 1 + if self.x > 780: + self.vx =- self.vx / 2 + self.x = 779 + + + def draw(self): + pygame.draw.circle( + self.screen, + self.color, + (self.x, self.y), + self.r + ) + + def hittest(self, obj): + """Функция проверяет сталкивалкивается ли данный обьект с целью, описываемой в обьекте obj. + + Args: + obj: Обьект, с которым проверяется столкновение. + Returns: + Возвращает True в случае столкновения мяча и цели. В противном случае возвращает False. + """ + if abs(obj.x - self.x) <= (self.r + obj.r) and abs(obj.y - self.y) <= (self.r + obj.r): + return True + else: + return False + + +class Gun: + def __init__(self, screen): + global bullet + bullet = 0 + self.screen = screen + self.f2_power = 10 + self.f2_on = 0 + self.an = 1 + self.color = GREY + self.x0 = 40 + self.y0 = 450 + + def fire2_start(self, event): + self.f2_on = 1 + + def fire2_end(self, event): + """Выстрел мячом. + + Происходит при отпускании кнопки мыши. + Начальные значения компонент скорости мяча vx и vy зависят от положения мыши. + """ + global bullet + bullet += 1 + new_ball = Ball(self.screen, x=self.x0 + self.deltas()[0], y=self.y0 + self.deltas()[1]) + new_ball.r += 5 + self.an = math.atan2((event.pos[1] - new_ball.y), (event.pos[0] - new_ball.x)) + new_ball.vx = self.f2_power * math.cos(self.an) + new_ball.vy = - self.f2_power * math.sin(self.an) + balls.append(new_ball) + self.f2_on = 0 + self.f2_power = 10 + + def targetting(self, event): + """Прицеливание. Зависит от положения мыши.""" + if event and event.pos[0] != 20: + self.an = math.atan((event.pos[1] - 450) / (event.pos[0] - 20)) + if self.f2_on: + self.color = RED + else: + self.color = GREY + + def deltas(self) -> typing.Union[float, float]: + length = 50 + self.f2_power + return length * math.cos(self.an), length * math.sin(self.an) + + def draw(self): + r, length = 10, 50 + self.f2_power + dx, dy = r * math.sin(self.an), -r * math.cos(self.an) + delta_x, delta_y = self.deltas() + points = [ + (self.x0 + dx, self.y0 + dy), (self.x0 - dx, self.y0 - dy), + (self.x0 + delta_x - dx, self.y0 + delta_y - dy), (self.x0 + delta_x + dx, self.y0 + delta_y + dy)] + pygame.draw.polygon(self.screen, self.color, points) + + def power_up(self): + if self.f2_on: + if self.f2_power < 100: + self.f2_power += 1 + self.color = RED + else: + self.color = GREY + + +class Target: + def __init__(self, screen): + """Инициализация новой цели. """ + x = self.x = rnd(600, 780) + y = self.y = rnd(300, 550) + r = self.r = rnd(2, 50) + self.vx = rnd(2, 6) + self.vy = rnd(2, 6) + color = self.color = RED + self.screen = screen + self.points = 0 + self.live = 1 + + def hit(self, points=1): + """Попадание шарика в цель.""" + self.points += points + + def draw(self): + pygame.draw.circle( + self.screen, + self.color, + (self.x, self.y), + self.r + ) + + def move(self): + """Переместить мяч по прошествии единицы времени. + + Метод описывает перемещение мяча за один кадр перерисовки. То есть, обновляет значения + self.x и self.y с учетом скоростей self.vx и self.vy, силы гравитации, действующей на мяч, + и стен по краям окна (размер окна 800х600). + """ + if self.y < 0: + self.vy = -abs(self.vy) + if self.y <= 500: + self.y -= self.vy + self.x += self.vx + self.vx *= 0.99 + else: + if self.vx ** 2 + self.vy ** 2 > 10: + self.vy =- self.vy / 2 + self.vx = self.vx / 2 + self.y = 499 + if self.x > 780: + self.vx =- self.vx / 2 + self.x = 779 + + +pygame.init() +screen = pygame.display.set_mode((WIDTH, HEIGHT)) +bullet = 0 +balls = [] + +clock = pygame.time.Clock() +gun = Gun(screen) +targets = [Target(screen), Target(screen)] +finished = False + +while not finished: + screen.fill(WHITE) + gun.draw() + for target in targets: + target.draw() + target.move() + for b in balls: + b.draw() + pygame.display.update() + + clock.tick(FPS) + for event in pygame.event.get(): + if event.type == pygame.QUIT: + finished = True + elif event.type == pygame.MOUSEBUTTONDOWN: + gun.fire2_start(event) + elif event.type == pygame.MOUSEBUTTONUP: + gun.fire2_end(event) + elif event.type == pygame.MOUSEMOTION: + gun.targetting(event) + + for b in balls: + b.move() + for target in targets: + if b.hittest(target) and target.live: + target.live = 0 + target.hit() + targets.remove(target) + targets.append(Target(screen)) + gun.power_up() + +pygame.quit()