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()