Условие столкновения в pygame (некорректно срабатывает условие)

В игре должно работать условие:

  • Если вражеский корабль достиг нижней границы экрана, то игра завершается, но условие работает некорректно (в функции enemy_chek()).
  • Если сделать проверку на нахождение точки bottom вражеского корабля за пределами экрана, то это условие срабатывает даже тогда, когда этого не случается.

Могу предположить, что это из-за того, что координата y уничтоженных противников продолжает отслеживаться.

вот пример игрового окна

import pygame
import sys
from pygame.sprite import Group
pygame.init()

#параметры экрана
height, width = 800, 650  # высота  # ширина
screen = pygame.display.set_mode((width, height))

# игрок
class Ship:
    def __init__(self, screen):
        self.screen = screen
        self.screen_rect = screen.get_rect()
        self.rect = pygame.Rect(300, 710, 80, 80)
        self.color = (255, 255, 255)

        self.move_left = False
        self.move_right = False

    def move(self):
        if self.move_left == True:
            self.rect.centerx -= 1
        elif self.move_right == True:
            self.rect.centerx += 1
        if self.rect.right >= 650:
            self.rect.right = 650
        elif self.rect.left <= 0:
            self.rect.left = 0

    def output_s(self):
        pygame.draw.rect(self.screen, self.color, self.rect)

#пули
class Bullet(pygame.sprite.Sprite):
    def __init__(self, screen, my_ship):
        super(Bullet, self).__init__()
        self.screen = screen
        self.rect = pygame.Rect(0, 0, 100, 10)
        self.speed = 5
        self.rect.centerx = my_ship.rect.centerx
        self.rect.top = my_ship.rect.top
        self.y = self.rect.y

    def update(self):
        """перемещение пули вверх"""
        self.y -= self.speed
        self.rect.y = self.y

    def draw_bullet(self):
        pygame.draw.rect(self.screen, (255, 255, 255), self.rect)

#враги
class Enemy(pygame.sprite.Sprite):  # класс enemy наследник класса sprite
    def __init__(self, screen):
        super(Enemy, self).__init__()
        self.screen = screen
        self.screen_rect = screen.get_rect()
        self.rect = pygame.Rect(0, 0, 70, 70)
        self.rect.top = self.screen_rect.top
        self.rect.centerx = self.screen_rect.centerx
        self.x = float(self.rect.x)
        self.y = float(self.rect.y)

    def output_e(self):
        pygame.draw.rect(self.screen, (255, 255, 255), self.rect)

    def update(self):
        self.y += 0.1
        self.rect.y = self.y

# объекты классов и группы
my_ship = Ship(screen)
bullets = Group()
enemys = Group()


# создания ряда врагов
def create_enemy(screen, enemys, width=600, current_row_y=25):
    enemy_width = Enemy(screen).rect.width
    e_numbers = (width - 50) // enemy_width
    for i in range(e_numbers):
        enemy = Enemy(screen)
        enemy.x = 25 + (enemy_width + 25) * i
        enemy.rect.x = enemy.x
        enemy.y = current_row_y
        enemy.rect.y = enemy.y
        enemys.add(enemy)

#движение пуль и их столконовение с врагами
def update_bullets(screen, bullets, enemys, width=600):
    bullets.update()
    for bullet in bullets.copy():
        if bullet.rect.bottom <= 0:
            bullets.remove(bullet)
    collision = pygame.sprite.groupcollide(bullets, enemys, True, True)

#отрисовка пуль. игрока и врагов на экране
def screen_update(screen, enemys, bullets):
    screen.fill((0, 0, 0))
    for enemy in enemys:
        enemy.output_e()
    for bullet in bullets:
        bullet.draw_bullet()
    my_ship.output_s()
    my_ship.move()
    pygame.display.flip()

#проигрыш
def gun_kill( screen, enemys, width=600):
    enemys.empty()
    bullets.empty()
    create_enemy(screen, enemys, width)

#неправильно работающая функция
def enemy_check(screen, enemys, width=600):
    # добралась ли армия до конца экрана
    screen_rect = screen.get_rect()
    for enemy in enemys:
        if enemy.rect.bottom >= screen_rect.bottom:
            gun_kill(screen, enemys)
            break

def main():
    create_enemy(screen, enemys)  # Создаем врагов при старте


    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_a:
                    my_ship.move_left = True
                elif event.key == pygame.K_d:
                    my_ship.move_right = True
                # стрельба
                elif event.key == pygame.K_SPACE:

                    new_bullet = Bullet(screen, my_ship)
                    bullets.add(new_bullet)

            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_a:
                    my_ship.move_left = False
                elif event.key == pygame.K_d:
                    my_ship.move_right = False
                    
        #создание рядов, следующих друг за другом
        #возможно, этот способ их создания - причина некорректной работы
        last_enemy = enemys.sprites()[-1]
        if enemys and last_enemy.rect.y >= 100:
            create_enemy(screen, enemys, width, 0)

        enemys.update()
        enemy_check(screen, enemys)
        update_bullets(screen, bullets, enemys, width=600)
        screen_update(screen, enemys, bullets)


if __name__ == "__main__":
    main()


Ответы (1 шт):

Автор решения: S. Nick

Я не обнаружил в приведенном вами примере того что вы пишите, т.е. у меня при достижении нижней границы экрана, начинается новая игра и вверху рисуется новый ряд врагов. Может я чего-то не так понял.

Я вам предложу проверить другой вариант похожей и более продвинутой игры.

Обратите внимание:

  • движение игрока реализовано под левую и под правую руку;
  • в игре реализован подсчет баллов и уровни игры;
  • игра заканчивается при столкновении игрока с врагом;
  • ...

Надеюсь пример будет полезет для вас.

import random
import time
import pygame
import pygame.mixer
from pygame.locals import *


class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
# --------------------------------------> vvvvvvvvvvvvvvv <------- установите свое
        self.images = [pygame.image.load("img/player1.png").convert_alpha(),
                       pygame.image.load("img/player2.png").convert_alpha()]

        self.current_image = 0
        self.speed = 20
# ------------------------------------> vvvvvvvvvvvvvvv <------- установите свое
        self.image = pygame.image.load("img/player1.png").convert_alpha()
        self.image = pygame.transform.scale(self.image, (68, 78))
        self.rect = self.image.get_rect()
        self.rect[0] = 600
        self.rect[1] = 510
        self.points = 0

    def update(self):
        self.animation()

    def animation(self):
        self.current_image = (self.current_image + 1) % 2
        self.image = self.images[self.current_image]
        self.image = pygame.transform.scale(self.image, (68, 78))


class Shot(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
# ------------------------------------> vvvvvvvvvvvv <--------------- установите свое
        self.image = pygame.image.load("img/shot.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.speed = 30

    def update(self):
        self.rect[1] -= self.speed


class Enemy (pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
# -----------------------------------------> vvvvvvvvvvvvvvv <------- установите свое
        self.animation = [pygame.image.load("img/meteor1.png").convert_alpha(),
                          pygame.image.load("img/meteor2.png").convert_alpha(),
                          pygame.image.load("img/meteor3.png").convert_alpha()]
# ------------------------------------> vvvvvvvvvvvvvvv <------------- установите свое
        self.image = pygame.image.load("img/meteor1.png").convert_alpha()
        self.image = pygame.transform.scale(self.image, (68, 78))
        self.rect  = self.image.get_rect()
        self.rect[0] = random.randint(10, 1100)
        self.rect[1] = random.randint(10, 30)
        self.speed = 1
        self.current_image = 0
        self.current_explosion = 0

    def update(self):
        self.current_image = (self.current_image + 1) % 3
        self.image = self.animation[self.current_image]
        self.image = pygame.transform.scale(self.image, (68, 78))
        self.rect[1] += self.speed
        

class Game():
    def __init__(self):
        pygame.init()                                     # инициализировать pygame
        # переменная, которая контролирует, запускается игра или нет
        self.game_start = True 

        self.window_width = 1200                          # ширина окна
        self.window_height = 600                          # высота окна

        # создает окно, передавая ширину и высоту в качестве параметров
        self.window = pygame.display.set_mode((self.window_width, self.window_height)) 
# -----------------------------------------> vvvvvvvvvvvvvvvvvv <------ установите свое
        self.background = pygame.image.load("img/background.jpg") # фоновое изображение
        # определил ширину и высоту фонового изображения.
        self.background = pygame.transform.scale(
            self.background, (self.window_width, self.window_height)) 

        # Игрок
        self.player_group = pygame.sprite.Group()          # создает группу для игрока
        self.player = Player()                             # создать игрока
        self.player_group.add(self.player)                 # добавить игрока в группу
        self.player_right = False          # инициализирует переменную движения FALSE
        self.player_left = False           # инициализирует переменную движения FALSE

        # Выстрелил
        self.shoot_group = pygame.sprite.Group()           # создать группу выстрелов

        # Враг
        self.create_enemy = True
        self.enemy_group = pygame.sprite.Group()

        # !!! ОЧКИ И ИНФОРМАЦИЯ ОБ УРОВНЕ
        self.player_points = self.player.points
# ----------------------------------> vvvvvvvvvvvvv <--------------- установите свое        
        self.font = pygame.font.Font("font/8bit.ttf", 30)
        self.points_text = self.font.render("SCORE: " + str(self.player_points), 
            1, (255,255,255))
        self.level = 0
        self.enemy_in_window = 5
        self.level_text = self.font.render("LEVEL: "+ str(self.level), 
            1, (255, 255, 255))


        # ФОНОВАЯ МУЗЫКА
        pygame.mixer.init()
        pygame.mixer.set_reserved(0)
# ------------------------------------------> vvvvvvvvvvvvvvvvvvvvv <--- установите свое
        self.game_music = pygame.mixer.Sound("sounds/game_music.wav")
        pygame.mixer.Channel(0).play(self.game_music, -1)

        # FPS
        self.fps = pygame.time.Clock() # добавить часы в переменную для управления fps

        # ГЛАВНЫЙ ЦИКЛ
        self.game_init = True
        while self.game_init:
            self.fps.tick(30)                              # установите частоту кадров в игре
            for event in pygame.event.get():               # событие для игровых команд
                if event.type == QUIT:
                    pygame.quit()

                if event.type == KEYDOWN:                  # когда клавиша нажата
                    if event.key == K_d or event.key == K_RIGHT:
                        self.player_right = True
                    if event.key == K_a or event.key == K_LEFT:
                        self.player_left = True
                    if event.key == K_SPACE:               # кнопка для стрельбы
                        self.player_shot = Shot()          # 
                        self.player_shot.rect[0] = self.player.rect[0]+23 # передает начальную позицию по x
                        self.player_shot.rect[1] = self.player.rect[1]    # передает начальную позицию в y
                        self.shoot_group.add(self.player_shot)            # добавить кадр в группу кадров
                        pygame.mixer.Channel(1).play(pygame.mixer.Sound("sounds/shot.wav")) # огнестрельная музыка

                if event.type == KEYUP:                     # когда отпустишь клавишу
                    if event.key == K_d or event.key == K_RIGHT:
                        self.player_right = False
                    if event.key == K_a or event.key == K_LEFT:
                        self.player_left = False

            if self.player_right:             # если условие истинно, игрок движется вправо
                self.player.rect[0] += self.player.speed
            if self.player_left:              # если условие истинно, игрок движется влево
                self.player.rect[0] -= self.player.speed

            self.window.blit(self.background, (0, 0))            # нарисовать фон
            self.window.blit(self.points_text, (850, 10))        # счет
            self.window.blit(self.level_text, (650, 10))
            self.shoot_group.update()  # вызывает функцию обновления для всех в группе снимков
            self.player_group.update() # вызывает функцию обновления для всех в группе игрока
            self.player_group.draw(self.window) # рисует всю группу игроков на экране
            self.enemy_group.update()          # Вызов обновления группы противника
            self.enemy_group.draw(self.window) # рисовать врагов на экране

            if len(self.enemy_group) < 5: # Если у вас меньше 5 врагов, добавьте больше врагов.
                for i in range(5):
                    self.enemy = Enemy()
                    self.enemy_group.add(self.enemy)
                    print("Добавил еще одного врага")
# !!!
            if self.enemy.rect[1] > 600: # Когда враги достигают 700 пикселей экрана, они уничтожаются.
                self.enemy_group.remove(self.enemy)
                print("\nВраг покинул экран\n")
# !!!
            # ПРОВЕРКА СЧЕТА И УРОВНЯ
            if self.player_points > 500:
                self.enemy.speed  = 2
                self.level = 1
                self.level_text = self.font.render(
                    "LEVEL: "+ str(self.level), 1, (255, 255, 255))
            if self.player_points > 2000:
                self.enemy.speed  = 3
                self.level = 2
                self.level_text = self.font.render("LEVEL: "+ str(self.level),1,(255,255,255))
            if self.player_points > 4000:
                self.enemy.speed  = 4
                self.level = 3
                self.level_text = self.font.render("LEVEL: "+ str(self.level),1,(255,255,255))
            if self.player_points > 8000:
                self.enemy.speed  = 6
                self.level = 4
                self.level_text = self.font.render("LEVEL: "+ str(self.level),1,(255,255,255))
            if self.player_points > 10000:
                self.enemy.speed  = 8
                self.level = 5
                self.level_text = self.font.render("LEVEL: "+ str(self.level),1,(255,255,255))
            if self.player_points > 20000:
                self.enemy.speed  = 9
                self.level = 6
                self.level_text = self.font.render("LEVEL: "+ str(self.level),1,(255,255,255))
            if self.player_points > 50000:
                self.enemy.speed  = 12
                self.level = "FINAL LEVEL"
                self.level_text = self.font.render("LEVEL: "+ str(self.level),1,(255,255,255))

            for bullet in self.shoot_group: # для каждого кадра, находящегося в группе выстрелов
                self.shoot_group.draw(self.window)  # нарисуй кадр на экране
                if self.player_shot.rect[1] < -20:  # проверяет, меньше ли y-позиция кадра - 20 и в этом случае он уже покинул экран
                    self.shoot_group.remove(self.player_shot) # если проверка верна, то этот выстрел исключается из группы снимков
# !!!
            # ПРОВЕРКА СТОЛКНОВЕНИЙ
            if (pygame.sprite.groupcollide(self.shoot_group, self.enemy_group, True, True)):
                self.player_points += random.randint(1, 10)
                self.points_text = self.font.render("SCORE: " + str(self.player_points), 1, (255,255,255))
# ---  установите свое  ---------------------------------------> vvvvvvvvvvvvvvvvvvvvv <--- 
                pygame.mixer.Channel(2).play(pygame.mixer.Sound("sounds/enemy_death.wav")) # музыка смерти врага

            if (pygame.sprite.groupcollide(self.player_group, self.enemy_group, True, False)):
                    print(f'\nСтолкновение, новая игра \n') # 
                    Game()
                    self.game_init = False
            pygame.display.update()
            
Game()

введите сюда описание изображения


background.jpg

введите сюда описание изображения

shot.png

введите сюда описание изображения

player1.png

введите сюда описание изображения

meteor1.png

введите сюда описание изображения

→ Ссылка