【问题标题】:How to make a variable output 'True' when two images collide in pygame当两个图像在pygame中发生碰撞时如何使变量输出“真”
【发布时间】:2018-01-22 08:57:50
【问题描述】:

我目前正在制作一款自上而下的赛车游戏,需要一种方法来检测车辆何时跑完一圈。我选择通过在赛道周围添加图像来做到这一点,作为检查点,与赛道表面相匹配。驶过时,它们输出 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() 方法访问矩形。
  • 听起来你可以使用pixel-perfect collision detection,你可以借助掩码来实现。

标签: python pygame sprite collision rect


【解决方案1】:

问题是您的代码将两个图像传递给您的Vehicle 类中的checkCollision 方法。然后你将这两个图像传递给collide_rect 函数,该函数需要两个Sprites。

因此,您会收到一条错误消息,告诉您传入的两个对象(在本例中为Surfaces)不包含矩形。

要解决此问题:

  • 为您的Vehicle 类使用超类Sprite

  • 只需将 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)

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

  • 您正在接收来自键盘的输入。这在游戏中是非常奇怪的。相反,您应该考虑通过键盘输入来处理这个问题。

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

  • 您的主游戏循环中有一些代码不需要每帧都运行,例如pygame.display.set_caption()。这也是不需要的,这样的代码应该在主游戏循环之前。

  • 主游戏循环的顺序与通常的顺序不同。首先,您应该进行事件处理。其次,做你的逻辑,最后,做你的渲染。

  • 另外,您正在制作 5 个对象并加载许多图像,而其中仅使用两个。相反,在用户决定他们将扮演哪辆车之后,创建并加载将在游戏中使用的对象。

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

我强烈建议您遵循这些项目。

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

【讨论】:

  • 你必须将两个 sprites 传递给checkCollision 而不是两个矩形。更改它也很有意义,以便该方法仅将另一个精灵作为参数,然后将self 传递给collide_rectcol = pygame.sprite.collide_rect(self, other_sprite)collide_rect 也可以替换为 pygame.Rect.colliderect。顺便说一句,Vehicle 类应该继承自 pygame.sprite.Sprite
  • 感谢您的关注!由于某种原因,我以为他使用的是rect.colliderect 方法而不是pygame.sprite.collide_rect 方法。
  • @MichealO'Dwyer ,感谢您的回复,这个解决方案似乎确实有点工作。当程序运行时,它似乎不断地输出“真”,即使两辆车没有相互接触/相交。有什么想法吗?
  • @skrx 查看我上面对 MichealO'Dwyer 的回复,有什么想法是什么原因造成的吗?
  • 我认为问题在于 pygame 使用矩形进行碰撞检测,这有时是不准确的。 @skrx 在下面发布了有关如何实现像素完美碰撞检测的信息性答案。
【解决方案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()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多