Reputation: 103
I am a student working on a school project. My collision isn't working and I am starting to get frustrated. Whenever I try to run my game, an error message pops up saying
"AttributeError: type object 'Ball' has no attribute 'rect'".
In my code you will see that I have self.rect = pygame.Rect(self.x, self.y, 100, 100)
. I have tried to use the get_rect function, but this still doesn't work. If you have any tips for the code, feel free to add a comment.
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()
Upvotes: 0
Views: 136
Reputation: 20478
There are several problems, but as Sloth has already explained, the AttributeError
gets raised because the player
is a reference to the Ball
class and not an instance. The player
variable actually makes no sense, since the basketball
is the actual playable instance not the player
and you should pass the basketball
to the collide_mask
function instead. Just remove the player
.
point = pygame.sprite.collide_mask(basketball, ring)
The event handling is a bit messed up. Don't call pygame.key.get_pressed
inside of the event loop (for event in pygame.get_event():
) because that line and the following code will be exectued once per event in the queue.
You've also mixed up two different ways to move the sprite: Either do basketball.y -= 5
with key.get_pressed
or set the speed in the event loop: 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
If you use the key.get_pressed
solution, you can remove the self.speed_x
and speed_y
attributes.
You can also pass a rect with the size of the screen to the update
method and use it to clamp the rect of the ball.
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
You should add a pygame.time.Clock
instance and call clock.tick
every frame to limit the frame rate, otherwise the game will run way too fast and the speed will depend on the PC.
Lists (and other mutable containers) shouldn't be modified while you're iterating over them or items could be skipped. Just iterate over a slice copy (or use a list comprehension):
# 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
You forgot to render the ring
(which was a bit confusing during the debugging): ring.render(screen)
.
And the score
variable was missing.
Here's the complete, working example (I used some replacement surfaces):
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()
Upvotes: 1
Reputation: 101122
The exception is raised because the variable player
is a reference to the class Ball
, and not an instance of it.
Look at this line:
player = Ball
This does not create a new instance, since you don't call the class with (...)
.
Other than that, if you use the Sprite
class, better use it like intended. There's no point in storing the coordiantes in a x
and y
attribute in your classes when there's already the rect
attribute.
Also, you don't need a render
method when there's already the Group
class that does this for you.
Here's an example where I changed your code to use the intended pygame features (shorted for brevity):
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()
Upvotes: 1