1. import pygame, time, random
  2.  
  3. # Для работы со шрифтами:
  4. pygame.font.init()
  5.  
  6. # Чтобы проигрывать музыку:
  7. pygame.mixer.init()
  8.  
  9. # Параметры окна
  10. WIDTH, HEIGHT = 1024, 768
  11. WIN = pygame.display.set_mode((WIDTH, HEIGHT))
  12. pygame.display.set_caption("Вторжение пришельцев")
  13.  
  14. # Корабль игрока
  15. PLAYER_SHIP = pygame.image.load("img/ship.png")
  16.  
  17. # Пули
  18. LASER = pygame.image.load("img/pixel_laser.png")
  19.  
  20. # Задний фон
  21. BG = pygame.transform.scale(pygame.image.load("img/bg.jpg"), (WIDTH, HEIGHT))
  22.  
  23. class Laser:
  24.     # Создаём пулю и задаём её логику
  25.     def __init__(self, x, y, img):
  26.         self.x = x-15
  27.         self.y = y-20
  28.         self.img = img
  29.         self.mask = pygame.mask.from_surface(self.img)
  30.  
  31.     def draw(self, window):
  32.         window.blit(self.img, (self.x, self.y))
  33.  
  34.     def move(self, vel):
  35.         self.y += vel
  36.  
  37.     def off_screen(self, height):
  38.         return not(self.y <= height and self.y >= 0)
  39.  
  40.     def collision(self, obj):
  41.         return collide(self, obj)
  42.  
  43. class Ship:
  44.     # Скорость стрельбы
  45.     COOLDOWN = 20
  46.  
  47.     # Настройки корабля
  48.     def __init__(self, x, y, health=100):
  49.         self.x = x
  50.         self.y = y
  51.         self.health = health
  52.         self.ship_img = None
  53.         self.laser_img = None
  54.         self.lasers = []
  55.         self.cool_down_counter = 0
  56.  
  57.     def draw(self, window):
  58.         window.blit(self.ship_img, (self.x, self.y))
  59.         for laser in self.lasers:
  60.             laser.draw(window)
  61.  
  62.     def move_lasers(self, vel, obj):
  63.         self.cooldown()
  64.         for laser in self.lasers:
  65.             laser.move(vel)
  66.             if laser.off_screen(HEIGHT):
  67.                 self.lasers.remove(laser)
  68.             elif laser.collision(obj):
  69.                 obj.health -= 10
  70.                 self.lasers.remove(laser)
  71.  
  72.     def cooldown(self):
  73.         if self.cool_down_counter >= self.COOLDOWN:
  74.             self.cool_down_counter = 0
  75.         elif self.cool_down_counter > 0:
  76.             self.cool_down_counter += 1
  77.  
  78.     def shoot(self):
  79.         if self.cool_down_counter == 0:
  80.             laser = Laser(self.x, self.y, self.laser_img)
  81.             self.lasers.append(laser)
  82.             self.cool_down_counter = 1
  83.  
  84.     def get_width(self):
  85.         return self.ship_img.get_width()
  86.  
  87.     def get_height(self):
  88.         return self.ship_img.get_height()
  89.  
  90. class Player(Ship):
  91.     # Создаём корабль игрока
  92.     def __init__(self, x, y, health=100):
  93.         super().__init__(x, y, health)
  94.         self.ship_img = PLAYER_SHIP
  95.         self.laser_img = LASER
  96.         self.mask = pygame.mask.from_surface(self.ship_img)
  97.         self.max_health = health
  98.  
  99.     def move_lasers(self, vel, objs):
  100.         self.cooldown()
  101.         for laser in self.lasers:
  102.             laser.move(vel)
  103.             if laser.off_screen(HEIGHT):
  104.                 self.lasers.remove(laser)
  105.             else:
  106.                 for obj in objs:
  107.                     if laser.collision(obj):
  108.                         objs.remove(obj)
  109.                         if laser in self.lasers:
  110.                             self.lasers.remove(laser)
  111.  
  112.     def draw(self, window):
  113.         super().draw(window)
  114.         self.healthbar(window)
  115.  
  116.     def healthbar(self, window):
  117.         pygame.draw.rect(window, (255,0,0), (self.x, self.y + self.ship_img.get_height() + 10,
  118.                                              self.ship_img.get_width(), 10))
  119.          
  120.         pygame.draw.rect(window, (0,255,0), (self.x, self.y + self.ship_img.get_height() + 10,
  121.                                              self.ship_img.get_width() * (self.health/self.max_health), 10))
  122. class Enemy(Ship):
  123.     # Пришельцы
  124.     def __init__(self, x, y, health=50):
  125.         super().__init__(x, y, health)
  126.         self.ship_img = pygame.image.load('img/ufo.png')
  127.         self.mask = pygame.mask.from_surface(self.ship_img)
  128.  
  129.     def move(self, vel):
  130.         self.y += vel
  131.  
  132. def collide(obj1, obj2):
  133.     offset_x = obj2.x - obj1.x
  134.     offset_y = obj2.y - obj1.y
  135.     return obj1.mask.overlap(obj2.mask, (offset_x, offset_y)) != None
  136.  
  137. def main():
  138.     run = True
  139.     FPS = 60
  140.     level = 0
  141.     lives = 3
  142.      
  143.     main_font = pygame.font.SysFont("comicsans", 50)
  144.     lost_font = pygame.font.SysFont("comicsans", 60)
  145.     win_font = pygame.font.SysFont("comicsans", 60)
  146.  
  147.     enemies = []
  148.     wave_length = 5
  149.  
  150.     # Задаём скорость пришельцев, пуль, игрока
  151.     enemy_vel = 1
  152.     player_vel = 5
  153.     laser_vel = 5
  154.  
  155.     # Начальная позиция игрока
  156.     player = Player(500, 600)
  157.  
  158.     clock = pygame.time.Clock()
  159.  
  160.     lost = False
  161.     lost_count = 0
  162.  
  163.     win = False
  164.     win_count = 0
  165.  
  166.     def redraw_window():
  167.         WIN.blit(BG, (0,0))
  168.         # Переменные, которые хранят кол-во жизней и номер уровня
  169.         lives_label = main_font.render(f"Жизни: {lives}", 1, (255,255,255))
  170.         level_label = main_font.render(f"Уровень: {level}", 1, (255,255,255))
  171.  
  172.         # Вывод на экран жизней и номер уровня
  173.         WIN.blit(lives_label, (10, 10))
  174.         WIN.blit(level_label, (WIDTH - level_label.get_width() - 10, 10))
  175.  
  176.         for enemy in enemies:
  177.             enemy.draw(WIN)
  178.  
  179.         player.draw(WIN)
  180.  
  181.         if lost:
  182.             lost_label = lost_font.render("Попробуй ещё раз!", 1, (255,255,255))
  183.             WIN.blit(lost_label, (WIDTH/2 - lost_label.get_width()/2, 350))
  184.  
  185.         if win:
  186.             win_label = win_font.render("Хорошая работа!", 1, (255,255,255))
  187.             WIN.blit(win_label, (WIDTH/2 - win_label.get_width()/2, 350))
  188.  
  189.         pygame.display.update()
  190.                       
  191.     while run:
  192.         clock.tick(FPS)
  193.         redraw_window()
  194.      
  195.         if lives <= 0 or player.health <= 0:
  196.             pygame.mixer.music.pause()
  197.             lost = True
  198.             lost_count += 1
  199.  
  200.         if level == 16:
  201.             pygame.mixer.music.pause()
  202.             win = True
  203.             win_count += 1
  204.  
  205.         if lost:
  206.             if lost_count > FPS * 3:
  207.                 run = False
  208.             else:
  209.                 continue
  210.  
  211.         if win:
  212.             if win_count > FPS * 3:
  213.                 run = False
  214.             else:
  215.                 continue
  216.  
  217.         if len(enemies) == 0:
  218.             level += 1
  219.             wave_length += 5
  220.             for i in range(wave_length):
  221.                 enemy = Enemy(random.randrange(50, WIDTH-100), random.randrange(-1500, -100))
  222.                 enemies.append(enemy)
  223.  
  224.         for event in pygame.event.get():
  225.             if event.type == pygame.QUIT:
  226.                 quit()
  227.  
  228.         # Задаём направления, в которых может двигаться игрок
  229.         # и запрещаем кораблю игрока вылетать за границы игрового поля
  230.         keys = pygame.key.get_pressed()
  231.  
  232.         # Движение вверх и запрет на вылет за верхнюю границу экрана
  233.         if keys[pygame.K_w] and player.y - player_vel > 0:
  234.             player.y -= player_vel
  235.  
  236.         # Движение влево и запрет на вылет за левую границу экрана
  237.         if keys[pygame.K_a] and player.x - player_vel > 0:
  238.             player.x -= player_vel
  239.  
  240.         # Движение вниз и запрет на вылет за нижнюю границу экрана
  241.         if keys[pygame.K_s] and player.y + player_vel + player.get_height() + 15 < HEIGHT:
  242.             player.y += player_vel
  243.  
  244.         # Движение вправо и запрет на вылет за правую границу экрана
  245.         if keys[pygame.K_d] and player.x + player_vel + player.get_width() < WIDTH:
  246.             player.x += player_vel
  247.  
  248.         # Теперь корабль будет стрелять при нажании клавиши "Пробел"
  249.         if keys[pygame.K_SPACE]:
  250.             player.shoot()
  251.         # Рестарт игры
  252.         if keys[pygame.K_q]:
  253.             pygame.mixer.music.pause()
  254.             run = False
  255.             break
  256.  
  257.         for enemy in enemies[:]:
  258.             enemy.move(enemy_vel)
  259.  
  260.             # При столкновении пришельца и игрока, здоровье игрока
  261.             # Уменьшается на 10, а пришелец стирается
  262.             if collide(enemy, player):
  263.                 player.health -= 10
  264.                 enemies.remove(enemy)
  265.             elif enemy.y + enemy.get_height() > HEIGHT:
  266.                 lives -= 1
  267.                 enemies.remove(enemy)
  268.  
  269.         player.move_lasers(-laser_vel, enemies)
  270.  
  271. def main_menu():
  272.     title_font = pygame.font.SysFont("comicsans", 70)
  273.     run = True
  274.      
  275.     pygame.mixer.music.unpause()
  276.     pygame.mixer.music.load('sounds/bg.wav')
  277.     pygame.mixer.music.play(-1)
  278.     pygame.mixer.music.set_volume(0.2)
  279.      
  280.     while run:
  281.      
  282.         WIN.blit(BG, (0,0))
  283.         title_label = title_font.render("Нажмите ЛКМ, чтобы начать...", 1, (255,255,255))
  284.         WIN.blit(title_label, (WIDTH/2 - title_label.get_width()/2, 350))
  285.         pygame.display.update()
  286.         for event in pygame.event.get():
  287.             if event.type == pygame.QUIT:
  288.                 run = False
  289.             if event.type == pygame.MOUSEBUTTONDOWN:
  290.                 pygame.mixer.music.unpause()
  291.                 main()
  292.     pygame.quit()
  293.  
  294. main_menu()