This is bad:
class Star:
def __init__(self):
self = pygame.Surface((10, 10))
self.rect = self.get_rect(center=(0, random.randint(0, 590)))
self = pygame.Surface sets self to a Surface. When you do self.rect = self.get_rect() you are adding rect to the Surface, not the Star. This causes the code to crash when I run it. Surface must have slots instead of an attribute dictionary. You cannot add attributes to instances of a class that has slots.
If it did run, rect would be an attribute of the Surface, not Star. If you wanted to get the rect, you would have to ask the Surface. Unfortunately you don't have any way to reference the surface.
To fix those problems you could do this:
class Star(pygame.surface.Surface):
def __init__(self):
super().__init__((10, 10))
self.rect = self.get_rect(center=(0, random.randint(0, 590)))
This makes a new kind of Surface that has a rect attribute. The subclass has an attribute dictionary, so it you can add a rect attribute to instances of Star.
This also fixes the problem of not having a reference to the Surface. Star is a subclass of Surface, so all Stars are also Surfaces.
Another way to write this is to make Star and aggregate class instead of a subclass. In this code Star HAS A surface instead of Star IS A surface. Written this way, Star remindes me of a Sprite.
class Star():
def __init__(self):
self.surface = pygame.surface.Surface((10, 10))
self.rect = self.surface.get_rect()
self.rect.center = (0, random.randint(0, 590)))
A sprite is a surface you can move around, just like Star. Unlike Star, pygame knows all about sprites and provides a lot of support that makes working with sprites easy. This is Star and Rock as sprites. In Sprite, the surface attribute is named "image". Usually the image is loaded from an image file, so a rock can look like a rock instead of a rectangle. Sprites should have an update() method that is called to move the sprite around. If you want to delete a sprite you kill() it.
class Star(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.Surface((10, 10)) # Replace with code that loads a star image
self.rect = self.image.get_rect()
self.rect.center = (0, random.randint(0, 590))
def update(self):
self.rect.x += 1
if self.rect.x > 800:
self.kill()
class Rock(pygame.sprite.Sprite):
def __init__(self):
self.image = pygame.Surface((10, 10)) # Replace with code that loads a rock image
self.rect = self.image.get_rect()
self.rect.center = (0, random.randint(0, 590))
def update(self):
self.rect.x += 2
if self.rect.x > 800:
self.kill()
The Rock and Star code is very similar, so you might want to make a superclass that does most of the work. In this code the Obstacle class does all the work. The Star and Rock classes just tell Obsacle how a Star or Rock looks and how fast it moves.
class Obstacles(pygame.sprite.Sprite):
def __init__(self, window, size, speed, color):
super().__init__()
self.window = window
self.image = pygame.Surface(size)
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.center = (0, random.randint(0, 590))
self.speed = speed
def update(self):
self.rect.x += self.speed
wide, high = self.window.get_size()
if self.rect.x > wide:
self.kill()
class Star(Obstacles):
"""A small, slow, yellow obstacle."""
def __init__(self, window):
super().__init__(window, (10, 10), 1, "yellow")
class Rock(Obstacles):
"""A big, fast, grey obstacle."""
def __init__(self, window):
super().__init__(window, (20, 20), 2, "grey")
This code doesn't do what you think:
if random.randint(1, 25):
stars.append(Star())
Maybe you want there to be a 1 in 25 chance of creating a Star? That code would look like this:
if random.randint(1, 25) == 1:
stars.append(Star())
Remember that I said pygame knows about sprites and makes it easier to program when you use sprites? Your collision and screen update code works, but it is easier if you let pygame do the work. In this code I put stars and rocks in a group and I let pygame do the updates and detect collisions.
def mainloop():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
# Move player and obstacles.
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE] or any(mouse.get_pressed()):
player.update(-3)
else:
player.update(3)
obstacles.update()
# Randomly add obstacles.
if random.randint(1, 25) == 1:
obstacles.add(Star(window))
if random.randint(1, 100) == 1:
obstacles.add(Rock(window))
# check for collisions
if pygame.sprite.spritecollide(player, obstacles, True):
return
# Update the display
window.fill(0)
player.draw(window)
obstacles.draw(window)
pygame.display.flip()
clock.tick(40)
All the code. I modified the game play so the goal is to catch rocks and avoid stars. When the player catches the rock they consume it and grow 5% in size.
import pygame
import random
class Player(pygame.sprite.Sprite):
def __init__(self, window):
super().__init__()
self.window = window
self.image = pygame.Surface((25, 25))
self.image.fill("Blue")
self.rect = self.image.get_rect()
self.rect.center = (700, 300)
def grow(self):
pos = self.rect.center
self.image = pygame.transform.scale_by(self.image, 1.05)
self.rect = self.image.get_rect()
self.rect.center = pos
def update(self, move):
y = self.rect.y + move
wide, high = self.window.get_size()
if 0 <= y <= high - self.rect.height:
self.rect.y = y
def draw(self, window):
window.blit(self.image, self.rect)
class Obstacles(pygame.sprite.Sprite):
def __init__(self, window, size, speed, color):
super().__init__()
self.window = window
self.image = pygame.Surface(size)
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.center = (0, random.randint(0, 590))
self.speed = speed
def update(self):
self.rect.x += self.speed
wide, high = self.window.get_size()
if self.rect.x > wide:
self.kill()
class Star(Obstacles):
def __init__(self, window):
super().__init__(window, (10, 10), 1, "yellow")
class Rock(Obstacles):
def __init__(self, window):
super().__init__(window, (20, 20), 2, "grey")
def play(window):
mouse = pygame.mouse
clock = pygame.time.Clock()
player = Player(window)
obstacles = pygame.sprite.Group()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
# Move player and obstacles.
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE] or any(mouse.get_pressed()):
player.update(-3)
else:
player.update(3)
obstacles.update()
# Randomly add obsacles.
if random.randint(1, 25) == 1:
obstacles.add(Star(window))
if random.randint(1, 100) == 1:
obstacles.add(Rock(window))
# check for collisions
for object in pygame.sprite.spritecollide(player, obstacles, False):
if isinstance(object, Rock):
object.kill()
player.grow()
else:
return
# Update the display
window.fill(0)
player.draw(window)
obstacles.draw(window)
pygame.display.flip()
clock.tick(40)
pygame.init()
window = pygame.display.set_mode((800, 600))
play(window)
pygame.quit()