当两个图像在pygame中发生碰撞时,如何使变量输出为“True”

时间:2018-01-21 17:07:58

标签: python pygame sprite collision rect

我目前正在制作一款自上而下的赛车游戏,需要一种方法来检测车辆何时完成整圈。我选择通过在电路周围添加图像作为检查点来实现这一点,这些检查点与轨道表面相匹配。当被驱动时,它们输出为真,所有必须输出为真,以便计算一圈。但是,我找不到一种方法来检测我的车辆和图像之间的碰撞。

我尝试在车辆上添加rects并检查两辆车相撞时是否可以产生输出但是我得到了这个错误:

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)

2 个答案:

答案 0 :(得分:1)

问题是您的代码会将两张图片传入checkCollision类的Vehicle方法。然后,您将这两个图像传递到collide_rect函数中,该函数需要两个Sprite s。

结果,您收到一个错误,告诉您传入的两个对象(在这种情况下为Surface),不包含rects。

解决此问题:

  • 为您的Sprite课程使用超类Vehicle

  • 只需将其他精灵传递到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)

您的代码中应修复的其他一些问题:

  • 您正在接收键盘输入。这在游戏中非常奇怪。相反,您应该通过键盘输入来处理它。

  • 您在choice1和choice2周围使用括号。这不是必需的。

  • 主游戏循环中的代码不需要在pygame.display.set_caption()等每一帧都运行。这不再需要,像这样的代码应该在主游戏循环之前。

  • 主游戏循环的顺序与正常情况不同。首先,你应该做事件处理。第二,做你的逻辑,最后做你的渲染。

  • 此外,您正在制作5个对象并加载许多图像,其中只使用两个。相反,在用户决定将播放哪辆汽车后,创建并加载将在游戏中使用的对象。

  • 切勿在pygame脚本中使用time.sleep() 。当与pygame一起使用时,此函数是邪恶的,并导致许多错误和错误。如果您想使用帧率上限,请使用Clock

我强烈建议您遵循这些条款。

我希望这个答案对您有所帮助,如果您有任何其他问题,请随时在下面发表评论!

答案 1 :(得分:1)

要在游戏中实施检查点,我将使用与此类似的解决方案:定义包含检查点的列表,组等,并将起点和活动点设置为列表中的第一个检查点。您可以使用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()