Pygame Collision, Ошибка атрибута - PullRequest
0 голосов
/ 06 июля 2018

Я студент, работающий над школьным проектом. Мое столкновение не работает, и я начинаю расстраиваться. Всякий раз, когда я пытаюсь запустить свою игру, появляется сообщение об ошибке

"AttributeError: объект типа '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()

Ответы [ 2 ]

0 голосов
/ 06 июля 2018

Есть несколько проблем, но, как уже объяснил Слот, AttributeError повышается, потому что player является ссылкой на класс Ball, а не экземпляром. Переменная player на самом деле не имеет смысла, поскольку basketball - это фактический воспроизводимый экземпляр, а не player, и вы должны вместо этого передать basketball в функцию collide_mask. Просто удалите player.

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

Обработка событий немного запутана. Не вызывайте pygame.key.get_pressed внутри цикла события (for event in pygame.get_event():), потому что эта строка и следующий код будут выполняться один раз для каждого события в очереди.

Вы также перепутали два разных способа перемещения спрайта: либо выполните basketball.y -= 5 с помощью key.get_pressed, либо установите скорость в цикле событий: 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_x и speed_y.

Вы также можете передать прямоугольник с размером экрана методу update и использовать его для зажима прямоугольника шара.

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 каждый кадр, чтобы ограничить частоту кадров, в противном случае игра будет работать слишком быстро и скорость будет зависеть от ПК.


Списки (и другие изменяемые контейнеры) не должны изменяться, пока вы перебираете их, иначе элементы могут быть пропущены. Просто переберите копию фрагмента (или используйте понимание списка):

# 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()
0 голосов
/ 06 июля 2018

Исключение возникает, поскольку переменная player является ссылкой на класс Ball, а не ее экземпляром.

Посмотрите на эту строку:

player = Ball

Это не создает новый экземпляр, так как вы не вызываете класс с (...).


Кроме этого, если вы используете класс Sprite, лучше используйте его, как и предполагалось. Нет смысла хранить координаты в атрибутах x и y в ваших классах, когда уже есть атрибут rect.

Кроме того, вам не нужен метод render, когда уже существует класс Group, который делает это за вас.

Вот пример, в котором я изменил ваш код, чтобы использовать предполагаемые функции 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()
...