Why the image in pygame doesn't remove but stops moving? - python

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()

Related

Making rows of sprites re-appear at top of screen in pygame

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.

Python/Pygame: loop over a copy of Sprite list

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.

Making an entire grid of objects fall toward the bottom of the screen

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.

Pygame - sprite collision with sprite group

I have created two simple sprites in PyGame and one of them is an Umbrella, the other one is a rain drop.
The Raindrops are added into a sprite group called all_sprites. The Umbrella sprite has its own group called Umbrella_sprite
The raindrops are "falling" from top of the screen and if one of them touches the umbrella / collides with it.. the raindrop is supposed to be deleted. BUT instead of that specific raindrops all other are affected by this.
main file (rain.py)
#!/usr/bin/python
VERSION = "0.1"
import os, sys, raindrop
from os import path
try:
import pygame
from pygame.locals import *
except ImportError, err:
print 'Could not load module %s' % (err)
sys.exit(2)
# main variables
WIDTH, HEIGHT, FPS = 300, 300, 30
# initialize game
pygame.init()
screen = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption("Rain and Rain")
# background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((40,44,52))
# blitting
screen.blit(background,(0,0))
pygame.display.flip()
# clock for FPS settings
clock = pygame.time.Clock()
def main():
all_sprites = pygame.sprite.Group()
umbrella_sprite = pygame.sprite.Group()
# a function to create new drops
def newDrop():
nd = raindrop.Raindrop()
all_sprites.add(nd)
# creating 10 rain drops
for x in range(0,9): newDrop()
# variable for main loop
running = True
# init umbrella
umb = raindrop.Umbrella()
# all_sprites.add(umb)
umbrella_sprite.add(umb)
# event loop
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for enemy in all_sprites:
gets_hit = pygame.sprite.spritecollideany(umb, all_sprites)
if gets_hit:
all_sprites.remove(enemy)
screen.blit(background,(100,100))
# clear
all_sprites.clear(screen,background)
umbrella_sprite.clear(screen,background)
# update
all_sprites.update()
umbrella_sprite.update()
# draw
all_sprites.draw(screen)
umbrella_sprite.draw(screen)
# flip the table
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
main()
raindrop.py ( Raindrop() & Umbrella() )
import pygame
from pygame.locals import *
from os import path
from random import randint
from rain import HEIGHT, WIDTH
img_dir = path.join(path.dirname(__file__), 'img')
class Raindrop(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.width = randint(32, 64)
self.height = self.width + 33
self.image = pygame.image.load(path.join(img_dir, "raindrop.png")).convert_alpha()
self.image = pygame.transform.scale(self.image, (self.width, self.height))
self.speedy = randint(1, 15)
self.rect = self.image.get_rect()
self.rect.x = randint(0, 290)
self.rect.y = -self.height
def reset(self):
self.rect.y = -self.height
def update(self):
self.rect.y += self.speedy
if self.rect.y >= HEIGHT:
self.rect.y = -self.height
self.rect.x = randint(0, 290)
class Umbrella(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.width = 50
self.height = 50
self.image = pygame.image.load(path.join(img_dir,"umbrella.png")).convert_alpha()
self.image = pygame.transform.scale(self.image, (self.width, self.height))
self.speedx = 10
self.rect = self.image.get_rect()
self.rect.x = (WIDTH/2) - self.width
self.rect.y = (0.7 * HEIGHT)
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and self.rect.x > 0:
self.rect.x -= self.speedx
elif keys[pygame.K_RIGHT] and self.rect.x < (WIDTH - self.width):
self.rect.x += self.speedx
This is your problem:
for enemy in all_sprites:
gets_hit = pygame.sprite.spritecollideany(umb, all_sprites)
if gets_hit:
all_sprites.remove(enemy)
You're looping through the group, and if any sprite collides, deleting all of them.
You don't need to loop through the group - the collision functions take care of that. You just need to use the spritecollide function, which compares a sprite versus a group. That function will return a list of collisions, as well as using the DOKILL flag to delete them automatically:
gets_hit = pygame.sprite.spritecollide(umb, all_sprites, True)
spritecollideany checks if the sprite collides with any sprite in the group and returns this sprite, so gets_hit is a trueish value as long as the collided sprite in the group is not removed and the if gets_hit: block gets executed. That means the code in the for loop simply keeps deleting every sprite in the group that appears before the collided sprite is reached and removed. A simple fix would be to check if the hit sprite is the enemy: if enemy == gets_hit:, but the code would still be inefficient, because spritecollideany has to loop over the all_sprites group again and again inside of the for loop.
I recommend to use spritecollide instead of spritecollideany as well, since it's more efficient and just one line of code.

Python pygame - Deleting offscreen sprites

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.

Categories

Resources