Browse Source

Add different balls and targets

master
Lev 3 years ago
parent
commit
ea1a6ce2a3
  1. 262
      lab5/gun.py

262
lab5/gun.py

@ -1,9 +1,12 @@
from __future__ import annotations
import math
from random import choice, randrange as rnd
from copy import deepcopy
from random import choice, uniform, randrange as rnd
from dataclasses import dataclass, field
import pygame
import typing
import numpy as np
FPS = 30
@ -20,26 +23,16 @@ GAME_COLORS = [RED, BLUE, YELLOW, GREEN, MAGENTA, CYAN]
WIDTH = 800
HEIGHT = 600
global balls, bullet
balls, bullet = [], 0
@dataclass
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
position: np.array
r: float = 10
v: np.array = field(default_factory=lambda: np.array([0., 0.]))
color: int = field(default_factory=lambda: choice(GAME_COLORS))
time_to_split: typing.Optional[int] = field(default_factory=lambda: choice([None, rnd(5, 25)]))
live: int = 30
def move(self):
"""Переместить мяч по прошествии единицы времени.
@ -48,52 +41,58 @@ class Ball:
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))
if self.time_to_split is not None:
self.time_to_split -= 1
if self.position[1] <= 500:
self.v[1] -= 1.2
self.position += self.v * np.array([1, -1])
self.v[0] *= 0.99
else:
if np.linalg.norm(self.v) > 10 ** 0.5:
self.v *= np.array([0.5, -0.5])
self.position[1] = 499
self.live -= 1
if self.x > 780:
self.vx =- self.vx / 2
self.x = 779
if self.position[0] > 780:
self.v[0] *= -0.5
self.position[0] = 779
def draw(self):
def draw(self, screen):
pygame.draw.circle(
self.screen,
screen,
self.color,
(self.x, self.y),
self.position,
self.r
)
def hittest(self, obj):
"""Функция проверяет сталкивалкивается ли данный обьект с целью, описываемой в обьекте obj.
"""Функция проверяет, сталкивается ли данный объект с целью, описываемой в объекте obj.
Args:
obj: Обьект, с которым проверяется столкновение.
obj: Объект, с которым проверяется столкновение.
Returns:
Возвращает True в случае столкновения мяча и цели. В противном случае возвращает False.
"""
if abs(obj.x - self.x) <= (self.r + obj.r) and abs(obj.y - self.y) <= (self.r + obj.r):
if np.linalg.norm(self.position - obj.position) < self.r + obj.r:
return True
else:
return False
def get_split(self) -> typing.Tuple[Ball, Ball]:
ball1, ball2 = deepcopy(self), deepcopy(self)
ball1.r /= 2 ** 0.5
ball2.r /= 2 ** 0.5
ball1.time_to_split = choice([None, rnd(5, 25)])
ball2.time_to_split = choice([None, rnd(5, 25)])
v_angle = np.arctan2(*self.v)
delta_angle = 0.5
ball1.v = np.linalg.norm(self.v) * np.array([np.sin(v_angle - delta_angle), np.cos(v_angle - delta_angle)])
ball2.v = np.linalg.norm(self.v) * np.array([np.sin(v_angle + delta_angle), np.cos(v_angle + delta_angle)])
return ball1, ball2
class Gun:
def __init__(self, screen):
global bullet
bullet = 0
self.screen = screen
def __init__(self):
self.bullet = 0
self.f2_power = 10
self.f2_on = 0
self.an = 1
@ -101,23 +100,22 @@ class Gun:
self.x0 = 40
self.y0 = 450
def fire2_start(self, event):
def fire2_start(self, _event):
self.f2_on = 1
def fire2_end(self, event):
def fire2_end(self, event, game: Game):
"""Выстрел мячом.
Происходит при отпускании кнопки мыши.
Начальные значения компонент скорости мяча vx и vy зависят от положения мыши.
"""
global bullet
bullet += 1
new_ball = Ball(self.screen, x=self.x0 + self.deltas()[0], y=self.y0 + self.deltas()[1])
self.bullet += 1
new_ball = Ball(position=np.array([self.x0 + self.deltas()[0], 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.an = math.atan2((event.pos[1] - new_ball.position[1]), (event.pos[0] - new_ball.position[0]))
new_ball.v[0] = self.f2_power * math.cos(self.an)
new_ball.v[1] = - self.f2_power * math.sin(self.an)
game.balls.append(new_ball)
self.f2_on = 0
self.f2_power = 10
@ -130,18 +128,18 @@ class Gun:
else:
self.color = GREY
def deltas(self) -> typing.Union[float, float]:
def deltas(self) -> typing.Tuple[float, float]:
length = 50 + self.f2_power
return length * math.cos(self.an), length * math.sin(self.an)
def draw(self):
def draw(self, screen):
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)
pygame.draw.polygon(screen, self.color, points)
def power_up(self):
if self.f2_on:
@ -152,30 +150,46 @@ class Gun:
self.color = GREY
@dataclass
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
x: float = field(default_factory=lambda: rnd(600, 780))
y: float = field(default_factory=lambda: rnd(300, 550))
r: float = field(default_factory=lambda: rnd(9, 50))
vx: float = field(default_factory=lambda: rnd(-4, 6))
vy: float = field(default_factory=lambda: rnd(-4, 6))
color: int = RED
points: int = 0
live: int = 1
ax: float = 0
ay: float = 0
randomness_ampl: float = field(default_factory=lambda: uniform(0, 5) * choice([0, 0, 1]))
oscillation_freq: float = field(default_factory=lambda: 0.05 * choice([-1, 1]))
oscillation_ampl: float = field(default_factory=lambda: rnd(0, 6) * choice([0, 0, 1]))
oscillation_phase: float = field(default_factory=lambda: uniform(0, 2 * np.pi))
def hit(self, points=1):
"""Попадание шарика в цель."""
self.points += points
def draw(self):
def draw(self, screen):
pygame.draw.circle(
self.screen,
screen,
self.color,
(self.x, self.y),
self.r
)
pygame.draw.circle(
screen,
WHITE,
(self.x, self.y),
self.r * 0.7
)
pygame.draw.circle(
screen,
self.color if not self.oscillation_ampl and not self.randomness_ampl else BLUE,
(self.x, self.y),
self.r * 0.45
)
def move(self):
"""Переместить мяч по прошествии единицы времени.
@ -185,60 +199,90 @@ class Target:
и стен по краям окна (размер окна 800х600).
"""
if self.y < 0:
self.vy = -abs(self.vy)
self.vy = -abs(self.vy) * 0.5 + self.ay
if self.x < 0:
self.vx = abs(self.vx) * 0.75 + self.ax
if self.y <= 500:
self.y -= self.vy
self.x += self.vx
self.vx *= 0.99
self.y -= self.vy + self.oscillation_ampl * np.sin(self.oscillation_phase)
self.x += self.vx + self.oscillation_ampl * np.cos(self.oscillation_phase)
self.vx *= 0.98
else:
if self.vx ** 2 + self.vy ** 2 > 10:
self.vy =- self.vy / 2
self.vx = self.vx / 2
# if self.vx ** 2 + self.vy ** 2 > 10:
# self.vy = -self.vy / 2
# self.vx = self.vx / 2
self.vy = 0.9 * abs(self.vy)
self.y = 499
if self.x > 780:
self.vx =- self.vx / 2
self.vx = -abs(self.vx) / 2
self.x = 779
self.oscillation_phase += self.oscillation_freq
self.ax += self.randomness_ampl * uniform(-1, 1)
self.ay += self.randomness_ampl * uniform(-1, 1)
@property
def position(self):
return np.array([self.x, self.y])
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
@dataclass
class Game:
balls: typing.List[Ball] = field(default_factory=list)
targets: typing.List[Target] = field(default_factory=lambda: [Target(), Target()])
gun: Gun = field(default_factory=Gun)
finished: bool = False
clock: pygame.time.Clock = field(default_factory=pygame.time.Clock)
fps: int = FPS
while not finished:
screen.fill(WHITE)
gun.draw()
for target in targets:
target.draw()
def move(self):
for target in self.targets:
target.move()
for b in balls:
b.draw()
for ball in self.balls:
ball.move()
if ball.live < 0:
self.balls.pop([i for i in range(len(self.balls)) if self.balls[i].position is ball.position][0])
elif ball.time_to_split is not None and ball.time_to_split < 0:
ball1, ball2 = ball.get_split()
self.balls.pop([i for i in range(len(self.balls)) if self.balls[i].position is ball.position][0])
self.balls.extend([ball1, ball2])
for target in self.targets:
if ball.hittest(target) and target.live:
target.live = 0
ball.live = -1
target.hit()
self.targets.remove(target)
self.targets.append(Target())
self.gun.power_up()
def draw(self, screen):
screen.fill(WHITE)
self.gun.draw(screen)
for target in self.targets:
target.draw(screen)
for ball in self.balls:
ball.draw(screen)
pygame.display.update()
clock.tick(FPS)
for event in pygame.event.get():
def process_event(self, event):
if event.type == pygame.QUIT:
finished = True
self.finished = True
elif event.type == pygame.MOUSEBUTTONDOWN:
gun.fire2_start(event)
self.gun.fire2_start(event)
elif event.type == pygame.MOUSEBUTTONUP:
gun.fire2_end(event)
self.gun.fire2_end(event, self)
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()
self.gun.targetting(event)
def main_loop(self):
screen = pygame.display.set_mode((WIDTH, HEIGHT))
while not self.finished:
self.draw(screen)
self.clock.tick(self.fps)
for event in pygame.event.get():
self.process_event(event)
self.move()
pygame.quit()
pygame.init()
game = Game()
game.main_loop()

Loading…
Cancel
Save