WedX - журнал о программировании и компьютерных науках

Как сделать вывод переменной «True», когда два изображения сталкиваются в pygame

В настоящее время я делаю гоночную игру сверху вниз, и мне нужен способ определить, когда автомобиль завершил полный круг. Я решил сделать это, добавив изображения вокруг трассы, действующие как контрольные точки, которые совпадают с поверхностью трассы. Когда их обгоняют, они выводят true, все должны выводить true, чтобы круг засчитывался. Однако я не могу найти способ обнаружить столкновение между моими автомобилями и изображением.

Я попытался добавить прямоугольники к транспортным средствам и проверить, может ли быть получен вывод при столкновении двух транспортных средств, но я просто получаю эту ошибку:

AttributeError: 'pygame.Surface' object has no attribute 'rect'

Есть ли способ сделать это? Мой код можно увидеть ниже.

import pygame
from pygame.locals import *
import math
import time

pygame.init()
F1image = pygame.image.load("F1image.png")
sportsimage = pygame.image.load("sportsimage.png")
bikeimage = pygame.image.load("bikeimage.png")
muscleimage = pygame.image.load("muscleimage.png")
truckimage = pygame.image.load("truckimage.png")
screen = pygame.display.set_mode((1280,720))
xpos = 280
xpos_2 = 280
ypos = 50
ypos_2 = 85
keys = [False, False, False, False]
keys_2 = [False, False, False, False]
direction = 0
direction_2 = 0
forward = 0
forward_2 = 0

class Background(pygame.sprite.Sprite):
    def __init__(self, image_file, location):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(image_file)
        self.rect = self.image.get_rect()
        self.rect.left, self.rect.top = location

BackGround = Background('track.png', [0,0])

class Vehicle:
    'Base class for all vehicles (Cars and Motorbikes) in the game'
    vehicleCount = 0

    def __init__(self, max_speed, acceleration, turning_radius, image):
        pygame.sprite.Sprite.__init__(self)
        self.max_speed = max_speed
        self.acceleration = acceleration
        self.turning_radius = turning_radius
        self.image = image
        self.rect = self.image.get_rect()
        Vehicle.vehicleCount  = Vehicle.vehicleCount + 1


    def displayAmount():
        print ("Total number of Vehicle enteries: ", Vehicle.vehicleCount)

    def displayVehicle(self):
        print ("max speed: ", self.max_speed, "acceleration: ", self.acceleration, "turning radius: ", self.turning_radius)

    def checkCollision(self, sprite1, sprite2):
        col = pygame.sprite.collide_rect(sprite1, sprite2)
        if col == True:
            print ("True")

F1 = Vehicle(5.0, 0.1, 2.84, F1image)
sportscar = Vehicle(4.5, 0.2, 2.01, sportsimage)
bike = Vehicle(4.0, 0.15, 2.64, bikeimage)
musclecar = Vehicle(3.5, 0.25, 1.76, muscleimage)
truck = Vehicle(3.0, 0.3, 1.20, truckimage)

print (F1.max_speed)

player1choice = input("Input player 1 choice").lower()
player2choice = input("Input player 2 choice").lower()

if player1choice == ("f1"):
    choice1 = F1
elif player1choice == ("sports"):
    choice1 = sportscar
elif player1choice == ("muscle"):
    choice1 = musclecar
elif player1choice == ("truck"):
    choice1 = truck
else:
    choice1 = bike

if player2choice == ("f1"):
    choice2 = F1
elif player2choice == ("sports"):
    choice2 = sportscar
elif player2choice == ("muscle"):
    choice2 = musclecar
elif player2choice == ("truck"):
    choice2 = truck
else:
    choice2 = bike

running = True
while running:
    pygame.display.set_caption("Speed Wars")
    WHITE = (255, 255, 255)
    screen.fill(WHITE)
    screen.blit(BackGround.image, BackGround.rect)

    #Vehicle 1
    if keys[0] == True:
        direction += (choice1).turning_radius
    if keys[1] == True:
        direction -= (choice1).turning_radius
    if keys[2] == True and forward <= (choice1).max_speed:
        forward += (choice1).acceleration
    if keys[3] == True and forward >= 0:
        forward -= (choice1).acceleration

    #Vehicle 2
    if keys_2[0] == True:
        direction_2 += (choice2).turning_radius
    if keys_2[1] == True:
        direction_2 -= (choice2).turning_radius
    if keys_2[2] == True and forward_2 <= (choice2).max_speed:
        forward_2 += (choice2).acceleration
    if keys_2[3] == True and forward_2 >= 0:
        forward_2 -= (choice2).acceleration

    movex = math.cos(direction / 57.29) * forward
    movey = math.sin(direction / 57.29) * forward
    xpos += movex
    ypos -= movey

    movex_2 = math.cos(direction_2 / 57.29) * forward_2
    movey_2 = math.sin(direction_2 / 57.29) * forward_2
    xpos_2 += movex_2
    ypos_2 -= movey_2

    rotation = pygame.transform.rotate((choice1).image, direction)
    rotation_2 = pygame.transform.rotate((choice2).image, direction_2)
    screen.blit(rotation, (xpos, ypos))
    screen.blit(rotation_2, (xpos_2, ypos_2))
    pygame.display.flip()
    time.sleep(0.01)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            exit(0)

        if event.type == pygame.KEYDOWN:
            if event.key == K_LEFT:
                keys[0] = True
            elif event.key == K_RIGHT:
                keys[1] = True
            elif event.key == K_UP:
                keys[2] = True
            elif event.key == K_DOWN:
                keys[3] = True

            if event.key == K_a:
                keys_2[0] = True
            elif event.key == K_d:
                keys_2[1] = True
            elif event.key == K_w:
                keys_2[2] = True
            elif event.key == K_s:
                keys_2[3] = True

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                keys[0] = False
            elif event.key == pygame.K_RIGHT:
                keys[1] = False
            elif event.key == pygame.K_UP:
                keys[2] = False
            elif event.key == pygame.K_DOWN:
                keys[3] = False

            if event.key == pygame.K_a:
                keys_2[0] = False
            elif event.key == pygame.K_d:
                keys_2[1] = False
            elif event.key == pygame.K_w:
                keys_2[2] = False
            elif event.key == pygame.K_s:
                keys_2[3] = False

        #Collision detection
        (choice1).checkCollision((choice2).image, (choice1).image)

  • Какая строка кода вызывает ошибку? Где-то в вашем коде вы пытаетесь получить доступ к прямоугольнику из Surface вместо объекта или с помощью метода get_rect(). 21.01.2018
  • Похоже, вы могли бы использовать определение столкновений с точностью до пикселя, которого вы можете достичь с помощью масок. 21.01.2018

Ответы:


1

Проблема в том, что ваш код передает два изображения в метод checkCollision в вашем классе Vehicle. Затем вы передаете эти два изображения в функцию collide_rect, которая ожидает два Sprite.

В результате вы получаете сообщение об ошибке, говорящее о том, что два переданных объекта (в данном случае Surfaces) не содержат прямоугольников.

Чтобы решить эту проблему:

  • Используйте суперкласс Sprite для вашего класса Vehicle.

  • Просто передайте спрайт other в метод checkCollision.

В результате ваша функция checkCollision должна выглядеть примерно так:

def checkCollision(self, sprite2):
    col = pygame.sprite.collide_rect(self, sprite2)
    if col == True:
        print ("True")

И вызов к нему должен выглядеть примерно так:

choice1.checkCollision(choice2)

Кроме того, заголовок вашего класса Vehicle должен выглядеть так:

class Vehicle(pygame.sprite.Sprite)

Некоторые другие проблемы в вашем коде, которые следует исправить:

  • Вы получаете ввод с клавиатуры. Это очень странно в игре. Вместо этого вы должны рассмотреть возможность обработки этого с помощью ввода с клавиатуры.

  • Вы используете скобки вокруг выбора1 и выбора2. Это не нужно.

  • В вашем основном игровом цикле есть код, который не нужно запускать каждый кадр, например pygame.display.set_caption(). Это снова не нужно, и такой код должен идти перед основным игровым циклом.

  • Порядок вашего основного игрового цикла отличается от того, как это обычно делается. Во-первых, вы должны выполнить обработку событий. Во-вторых, сделайте свою логику и, наконец, сделайте свой рендеринг.

  • Кроме того, вы создаете 5 объектов и загружаете много изображений, из которых будут использоваться только два. Вместо этого создайте и загрузите объекты, которые будут использоваться в игре после того, как пользователь решит, какой машиной он будет играть.

  • Никогда не используйте time.sleep() внутри скрипта pygame. Эта функция является злом при использовании с pygame и вызывает много ошибок и багов. Если вы хотите использовать ограничение частоты кадров, используйте Clock.

Я настоятельно рекомендую вам следовать этим пунктам.

Я надеюсь, что этот ответ помог вам, и если у вас есть дополнительные вопросы, не стесняйтесь оставлять комментарии ниже!

21.01.2018
  • Вы должны передать два спрайта в checkCollision вместо двух прямоугольников. Также имело бы смысл изменить его, чтобы метод принимал в качестве аргумента только другой спрайт, а затем передавал self в collide_rect:col = pygame.sprite.collide_rect(self, other_sprite). collide_rect также можно заменить на pygame.Rect.colliderect. Кстати, класс Vehicle должен наследоваться от pygame.sprite.Sprite. 21.01.2018
  • Спасибо, что поймали это! Почему-то я подумал, что он использует метод rect.colliderect вместо метода pygame.sprite.collide_rect. 21.01.2018
  • @MichealO'Dwyer, спасибо за ответ, похоже, это решение отчасти работает. Когда программа запущена, она постоянно выводит «Истина», даже если два автомобиля не касаются/не пересекаются друг с другом. Любые идеи? 22.01.2018
  • @skrx Смотрите мой ответ Майклу О'Дуайеру выше, есть идеи, в чем причина? 22.01.2018
  • Я думаю, проблема в том, что pygame использует прямоугольники для обнаружения столкновений, что иногда бывает неточным. @skrx опубликовал информативный ответ ниже о том, как реализовать обнаружение столкновений с точностью до пикселя. 22.01.2018
  • Если хотите, можете сами увидеть проблему, добавив строки pygame.draw.rect(screen, (255, 0, 0), self.rect, 4) и pygame.draw.rect(screen, (255, 0, 0), sprite2.rect, 4) в свой метод checkCollision. Это просто нарисует прямоугольники, передаваемые в функцию collide_rect. 22.01.2018
  • @RossC Я думаю, что ребята в другой ветке уже объяснили, что вам нужно обновить rect автомобилей. Вы также должны создать отдельные объекты/экземпляры транспортных средств для игроков, потому что, если оба игрока выберут одну и ту же машину, они будут использовать один и тот же прямоугольник. Я имею в виду, что сначала пусть игроки выбирают свои автомобили (строки), а затем создают экземпляры вместо того, чтобы назначать один и тот же экземпляр обоим игрокам. 23.01.2018

  • 2

    Чтобы реализовать контрольные точки в игре, я бы использовал решение, подобное этому: определите список, группу и т. д., содержащую ваши контрольные точки, и установите начальную точку и активную точку на первую контрольную точку в списке. Вы можете использовать итератор itertools.cycle, чтобы легко перебирать точки . Когда игрок касается контрольной точки, вы устанавливаете active_checkpoint на следующую точку в итераторе и проверяете, была ли она начальной точкой, если да, увеличиваете свой счетчик laps.

    Если вы хотите обнаружить столкновение с точностью до пикселя, вы можете задать для спрайтов self.mask и используйте pygame.sprite.collide_mask .

    Вот упрощенный пример. Я просто меняю изображения спрайтов здесь, чтобы показать, какой из них активен.

    import itertools
    
    import pygame as pg
    
    
    CHECKPOINT_IMG = pg.Surface((120, 20), pg.SRCALPHA)
    CHECKPOINT_IMG.fill((120, 60, 0))
    CHECKPOINT2_IMG = pg.Surface((120, 20), pg.SRCALPHA)
    CHECKPOINT2_IMG.fill((220, 110, 0))
    
    
    class Player(pg.sprite.Sprite):
    
        def __init__(self, pos, checkpoints):
            super().__init__()
            self.image = pg.Surface((60, 60), pg.SRCALPHA)
            pg.draw.polygon(self.image, (0, 100, 240), [(30, 0), (60, 60), (0, 60)])
            self.rect = self.image.get_rect(center=pos)
            self.mask = pg.mask.from_surface(self.image)
            self.checkpoints = itertools.cycle(checkpoints)
            self.active_checkpoint = next(self.checkpoints)
            self.start_point = self.active_checkpoint
            self.active_checkpoint.image = self.active_checkpoint.image_active
            self.laps = -1  # I start at -1 because the start is the first checkpoint.
    
        def handle_event(self, event):
            if event.type == pg.MOUSEMOTION:
                self.rect.center = event.pos
                if pg.sprite.collide_mask(self, self.active_checkpoint):
                    if self.active_checkpoint == self.start_point:  # Completed a round.
                        self.laps += 1
                        pg.display.set_caption('Laps: {}'.format(self.laps))
                    # I change the images of the previous and next checkpoint
                    # to show which one is active.
                    self.active_checkpoint.image = self.active_checkpoint.image_inactive
                    # Switch to the next checkpoint.
                    self.active_checkpoint = next(self.checkpoints)
                    self.active_checkpoint.image = self.active_checkpoint.image_active
    
    
    class Checkpoint(pg.sprite.Sprite):
    
        def __init__(self, pos, angle=0):
            super().__init__()
            self.image_inactive = pg.transform.rotate(CHECKPOINT_IMG, angle)
            self.image_active = pg.transform.rotate(CHECKPOINT2_IMG, angle)
            self.image = self.image_inactive
            self.rect = self.image.get_rect(center=pos)
            self.mask = pg.mask.from_surface(self.image)
    
    
    class Game:
        def __init__(self):
            self.screen = pg.display.set_mode((640, 480))
    
            self.done = False
            self.clock = pg.time.Clock()
            self.checkpoints = (
                Checkpoint((100, 200), 0),
                Checkpoint((300, 100), 60),
                Checkpoint((500, 300), 10),
                Checkpoint((200, 300), 30),
                )
    
            self.player = Player((20, 20), self.checkpoints)
            self.all_sprites = pg.sprite.Group(self.player)
            self.all_sprites.add(self.checkpoints)
    
        def run(self):
            while not self.done:
                self.event_loop()
                self.update()
                self.draw()
                pg.display.flip()
                self.clock.tick(60)
    
        def event_loop(self):
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    self.done = True
                self.player.handle_event(event)
    
        def update(self):
            pass
    
        def draw(self):
            self.screen.fill((30, 30, 30))
            self.all_sprites.draw(self.screen)
    
    
    if __name__ == '__main__':
        pg.init()
        game = Game()
        game.run()
        pg.quit()
    
    21.01.2018
    Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]