I am trying to create a stream of raindrops to give a sense of rain using the code below.
Raindrop is used to model the raindrop. It has an update method() to increase the y coordinate so that it will look like its is dropping on the screen when I call this method.
from pygame.sprite import Sprite
class Raindrop(Sprite):
"""A class to represent a single raindrop."""
def __init__(self, rain_game):
"""Initialize the raindrop and set its starting position."""
super().__init__()
self.screen = rain_game.screen
self.settings = rain_game.settings
# Load the raindrop image and set its rect attribute
self.image = pygame.image.load('raindrop.bmp')
self.rect = self.image.get_rect()
#Start each raindrop near the top left of the screen.
self.rect.x = self.rect.width
self.rect.y = self.rect.height
#Store the raindrop`s exact horizontal and vertical positions.
self.x = self.rect.x
self.y = self.rect.y
def update(self):
"""Shift raindrops to one level below"""
self.y += self.settings.rain_speed
self.rect.y = self.y
class Settings:
"""A Class to store all settings for a Sky Full of Stars."""
def __init__(self):
"""Initialize the game`s settings."""
# Screen setiings
self.screen_width = 1200
self.screen_height = 800
self.bg_color = (255, 255, 255)
self.rain_speed = 1.0
I have a problem with the remove_add_raindrops method in the below code.
It works as expected but only for 4 rows of raindrops in my laptop. After 4 rows this code stops to print any raindrops to the screen. So I see only 8 rows of raindrops dropping from top to the bottom of my screen.
import sys
import pygame
from settings import Settings
from raindrop import Raindrop
class Raining():
"""Overall class to display a sky with raindrops."""
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
self.settings.screen_width = self.screen.get_rect().width
self.settings.screen_height = self.screen.get_rect().height
pygame.display.set_caption("It is raining today")
self.raindrops = pygame.sprite.Group()
self._create_raindrops()
def _create_raindrop(self, raindrop_number, row_number):
"""Create a raindrop and place it in the row."""
raindrop = Raindrop(self)
raindrop_width, raindrop_height = raindrop.rect.size
raindrop.x = raindrop_width + 2 * raindrop_width * raindrop_number
raindrop.rect.x = raindrop.x
raindrop.y = raindrop_height + 2 * raindrop_height * row_number
raindrop.rect.y = raindrop.y
self.raindrops.add(raindrop)
def _create_raindrops(self):
"""Create a full screen fo raindrops."""
# Create a raindrop and find the number of raindrops in a row.
# Spacing between each raindrop is equal to one raindrop width.
raindrop = Raindrop(self)
raindrop_width, raindrop_height = raindrop.rect.size
available_space_x = self.settings.screen_width - (2 * raindrop_width)
number_raindrops_x = available_space_x // (2 * raindrop_width)
# Determine the number of rows of raindrops that fit on the screen.
available_space_y = (self.settings.screen_height - 2 * raindrop_height)
number_rows = available_space_y // (2 * raindrop_height)
# Create a full screen of raindrops.
for row_number in range(number_rows):
for raindrop_number in range (number_raindrops_x):
self._create_raindrop(raindrop_number, row_number)
def _remove_add_raindrops(self):
"""
Get rid of old raindrops.
Then update their y coordinate and append to the raindrops sprite group
"""
for raindrop in self.raindrops.copy():
if raindrop.rect.y >= self.settings.screen_height:
self.raindrops.remove(raindrop)
raindrop.rect.y -= self.settings.screen_height
self.raindrops.add(raindrop)
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self._update_raindrops()
self._remove_add_raindrops()
self._update_screen()
print(f"Nr of remaining raindrops: {len(self.raindrops)}")
#Make the most recently drawn screen visible.
pygame.display.flip()
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys-exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
def _check_keydown_events(self, event):
"""Respond to keypresses."""
if event.key == pygame.K_q:
sys.exit()
def _update_raindrops(self):
"""Update the positions of all raindrops."""
self.raindrops.update()
def _update_screen(self):
"""Update images on the screen, and flip to the new screen."""
self.screen.fill(self.settings.bg_color)
self.raindrops.draw(self.screen)
if __name__ == '__main__':
# Make a game instance, and run the game.
rain_game = Raining()
rain_game.run_game()
I also printed the number of raindrops in the list and it is always fixed at 40. I suppose that means I keep adding new raindrops with a new y coordinate after I delete the ones that slips below the screen.
Any hints?
Update:
I removed the _remove_add_raindrops method and re-wrote the update method under the raindrop class like this:
def update(self):
"""Shift raindrops to one level below"""
self.y += self.settings.rain_speed
self.rect.y = self.y
if self.rect.top >= self.screen.get_rect().bottom:
self.rect.y -= self.settings.screen_height
It is still the same behavior.
_remove_add_raindrops runs
raindrop.rect.y -= self.settings.screen_height
to change the y attribute of a Raindrop's rect.
But that change is immediately overwritten when Raindrop's update function is called:
def update(self):
"""Shift raindrops to one level below"""
self.y += self.settings.rain_speed
self.rect.y = self.y
because it gets changed again to self.y.
Just remove _remove_add_raindrops (removing and immediately readding a Raindrop to self.raindrops makes no sense anyway) and change your Raindrop class something like this:
class Raindrop(Sprite):
"""A class to represent a single raindrop."""
def __init__(self, rain_game):
"""Initialize the raindrop and set its starting position."""
super().__init__()
self.screen = rain_game.screen
self.settings = rain_game.settings
# Load the raindrop image and set its rect attribute
self.image = pygame.image.load('raindrop.bmp')
self.rect = self.image.get_rect()
#Start each raindrop near the top left of the screen.
self.rect.x = self.rect.width
self.rect.y = self.rect.height
def update(self):
"""Shift raindrops to one level below"""
self.rect.move_ip(0, self.settings.rain_speed)
if not pygame.display.get_surface().get_rect().bottom > self.rect.top:
self.rect.bottom = 0
so the Raindrop class can check itself if it is going out of screen. If you want to store the position of a Sprite using floats instead of integers, I recommend using the Vector2 class to do so instead of single attributes, because this makes it easier to move your sprites diagonally in the long run.
The problem was that self.y was increasing indefinitely since I was not re-setting it anywhere in the original code or in the updated code.
def update(self):
"""Shift raindrops to one level below"""
self.y += self.settings.rain_speed
self.rect.y = self.y
if self.rect.top >= self.screen.get_rect().bottom:
self.rect.y -= self.settings.screen_height
Change of last line above from "self.rect.y" to "self.y" solved the problem.
Related
I'm making a basic dodger game where you play as a fish and need to avoid getting hit by obstacles. I want the obstacles to come from the right side of the screen at a random velocity (from a certain range, let's just say 4 to 6). I already have the fish/underwater gravity mechanics working. But I have no idea where to start with the obstacles.
This is my main python script:
import pygame
from assets.player import Bob
# Screen properties
pygame.init()
# Important Variables
run = True
game_speed = 0
SCREEN_WIDTH, SCREEN_HEIGHT = 900, 600
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
clock = pygame.time.Clock()
FPS = 60
# Properties
bg_game_surface = pygame.image.load('images/game-background.png')
player = Bob(game_speed)
#############
# Functions #
#############
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.blit(bg_game_surface, (0, 0))
player.update(screen)
pygame.display.update()
clock.tick(FPS)
pygame.quit()
And this is the class for my player:
import pygame
class Bob:
def __init__(self, game_speed):
self.img = pygame.image.load('images/player/bob.png').convert_alpha()
self.img_up = pygame.image.load('images/player/bob_up.png').convert_alpha()
self.rect = self.img.get_rect()
self.game_speed = game_speed
def update(self, screen):
key = pygame.key.get_pressed()
### Collision
if self.rect.top <= 0:
self.rect.centery = (screen.get_height() - self.img.get_height() * 2) + 10
elif self.rect.top >= screen.get_height() - self.img.get_height() * 2:
self.rect.centery = 20
### Movement
if key[pygame.K_w] or key[pygame.K_UP]:
self.rect.centery -= 6
self.img = pygame.image.load('images/player/bob_up.png').convert_alpha()
### Gravity
else:
self.img = pygame.image.load('images/player/bob.png').convert_alpha()
self.rect.centery += 4
screen.blit(self.img, self.rect)
My folders are sorted like this:
Right now, I'd like that the trash.py is also a class with a function called "update". When that function is executed, obstacles (preferably in the form of an image) come out of the right side of the display, doing what I said above.
Any help is appreciated. Thanks in advance :)
You should make a sprite class group, but first you need to import the random module:
import random
Now you can create the Obstacle Sprite class, this is just an example:
class Obstacle(pygame.sprite.Sprite):
def __init__(self, pos): #pos is the starting position
super().__init__()
self.image = pygame.Surface((10,10)) # You can change this with pygame.image.load('...')
self.rect = self.image.get_rect(topleft = pos)
self.vel = random.randint(4,6) #random speed of the obstacle
def collisions(self, player): #collisions function
if self.rect.colliderect(player.rect):
print('collision!') #write what you want to happen
def movement(self): #the blocks are moving left
if self.rect.x > -100: #just putting some limits
self.rect.x -= self.vel
def update(self, player): #try to put all the other functions in the update one
self.collisions(player)
self.movement()
Once you created the class, you only need to create some objects of that class (every obstacle).
First we create a sprite group before of the while loop:
obstacles = pygame.sprite.Group()
Then, inside your main loop, you need a condition that generates the obstacles.
I've done something like this, but you really should change it:
if x < 10: #I created x before the while (x=0)
posx = SCREEN_WIDTH + 50 #the starting x will be 50 pixels right of the screen
posy = random.randint(0, SCREEN_HEIGHT) #Im setting this to random
obs = Obstacle((posx, posy))
obstacles.add(obs)
x +=1
This basically creates 9 obstacles when we start the game, with different random x speed and y location, and draw them on the screen. If you collide with one of the blocks, the code just prints "collision!" (see the Obstacle class). Hope it was helpful and I didn't miss something.
I'm making an exercise from python crash course book and I want raindrops to dissapear(remove) after they hit the bottom of the screen. Instead of dissapearing they stop moving. When I write the same code (the removing part) in other similar project it works. This is the part of the code that is not working like I want it to.
import pygame
from settings import Settings
import sys
from raindrop import Image
from pygame.sprite import Sprite
class Raindrop(Sprite):
def __init__(self):
# Initialize pygame and create screen
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode(
[self.settings.screen_width, self.settings.screen_height])
self.screen.fill(self.settings.bg)
# Create group of raindrops
self.raindrop = Image(self)
self.raindrops = pygame.sprite.Group()
self.raindrops.add(self.raindrop)
self.row_of_raindrops()
def run_game(self):
while True:
self.not_quit()
self._update_raindrop()
self.update_screen()
for drop in self.raindrops.copy():
if drop.rect.bottom >= 750:
self.raindrops.remove(drop)
def not_quit(self):
# Not close the pygame window
for event in pygame.event.get():
if event.type == pygame.QUIT or (
event.type == pygame.KEYDOWN and event.key == pygame.K_q):
sys.exit()
def row_of_raindrops(self):
raindrop = Image(self)
raindrop_width, raindrop_height = raindrop.rect.size
width_for_raidnrops = self.settings.screen_width - (2 * raindrop_width)
range_for_raindrops = width_for_raidnrops // (2 * raindrop_width)
raindrop_width, raindrop_height = raindrop.rect.size
height_for_raindrops = self.settings.screen_height - (3 * raindrop_height)
range_height_for_raindrops = height_for_raindrops // (2 * raindrop_height)
for raindrop_number in range(range_for_raindrops):
for column_numbers in range(range_height_for_raindrops):
raindrop = Image(self)
raindrop.x = raindrop_width + 2 * raindrop_width * raindrop_number
raindrop.rect.x = raindrop.x
raindrop.y = raindrop_height + 2 * raindrop_height * column_numbers
raindrop.rect.y = raindrop.y
self.raindrops.add(raindrop)
def _update_raindrop(self):
self.raindrops.update()
def update_screen(self):
self.raindrops.draw(self.screen)
pygame.display.flip()
if __name__ == "__main__":
x = Raindrop()
x.run_game()
import pygame
from pygame.sprite import Sprite
class Image(Sprite):
def __init__(self, x_game):
super().__init__()
self.screen = x_game.screen
self.settings = x_game.settings
# Load image and get it rect
self.image = pygame.image.load("C:/Users/lukas/Desktop/python/raindrops/raindrop.jpg")
self.image = pygame.transform.scale(self.image, (50, 50))
self.rect = self.image.get_rect()
# Raindrop position
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.y = float(self.rect.y)
def update(self):
self.y += self.settings.raindrop_speed
self.rect.y = self.y
The scene is redrawn in every frame. Therefore you need to clear the display in the application loop:
class Raindrop(Sprite):
# [...]
def update_screen(self):
self.screen.fill(self.settings.bg)
self.raindrops.draw(self.screen)
pygame.display.flip()
You need to call kill to remove Sprites. kill() remove the Sprite from all Groups and you can call this method while iterating through the Group.
Additionally use pygame.time.Clock to control the frames per second and thus the game speed.
The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time. See pygame.time.Clock.tick():
This method should be called once per frame.
class Raindrop(Sprite):
# [...]
def run_game(self):
clock = pygame.time.Clock()
while True:
clock.tick(100)
self.not_quit()
self._update_raindrop()
self.update_screen()
for drop in self.raindrops:
if drop.rect.bottom >= self.settings.screen_height:
drop.kill()
I'm trying to make the rows of sprites reappear at the top of the screen when they disappear off of the bottom. I'm able to make the sprites move downwards and off the screen but don't know to get them to reappear as soon as one row moves off. Here is the code I have so far:
import pygame
import sys
from pygame.sprite import Sprite
class Rain:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((1200, 800))
pygame.display.set_caption("Rain Drops")
self.bg_color = (100,100,100)
self.rain_drops = pygame.sprite.Group()
self._create_drop()
drop = Drop()
def run_rain(self):
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
sys.exit()
self.screen.fill(self.bg_color)
self._update_drops()
self.rain_drops.draw(self.screen)
pygame.display.flip()
def _create_drop(self):
drop = Drop()
drop.rect.x = -10
drop.rect.y = 4
drop_start_x = drop.rect.x
drop_start_y = drop.rect.y
for row_number in range(5):
for drop_number in range(20):
drop = Drop()
drop.rect.x = drop_start_x + 70 * drop_number
drop.rect.y = drop_start_y + 100 * row_number
self.rain_drops.add(drop)
def _update_drops(self):
self.rain_drops.update()
class Drop(Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('smaller_drop.bmp')
self.rect = self.image.get_rect()
def update(self):
self.rect.y += 1
rain = Rain()
rain.run_rain()
This sort of thing is best done in your sprite's .update() function. Check to see if the y co-ordinate of the sprites Rect is past the height of the window, and if-so move it back to the top.
However, you have hard-coded the window dimensions inside the display initialisation function. We need to know the window length in the update, so these need to be moved out to constant variables.
class Rain:
WINDOW_WIDTH = 1200
WINDOW_HEIGHT= 800
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((self.WINDOW_WIDTH, self.WINDOW_HEIGHT))
pygame.display.set_caption("Rain Drops")
Now in the DropSprite it's pretty easy to compare the position against the window height. if the position is greater than the window height,
class Drop(Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load('smaller_drop.bmp')
self.rect = self.image.get_rect()
def update(self):
self.rect.y += 1
# if we go off the bottom of the window, move back to top
if ( self.rect.y > Rain.WINDOW_HEIGHT ):
self.rect.y = 0
You may also want the drop to start just above the screen so it "falls" into view. In this case start it with an x of (0 - self.rect.height), since the x,y of a rect is the top-left co-ordinate. Moving it to 0 makes it suddenly appear at the top. Moving it to -height puts it just above the player's view.
I'm trying to make a grid of raindrops fall to the bottom and disappear once it passes the bottom of the screen. What is happening with the current code is one row will fall to the bottom and disappear. The whole grid will appear no problem if I want them to move horizontally. I don't think the code issue is in rainy_day.py, I believe the issue is in raindrop.py. I've posted both below.
raindrop.py:
import pygame
from pygame.sprite import Sprite
class RainDrop(Sprite):
"""A class to represent a single raindrop in the fleet."""
def __init__(self, rain_game):
"""Initialize the raindrop and set its starting position."""
super().__init__()
self.screen = rain_game.screen
self.raindrop_settings = rain_game.raindrop_settings
self.image = pygame.image.load('images/raindrop.bmp')
self.rect = self.image.get_rect()
self.rect.x = self.rect.width
self.rect.y = self.rect.height
self.y = float(self.rect.y)
def check_edges(self):
"""Return True if rain drop reaches the bottom of the screen screen."""
screen_rect = self.screen.get_rect()
if self.rect.top >= screen_rect.bottom:
return True
def update(self):
"""Move the raindrop down."""
self.y += self.raindrop_settings.rain_speed
self.rect.x = self.x
self.rect.y = self.y
rainy_day.py
import sys
import pygame
from raindrop_settings import Settings
from raindrop import RainDrop
class RainyDay:
"""Overall class to manage game assets and behavior."""
def __init__(self):
"""Initialize the game, and create game resources."""
pygame.init()
self.raindrop_settings = Settings()
self.screen = pygame.display.set_mode(
(self.raindrop_settings.screen_width, self.raindrop_settings.screen_height))
pygame.display.set_caption("Rainy Day")
self.raindrops = pygame.sprite.Group()
self._create_fleet()
def run_game(self):
"""Start the main loop for the game."""
while True:
self._check_events()
self._update_raindrops()
self._update_screen()
def _check_events(self):
"""Respond to keypresses and mouse events."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
def _update_raindrops(self):
"""
Check if the fleet is at an edge,
then update the positions of all raindrops in the fleet.
"""
self._check_fleet_edges()
self.raindrops.update()
def _create_fleet(self):
"""Create the fleet of raindrops."""
raindrop = RainDrop(self)
raindrop_width, raindrop_height = raindrop.rect.size
available_space_x = self.raindrop_settings.screen_width - (2 * raindrop_width)
number_raindrops_x = available_space_x // (2 * raindrop_width)
available_space_y = (self.raindrop_settings.screen_height -
(3 * raindrop_height) - raindrop_height)
number_rows = available_space_y // (2 * raindrop_height)
for row_number in range(number_rows):
for raindrop_number in range(number_raindrops_x):
self._create_raindrop(raindrop_number, row_number)
def _create_raindrop(self, raindrop_number, row_number):
"""Create a raindrop and place it in the row."""
raindrop = RainDrop(self)
raindrop_width, raindrop_height = raindrop.rect.size
raindrop.x = raindrop_width + 2 * raindrop_width * raindrop_number
raindrop.rect.x = raindrop.x
raindrop.rect.y = raindrop.rect.height + 2 * raindrop.rect.height * row_number
self.raindrops.add(raindrop)
def _check_fleet_edges(self):
"""Respond appropriately if the raindrops have reached the edge."""
for raindrop in self.raindrops.sprites():
if raindrop.check_edges():
self.raindrops.remove(raindrop)
def _update_screen(self):
"""Update images on the screen, and flip to the new screen."""
self.screen.fill(self.raindrop_settings.bg_color)
self.raindrops.draw(self.screen)
pygame.display.flip()
if __name__ == '__main__':
rainday = RainyDay()
rainday.run_game()
You have to assign position to raindrop.y instead of raindrop.rect.y (or to both)
raindrop.y = raindrop.rect.height + 2 * raindrop.rect.height * row_number
raindrop.rect.y = raindrop.y
because later you use self.y to change self.rect.y when you move raindrop
You assign different values to self.rect.y but all objects have the same self.y so later all rows are moved to the same row and it draws only one row.
I created a simple 2D game with python 2 and pygame where you have to avoid squares that are moving down. I created this class for the 'enemy':
class Enemy(pygame.sprite.Sprite):
def __init__(self, game):
self.groups = game.all_sprites
pygame.sprite.Sprite.__init__(self, self.groups)
self.game = game
self.image = pygame.Surface((TILESIZE + 10, TILESIZE + 10))
self.image.fill(ENEMY_COLOR)
self.rect = self.image.get_rect()
self.x = random.uniform(0, WIDTH - TILESIZE)
self.rect.x = self.x
self.y = 0
def update(self):
if self.rect.colliderect(self.game.player.rect):
self.game.deaths += 1
self.game.score = 0
self.game.run()
self.rect.y += (self.game.score + 500) / 50
Then, I have a thread that creates an instance of the enemy:
class Create_Enemy(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
while True:
while not game.game_paused:
time.sleep((game.score + 1) / game.speed)
Enemy(game)
Then in the draw method i just write self.all_sprites.draw()
The game is an infinite runner, and every frame the enemies move a few pixels down. The problem is that after a bit the game gets laggy since, when the blocks go offscreen, the sprites don't get deleted.
Is there a way to automatically delete the offscreen instances?
I tried the following but it just deletes all the enemies onscreen.
if self.rect.y >= WIDTH:
self.rect.kill()
I was thinking I could do something like game.all_sprites.pop(1) (the position 0 is the player) but I couldn't find a way to archive something similar ...
The enemies can remove themselfs from the game by calling self.kill() if their rect is no longer inside the screen, e.g.:
def update(self):
if self.rect.colliderect(self.game.player.rect):
self.game.restart() # reset score + count deaths
# we can simply use move_ip here to move the rect
self.rect.move_ip(0, (self.game.score + 500) / 100)
# check if we are outside the screen
if not self.game.screen.get_rect().contains(self.rect):
self.kill()
Also, instead of the thread, you can use pygame's event system to spawn your enemies. Here's a simple, incomplete but runnable example (note the comments):
import random
import pygame
TILESIZE = 32
WIDTH, HEIGHT = 800, 600
# a lot of colors a already defined in pygame
ENEMY_COLOR = pygame.color.THECOLORS['red']
PLAYER_COLOR = pygame.color.THECOLORS['yellow']
# this is the event we'll use for spawning new enemies
SPAWN = pygame.USEREVENT + 1
class Enemy(pygame.sprite.Sprite):
def __init__(self, game):
# we can use multiple groups at once.
# for now, we actually don't need to
# but we could do the collision handling with pygame.sprite.groupcollide()
# to check collisions between the enemies and the playerg group
pygame.sprite.Sprite.__init__(self, game.enemies, game.all)
self.game = game
self.image = pygame.Surface((TILESIZE + 10, TILESIZE + 10))
self.image.fill(ENEMY_COLOR)
# we can use named arguments to directly set some values of the rect
self.rect = self.image.get_rect(x=random.uniform(0, WIDTH - TILESIZE))
# we dont need self.x and self.y, since we have self.rect already
# which is used by pygame to get the position of a sprite
def update(self):
if self.rect.colliderect(self.game.player.rect):
self.game.restart() # reset score + count deaths
# we can simply use move_ip here to move the rect
self.rect.move_ip(0, (self.game.score + 500) / 100)
# check if we are outside the screen
if not self.game.screen.get_rect().contains(self.rect):
self.kill()
class Player(pygame.sprite.Sprite):
def __init__(self, game):
pygame.sprite.Sprite.__init__(self, game.all, game.playerg)
self.game = game
self.image = pygame.Surface((TILESIZE, TILESIZE))
self.image.fill(PLAYER_COLOR)
self.rect = self.image.get_rect(x=WIDTH/2 - TILESIZE/2, y=HEIGHT-TILESIZE*2)
def update(self):
# no nothing for now
pass
class Game(object):
def __init__(self):
# for now, we actually don't need mujtiple groups
# but we could do the collision handling with pygame.sprite.groupcollide()
# to check collisions between the enemies and the playerg group
self.enemies = pygame.sprite.Group()
self.all = pygame.sprite.Group()
self.playerg = pygame.sprite.GroupSingle()
self.running = True
self.score = 0
self.deaths = -1
self.clock = pygame.time.Clock()
def restart(self):
# here we set the timer to create the SPAWN event
# every 1000 - self.score * 2 milliseconds
pygame.time.set_timer(SPAWN, 1000 - self.score * 2)
self.score = 0
self.deaths += 1
def run(self):
self.restart()
self.player = Player(self)
self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
while self.running:
for e in pygame.event.get():
if e.type == pygame.QUIT:
self.running = False
# when the SPAWN event is fired, we create a new enemy
if e.type == SPAWN:
Enemy(self)
# draw and update everything
self.screen.fill(pygame.color.THECOLORS['grey'])
self.all.draw(self.screen)
self.all.update()
pygame.display.flip()
self.clock.tick(40)
if __name__ == '__main__':
Game().run()
Check the values of self.rect.y, WIDTH, maybe the method kill. It looks like self.rect.y is always greater or equal WIDTH, that's why it kills them all.