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