【问题标题】:Pygame Collision, Attribute errorPygame碰撞,属性错误
【发布时间】:2018-12-14 13:53:58
【问题描述】:

我是一名从事学校项目的学生。我的碰撞不起作用,我开始感到沮丧。每当我尝试运行我的游戏时,都会弹出一条错误消息说

“属性错误:类型对象'Ball'没有属性'rect'”。

在我的代码中你会看到我有self.rect = pygame.Rect(self.x, self.y, 100, 100)。我曾尝试使用 get_rect 函数,但这仍然不起作用。如果您对代码有任何提示,请随时添加评论。

import pygame
import random
import os
from pygame import mixer

# accessed: 4/6/18  code:https://github.com/codingandcaring/PYgame/blob/bacaab1bd6ec0c97412a136773dfd634455c3e2f/basketball_game.py
#Music
#Tutorial from computingmrh - https://www.youtube.com/watch?v=lUMSK6LmXCQ used on 5th may 2018
snd_file = 'Game.ogg'

mixer.init()
mixer.music.load(snd_file)
mixer.music.play()

#spirtes
class Ball(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('basketball.png').convert_alpha()
        self.image = pygame.transform.scale(self.image, (100, 100))
        self.x = x
        self.y = y
        self.rect = pygame.Rect(self.x, self.y, 100, 100)
        self.speed_x = 5
        self.speed_y = 5
        self.radiusx = 0
        self.radiusy = 100
        self.mask = pygame.mask.from_surface(self.image)

    def update(self, width, height):
        self.x += self.speed_x
        self.y += self.speed_y
        self.rect = pygame.Rect(self.x, self.y, 100, 100)
        if self.x + self.radiusx > width:
            self.speed_x = 0
        if self.y + self.radiusx > height:
            self.speed_y = 0        
        if self.x + self.radiusy > width:
            self.speed_x = 0
        if self.y + self.radiusy > height:
            self.speed_y = 0
        if self.x - self.radiusx <= 0:
            self.speed_x = 0
        if self.y - self.radiusx <= 0:
            self.speed_y = 0


    def render(self, screen):
        screen.blit(self.image, (self.x, self.y))


class Goal(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('goal.png').convert_alpha()
        self.image = pygame.transform.scale(self.image, (220, 220))
        self.x = x
        self.y = y
        self.rect = pygame.Rect(self.x, self.y, 220, 220)

    def render(self, screen):
        screen.blit(self.image, (self.x, self.y))

class Ring(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load('ring.png').convert_alpha()
        self.image = pygame.transform.scale(self.image, (400, 400))
        self.x = x
        self.y = y
        self.rect = pygame.Rect(self.x, self.y, 400, 400)
        self.mask = pygame.mask.from_surface(self.image)

    def render(self, screen):
        screen.blit(self.image, (self.x, self.y))

class Baddie(pygame.sprite.Sprite):
    def __init__(self, x, y, z):
        self.image = pygame.image.load().convert_alpha()
        self.image = pygame.transform.scale(self.image, (220, 220))
        self.rect = self.image.get_rect()
        self.x = x
        self.y = y

    def render(self, screen):
        screen.blit(self.image, (self.x, self.y))

def main():
    # basics
    width = 1200
    height = 722

    pygame.init()
    screen = pygame.display.set_mode((width, height))
    pygame.display.set_caption('Basketball Shootout')
    font = pygame.font.Font(None, 25)

    #code for the badguys
    badtimer=100
    badtimer1=0
    badguys=[[640,100]]

    # Add Variables
    court = pygame.image.load('halfcourt.jpg')
    court = pygame.transform.scale(court, (1200, 722))
    basketball = Ball(50, 50)
    goal = Goal(487, 0)
    ring = Ring(400,400)
    player = Ball
    badguyimg1 = pygame.image.load("wpierdol.png")
    badguyimg1 = pygame.transform.scale(badguyimg1, (100, 100))
    badguyimg2 = pygame.image.load("bad_guy2.gif")
    badguyimg2 = pygame.transform.scale(badguyimg2, (100, 100))
    badguyimg3 = pygame.image.load("bad_guy3.gif")
    badguyimg3 = pygame.transform.scale(badguyimg3, (100, 100))
    badlist = [badguyimg1, badguyimg2, badguyimg3]



    stop_game = False



    #main game logic
    while not stop_game:
        badtimer -= 1
        point = pygame.sprite.collide_mask(player, ring)
        if point:
            score = score + 1


        #Draw the bad guys
        if badtimer==0:
            badguys.append([1040, random.randint(50,430)])
            badtimer=100-(badtimer1*2)
        if badtimer1>=35:
            badtimer1=35
        else:
            badtimer1+=5
        index=0
        for badguy in badguys:
            if badguy[0]<-64:
                badguys.pop(index)
            badguy[0]-=7
            index+=1

        for event in pygame.event.get():
            pressed = pygame.key.get_pressed()
            if pressed[pygame.K_UP]:
                basketball.y -= 5
                basketball.speed_y = -5
            elif pressed[pygame.K_DOWN]:
                basketball.y += 5
                basketball.speed_y = 5
            elif pressed[pygame.K_LEFT]:
                basketball.x -= 5
                basketball.speed_x = -5
            elif pressed[pygame.K_RIGHT]:
                basketball.x += 5
                basketball.speed_x = 5
            if event.type == pygame.QUIT:
                stop_game = True

    # Updating
        basketball.update(width, height)
        screen.blit(court, (0,0))

        text = font.render('Dodge the other team to get to the goal!', True, (0, 0, 0))
        screen.blit(text, (430, 630))
        goal.render(screen)
        basketball.render(screen)
        for badguy in badguys:
            screen.blit(badguyimg1)



        pygame.display.update()

    pygame.quit()

if __name__ == '__main__':
    main()

【问题讨论】:

标签: python python-2.7 pygame collision attributeerror


【解决方案1】:

引发异常是因为变量player 是对 Ball 的引用,而不是它的实例。

看看这一行:

player = Ball

这不会创建新实例,因为您没有使用(...) 调用类。


除此之外,如果您使用 Sprite 类,最好按预期使用它。当已经有 rect 属性时,将坐标存储在您的类中的 xy 属性中是没有意义的。

此外,当已经有 Group 类为您执行此操作时,您不需要 render 方法。

这是一个示例,我将您的代码更改为使用预期的 pygame 功能(为简洁起见):

import pygame

class Ball(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        image = pygame.image.load('basketball.png').convert_alpha()
        self.image = pygame.transform.scale(image, (100, 100))
        self.rect = self.image.get_rect(topleft=(x,y))
        self.speed_x = 5
        self.speed_y = 5
        self.mask = pygame.mask.from_surface(self.image)

    def update(self, screen_rect):
        self.rect.move_ip(self.speed_x, self.speed_y)
        self.rect.clamp_ip(screen_rect)

def main():
    # basics
    width = 1200
    height = 722

    pygame.init()
    screen = pygame.display.set_mode((width, height))
    screen_rect = screen.get_rect()
    pygame.display.set_caption('Basketball Shootout')

    basketball = Ball(50, 50)

    stop_game = False

    sprites = pygame.sprite.Group(basketball)

    #main game logic
    while not stop_game:
        badtimer -= 1

        for event in pygame.event.get():
            pressed = pygame.key.get_pressed()
            if pressed[pygame.K_UP]:
                basketball.speed_y = -5
            elif pressed[pygame.K_DOWN]:
                basketball.speed_y = 5
            elif pressed[pygame.K_LEFT]:
                basketball.speed_x = -5
            elif pressed[pygame.K_RIGHT]:
                basketball.speed_x = 5
            if event.type == pygame.QUIT:
                stop_game = True

        screen.fill(pygame.color.THECOLORS['white'])
        sprites.update(screen_rect)
        sprites.draw(screen)

        pygame.display.update()

    pygame.quit()

if __name__ == '__main__':
    main()

【讨论】:

    【解决方案2】:

    有几个问题,但正如 Sloth 已经解释的那样,AttributeError 被引发是因为 player 是对 Ball 类的引用而不是实例。 player 变量实际上没有意义,因为basketball 是实际可播放实例而不是player,您应该将basketball 传递给collide_mask 函数。只需删除player

    point = pygame.sprite.collide_mask(basketball, ring)
    

    事件处理有点混乱。不要在事件循环 (for event in pygame.get_event():) 内调用 pygame.key.get_pressed,因为该行和以下代码将在队列中的每个事件中执行一次。

    您还混合了两种不同的方式来移动精灵:使用key.get_pressed 执行basketball.y -= 5 或在事件循环中设置速度:basketball.speed_y = -5

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            stop_game = True
        # Either set the speed here.
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                basketball.speed_y = -5
    
    # Or increment the `basketball.y` in the while loop with `pygame.key.get_pressed`.
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]:
        basketball.y -= 5
    elif pressed[pygame.K_DOWN]:
        basketball.y += 5
    if pressed[pygame.K_LEFT]:  # if not elif, to separate vertical and horizontal movement.
        basketball.x -= 5
    elif pressed[pygame.K_RIGHT]:
        basketball.x += 5
    

    如果您使用key.get_pressed 解决方案,您可以删除self.speed_xspeed_y 属性。

    你也可以将一个屏幕大小的rect传递给update方法,并用它来夹住球的rect。

    def update(self, screen_rect):  # Pass a rect with the size of the screen.
        self.x += self.speed_x
        self.y += self.speed_y
        self.rect.topleft = (self.x, self.y)
        if not screen_rect.contains(self.rect):
            # Clamp the rect if it's outside of the screen.
            self.rect.clamp_ip(screen_rect)
            self.x, self.y = self.rect.topleft
    

    您应该添加一个pygame.time.Clock 实例并在每帧调用clock.tick 以限制帧速率,否则游戏会运行得太快,速度取决于PC。


    在迭代列表(和其他可变容器)时不应修改它们,否则可能会跳过项目。只需遍历切片副本(或使用列表推导):

    # You can `enumerate` the badguys list to get the index and the item at the same time.
    for index, badguy in enumerate(badguys[:]):
        if badguy[0] < -64:
            # skrx: Don't modify a list while you're iterating over it.
            # Iterate over a slice copy: badguys[:]
            badguys.pop(index)
        badguy[0] -= 7
    

    你忘了渲染ring(在调试过程中有点混乱):ring.render(screen)

    并且缺少score 变量。

    这是完整的工作示例(我使用了一些替换表面):

    import random
    import pygame
    
    
    class Ball(pygame.sprite.Sprite):
        def __init__(self, x, y):
            pygame.sprite.Sprite.__init__(self)
            # self.image = pygame.image.load('basketball.png').convert_alpha()
            # self.image = pygame.transform.scale(self.image, (100, 100))
            self.image = pygame.Surface((100, 100)).convert_alpha()
            self.image.fill((160, 70, 0))
            self.x = x
            self.y = y
            self.rect = pygame.Rect(self.x, self.y, 100, 100)
            self.speed_x = 0
            self.speed_y = 0
            self.radiusx = 0
            self.radiusy = 100
            self.mask = pygame.mask.from_surface(self.image)
    
        def update(self, screen_rect):  # Pass a rect with the size of the screen.
            self.x += self.speed_x
            self.y += self.speed_y
            self.rect.topleft = (self.x, self.y)
            if not screen_rect.contains(self.rect):
                # Clamp the rect if it's outside of the screen.
                self.rect.clamp_ip(screen_rect)
                self.x, self.y = self.rect.topleft
    
        def render(self, screen):
            screen.blit(self.image, self.rect)
    
    
    class Goal(pygame.sprite.Sprite):
        def __init__(self, x, y):
            pygame.sprite.Sprite.__init__(self)
            # self.image = pygame.image.load('goal.png').convert_alpha()
            # self.image = pygame.transform.scale(self.image, (220, 220))
            self.image = pygame.Surface((220, 220)).convert_alpha()
            self.image.fill((60, 80, 110))
            self.x = x
            self.y = y
            self.rect = pygame.Rect(self.x, self.y, 220, 220)
    
        def render(self, screen):
            screen.blit(self.image, self.rect)
    
    
    class Ring(pygame.sprite.Sprite):
        def __init__(self, x, y):
            pygame.sprite.Sprite.__init__(self)
            # self.image = pygame.image.load('ring.png').convert_alpha()
            # self.image = pygame.transform.scale(self.image, (400, 400))
            self.image = pygame.Surface((400, 400)).convert_alpha()
            self.image.fill((60, 180, 110))
            self.x = x
            self.y = y
            self.rect = pygame.Rect(self.x, self.y, 400, 400)
            self.mask = pygame.mask.from_surface(self.image)
    
        def render(self, screen):
            screen.blit(self.image, self.rect)
    
    
    class Baddie(pygame.sprite.Sprite):
        def __init__(self, x, y, z):
            # self.image = pygame.image.load().convert_alpha()
            # self.image = pygame.transform.scale(self.image, (220, 220))
            self.image = pygame.Surface((90, 90)).convert_alpha()
            self.image.fill((250, 50, 0))
            self.rect = self.image.get_rect()
            self.x = x
            self.y = y
    
        def render(self, screen):
            screen.blit(self.image, (self.x, self.y))
    
    
    def main():
        width = 1200
        height = 722
    
        pygame.init()
        screen = pygame.display.set_mode((width, height))
        screen_rect = screen.get_rect()
        clock = pygame.time.Clock()  # Add a clock to limit the frame rate.
        pygame.display.set_caption('Basketball Shootout')
        font = pygame.font.Font(None, 25)
    
        badtimer = 100
        badtimer1 = 0
        badguys = [[640, 100]]
    
        # court = pygame.image.load('halfcourt.jpg')
        # court = pygame.transform.scale(court, (1200, 722))
        court = pygame.Surface((1200, 722))
        court.fill((30, 30, 30))
        basketball = Ball(50, 50)
        goal = Goal(487, 0)
        ring = Ring(400, 400)
        # The player is not needed since the `basketball` is already
        # the playable ball instance.
        # player = Ball  # Just remove this line.
    
        # badguyimg1 = pygame.image.load("wpierdol.png")
        # badguyimg1 = pygame.transform.scale(badguyimg1, (100, 100))
        # badguyimg2 = pygame.image.load("bad_guy2.gif")
        # badguyimg2 = pygame.transform.scale(badguyimg2, (100, 100))
        # badguyimg3 = pygame.image.load("bad_guy3.gif")
        # badguyimg3 = pygame.transform.scale(badguyimg3, (100, 100))
        badguyimg1 = pygame.Surface((90, 90))
        badguyimg1.fill((60, 50, 210))
        badguyimg2 = pygame.Surface((90, 90))
        badguyimg2.fill((250, 50, 210))
        badguyimg3 = pygame.Surface((90, 90))
        badguyimg3.fill((250, 50, 130))
        badlist = [badguyimg1, badguyimg2, badguyimg3]
    
        score = 0  # The score variable was missing.
    
        stop_game = False
    
        while not stop_game:
            # Event handling.
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    stop_game = True
                # Either set the speed here or increment the `basketball.y`
                # in the while loop with `pygame.key.get_pressed`.
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_UP:
                        basketball.speed_y = -5
                    elif event.key == pygame.K_DOWN:
                        basketball.speed_y = 5
                    elif event.key == pygame.K_LEFT:
                        basketball.speed_x = -5
                    elif event.key == pygame.K_RIGHT:
                        basketball.speed_x = 5
                elif event.type == pygame.KEYUP:
                    # Stop the ball.
                    if event.key == pygame.K_UP:
                        basketball.speed_y = 0
                    elif event.key == pygame.K_DOWN:
                        basketball.speed_y = 0
                    elif event.key == pygame.K_LEFT and basketball.speed_x < 0:
                        basketball.speed_x = 0
                    elif event.key == pygame.K_RIGHT and basketball.speed_x > 0:
                        basketball.speed_x = 0
    
            # Don't call get_pressed in the event loop (for every event).
            # pressed = pygame.key.get_pressed()
            # if pressed[pygame.K_UP]:
            #     basketball.y -= 5
            # elif pressed[pygame.K_DOWN]:
            #     basketball.y += 5
            # if pressed[pygame.K_LEFT]:  # if not elif, to separate vertical and horizontal movement.
            #     basketball.x -= 5
            # elif pressed[pygame.K_RIGHT]:
            #     basketball.x += 5
    
            # Updating.
            basketball.update(screen_rect)
            badtimer -= 1
    
            point = pygame.sprite.collide_mask(basketball, ring)  # Use basketball not player.
            if point:
                # The score will be incremented continually.
                score = score + 1
                print(score)
    
            # Update the bad guys.
            if badtimer == 0:
                badguys.append([1040, random.randint(50,430)])
                badtimer = 100-(badtimer1*2)
            if badtimer1 >= 35:
                badtimer1 = 35
            else:
                badtimer1 += 5
    
            # You can `enumerate` the badguys list to get the index
            # and the item at the same time.
            for index, badguy in enumerate(badguys[:]):
                if badguy[0] < -64:
                    # Don't modify a list while you're iterating over it.
                    # Iterate over a slice copy: badguys[:]
                    badguys.pop(index)
                badguy[0] -= 7
    
            # Drawing.
            screen.blit(court, (0,0))
    
            text = font.render(
                'Dodge the other team to get to the goal!',
                True, (0, 0, 0))
            screen.blit(text, (430, 630))
            goal.render(screen)
            # You forgot to render the ring.
            ring.render(screen)
    
            for badguy in badguys:
                screen.blit(badguyimg1, badguy)  # The `dest`ination arg was missing.
    
            basketball.render(screen)
    
            pygame.display.update()
            clock.tick(60)  # Limit the frame rate to 60 FPS.
    
        pygame.quit()
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

    • 首先我要感谢您。关于球/球员精灵的生成以及球/球员与敌人之间的碰撞,我需要更多帮助。我想知道如何将玩家/球的生成更改为底部中间。另外,当玩家击中敌人时,你能告诉我如何暂停游戏并显示图像。我还想知道你是否有一个很好的教程让我学习如何显示分数。再次感谢您的时间和考虑。
    • 我看到你已经打印了乐谱,但我想知道你将如何将它显示在屏幕上。
    • 关于文本渲染,请阅读Program Arcade Games的第5.17节。
    • 关于玩家生成和碰撞检测的其他问题,我认为最好在 Stack Overflow 上提出一个新问题。说明您想要实现的目标,您尝试解决问题的方法并提供minimal, complete and verifiable example
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多