【问题标题】:Pygame spritecollide giving me iterable error [duplicate]Pygame spritecollide给我可迭代的错误[重复]
【发布时间】:2019-10-02 11:53:55
【问题描述】:

我正在使用 pygame 创建一个 Brick Breaker 游戏。除了碰撞之外,我已经完成了其他所有工作。我尝试使用 collide_rect 但它不起作用,但它没有给我错误消息。我尝试使用 spritecollide,但它给了我错误消息 Ball object is not iterable。我的目标是检测块,删除它,并提高分数。抱歉,我的代码有点乱。 这是我的代码(碰撞检测在底部附近):

import pygame
import paddle
import ball
import block
import time
import random
import math
screenW, screenH = 1280, 720

backroundColor = [0,0,0]

offset = 30
PaddleW = 200
PaddleH = 25
PaddleX = screenW / 2 - PaddleW / 2
PaddleY = screenH - PaddleH - offset

n = 10
blockW = screenW / n #128 per block
blockH = blockW /4 + 10
pygame.init()
pygame.display.set_caption("Brick Breaker")
Display = pygame.display.set_mode((screenW, screenH))
Display.fill(backroundColor)

player = paddle.Paddle(PaddleX, PaddleY, PaddleW, PaddleH)

ballSize = 12
ballX = PaddleX
ballY = PaddleY-20
ballSpeed = -4
ballMoveY = ballSpeed
ballMoveX = ballSpeed
balls = pygame.sprite.Group()
balls = ball.Ball(ballX, ballY, ballSize)
#ballDraw = ball.Ball.draw(Display)

lives = 3
score = 0
shiftX = 0
shiftY = 0

#blocks init
'''blocks = pygame.sprite.Group()
blocks = []'''

#block1 = pygame.sprite.Group()
block1 = block.Block(10,60,blockW,blockH)

counter = 0
def displayText(text, color, posX, posY, size):
    str(text)
    font = pygame.font.SysFont('impact', size)
    textWrite = font.render(text, True, color)
    Display.blit(textWrite, (posX, posY))
def waitForKey():
    wait = True
    while wait:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                    pygame.quit()
            keys = pygame.key.get_pressed()
            if keys[pygame.K_SPACE]:
                wait = False


'''m=3
for y in range (4): #Spawn blocks
    for i in range (0, int(screenW), int(blockW)):
        blocks.append(block.Block(i+2, blockH*(y+2), blockW-4, blockH-4))'''
block1.draw(Display)
#blocks.draw = (14,14,14,14)

FPSClock = pygame.time.Clock()
FPS = 144

GameOver = False
Display.fill([130,140,230])
displayText('Brick Breaker', [200,230,100], 450,100, 80)
displayText('Use the mouse to move the paddle and bounce the ball. Try to break the bricks and not die.', [220,10,10], 120, 300,30)
displayText('Press space to begin', [0,0,0],520,510, 30)
pygame.display.flip()
waitForKey()
while not GameOver:
    counter += 1/FPS
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            if GameOver == False:
                GameOver = True
    mouseX = pygame.mouse.get_pos()[0]
    Display.fill(backroundColor)
    player.draw(Display)
    player.move(mouseX)

    balls.move(ballX, ballY) #pygame.draw.circle(Display, [0, 120, 120], [int(ballX), int(ballY)], ballSize)
    balls.draw(Display)

    '''
    for x in range(len(blocks)):
        blocks[x].draw(Display)
    '''
    #Draw blocks
    block1.draw(Display)

    if ballX <= 0:
        ballMoveX = -ballSpeed
        pygame.mixer.music.load("GungaGinga.mp3")
        pygame.mixer.music.play(1)
        #shiftX = random.randint(-2,2)
        #shiftY = -shiftX
    elif ballX >= screenW:
        ballMoveX = ballSpeed
        pygame.mixer.music.load("GungaGinga.mp3")
        pygame.mixer.music.play(1)
        #shiftX = random.randint(-2, 2)
        #shiftY = -shiftX
    if ballY <= 0:
        ballMoveY = -ballSpeed
        pygame.mixer.music.load("GungaGinga.mp3")
        pygame.mixer.music.play(1)
        #shiftY = random.randint(-2, 2)
        #shiftX = -shiftY
    elif ballY >= screenH:

        pygame.mixer.music.load("OOF.mp3")
        pygame.mixer.music.play(1)
        ballMoveY = ballSpeed
        lives -= 1
        ballX = PaddleX
        ballY = PaddleY - 20
        balls.move(ballX, ballY)
        #shiftX = 0
        #shiftY = 0
        time.sleep(1)

    if ballY <= PaddleY + PaddleW and ballY +ballSize >= PaddleY:
        if (ballX >= mouseX or ballX <= mouseX) and (ballX + ballSize <= mouseX + PaddleW/2 and ballX + ballSize >= mouseX - PaddleW/2): # or ballX + ballSize >= mouseX - PaddleW/2
            ballMoveY = -ballMoveY#ballSpeed
            pygame.mixer.music.load("GungaGinga.mp3")
            pygame.mixer.music.play(1)
            pygame.mixer.music.load("OOF.mp3")
            pygame.mixer.music.play(1)
    ballX += ballMoveX
    ballX = ballX +shiftX
    ballY += ballMoveY
    ballY = ballY + shiftY

    displayText('Lives: %s'%lives, [90,190,90], 20, 10, 40)
    displayText('Time: %.1f' %counter, [20,190,90], 1100, 10, 40)
    displayText('Score: %s' % score, [90, 190, 90], 570, 10, 40)
    if counter == 5:
        ballSpeed -= 1

#Collision Test
    #if pygame.sprite.spritecollideany(balls, blocks):
        #blocks.kill(blocks)
    #hitBlock = pygame.sprite.collide_mask(blocks, player)
    '''    def hit(self):
        hit_block = pygame.sprite.groupcollide(blocks, self.balls, False, True)
        if hit_block:
            return True
        else:
            return False'''
    #is_a_collision = pygame.sprite.collide_mask(blocks, balls)
    '''
    if self.hit():
        print("collision")
        score += 10
        blocks.kill(blocks)
        '''
    #collision
    #hit_block = pygame.sprite.spritecollide(balls, block1, False, True)
    #hit_block()
    #pygame.Surface.get_rect(block1)
    #Current colliderect not working
    if pygame.sprite.collide_rect(block1, balls):
        score += 10
        #block1.kill()
        print("collision")

    #Spritecollide not working -  gives error message

    if pygame.sprite.spritecollide(block1, balls,False):
        #block1.kill()
        score += 10



    pygame.display.flip()
    pygame.display.update()
    FPSClock.tick(FPS)
    if (lives <= 0):
        #You died
        Display.fill([130, 140, 230])
        displayText('You Lost', [200, 230, 100], 500, 100, 80)
        displayText('Thanks for trying',[250, 10, 10], 540, 300, 35)
        displayText('Press space to exit', [0, 0, 0], 540, 510, 30)
        pygame.display.flip()
        waitForKey()
        GameOver = True
    #balls.hit()
    '''
    if balls.collide():
        print("collide")
        score += 10
        '''
pygame.quit()

这是我的课程示例:

import pygame

class Block(pygame.sprite.Sprite):
    def __init__(self, posx, posy, width, height):
        pygame.sprite.Sprite.__init__(self)
        super().__init__()
        #self.rect = self.image.get_rect()
        self.rect = pygame.Rect(posx, posy, width, height)
        self.posx = posx
        self.posy = posy
        self.height = height
        self.width = width
    def draw (self, Display):
        pygame.draw.rect(Display, [6,130,183], [self.posx, self.posy, self.width, self.height])

我正在使用 PyCharm 请让我知道该怎么做。 谢谢

【问题讨论】:

  • 欢迎来到 Stack Overflow!请edit您的代码将其减少为您的问题的minimal reproducible example。您当前的代码包含许多与您的问题无关的内容 - 一个最小样本通常看起来类似于一个好的单元测试:只执行一项任务,并为可重复性指定输入值。
  • 我还没有阅读您的所有代码,但这种立即重新分配看起来很可疑:balls = pygame.sprite.Group() \ balls = ball.Ball(ballX, ballY, ballSize)。另外,如果您使用的是 PyCharm,那么我将解决所有灰色波浪线 :-)。
  • @FiddleStix 设置 balls = ball.Ball(ballX,ballY,ballSize) 可以更轻松地调用代码。我也试过不叫“球”,但还是不行。

标签: python pygame collision


【解决方案1】:

问题是条件

if (ballX >= mouseX or ballX <= mouseX) and (ballX + ballSize <= mouseX + PaddleW/2 and ballX + ballSize >= mouseX - PaddleW/2):

第一个条件((ballX &gt;= mouseX or ballX &lt;= mouseX))的结果总是True,第二个条件搞砸了。

如果您有一个从x1x1+w1 的范围和从x2x2+w2 的第二个范围,那么这两个范围是重叠的

x1 <= x2+w and x2 <= x1+w1 

所以碰撞测试必须是:

if ballY <= PaddleY + PaddleH and PaddleY <= ballY + ballSize:
    if ballX <= mouseX + PaddleW/2 and mouseX - PaddleW/2 <= ballX + ballSize:
        ballMoveY = -ballMoveY
        score += 10

无论如何我建议使用pygame.Rect 对象和方法.colliderect()。例如:

ball_rect   = pygame.Rect(ballX, ballY, ballSize, ballSize)
paddle_rect = pygame.Rect(mouseX-PaddleW/2, PaddleY, PaddleW, PaddleH)
if ball_rect.colliderect(paddle_rect):
    ballMoveY = -ballMoveY
    score += 10

由于您的对象是Sprites,我建议保持.rect 属性是最新的。例如:

class Ball(pygame.sprite.Sprite):
    # [...]

    def move(self, x, y):
        self.rect.topleft = (x, y)
        self.posx, self.posy = self.rect.topleft
class Paddle(pygame.sprite.Sprite):
    # [...]

    def move(self, x):
        self.rect.centerx = x
        self.posx = self.rect.left

现在您可以使用对象的.rect 属性进行碰撞测试:

if ball.rect.colliderect(player.rect):
    ballMoveY = -ballMoveY
    score += 10

由于两个对象都是精灵,因此建议使用pygame.sprite.collide_rect()

if pygame.sprite.collide_rect(ball, player):
    ballMoveY = -ballMoveY
    score += 10

注意,Sprite 对象和 Group 或事件 2 Groups 的碰撞可以通过 pygame.sprite.spritecollide() 分别找到 pygame.sprite.groupcollide()。例如:

blocks = pygame.sprite.Group()
blocks.add(block.Block(10,block1Y,blockW, blockH, blockColor))
if pygame.sprite.spritecollide(ball, blocks, False):
    ballMoveY = -ballMoveY
    score += 10
for block in blocks:
    block.draw(Display)

【讨论】:

  • 感谢您的提示。我尝试使用 collide_rect 检查与块(block1)的碰撞,但这不起作用。对于桨,我使用坐标对桨进行了编码。
  • 我没有收到任何错误消息,但是当我运行代码时,什么也没有发生。如果发生碰撞,它应该增加分数,但事实并非如此。理想情况下,我想使用 spritecollied,但是当我尝试使用它时,它会说“‘球’对象不可迭代”。
  • 感谢您的帮助。通过使用collide_rect,我能够使块碰撞正常工作。 Spritecollide 仍然给我可迭代的错误,但没关系。我正在尝试使用更多的块,但第二个没有检测到碰撞。我复制了相同的定义和 collide_rect 函数,并将其从 block1 更改为 block3。 block1 仍然有效,但 block3 无效。
  • @MiPlayer123 对于组中的多个块,您必须为单个 Sprite 使用 spritecollide,您必须使用 collide_rect
  • 我正在使用 if pygame.sprite.spritecollide(balls,block1,True): print("collisions") 但它给了我错误 'Block' object has no attribute 'sprites' 而我是不知道为什么
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-20
  • 1970-01-01
  • 2020-03-08
  • 2017-03-05
  • 2023-03-31
  • 2021-08-31
相关资源
最近更新 更多