You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
7.5 KiB
245 lines
7.5 KiB
3 years ago
|
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()
|