1

I am brand new in Python and I am trying to create grids of raindrops falling down the bottom of the screen and dissapearing after they reach the end (This is from Python Crash Course book).

I have managed to make the grids and drops falling, however the drops are leaving a trail on the screen and I am stuck at figuring out why.

I have checked many similar issues that I could found, but still could not find a resolution

Here is my code for the game and randrop instance:

import pygame
import sys
from settings import Settings
from raindrop import Raindrop

class RaindropsGame:
    """Overall class to manage game"""

    def __init__(self):
        pygame.init()
        self.settings = Settings()
        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
        self.raindrops = pygame.sprite.Group()
        self.screen_caption = pygame.display.set_caption("Raindrops")
        self.BackGround = pygame.image.load('images/clouds.bmp')

    def run_game(self):
        while True:
            self._update_screen()
            self._create_fleet()
            self.check_events()
            self._update_raindrops()



    def _update_screen(self):
        self.screen.blit(self.BackGround, (0, 0))
        self.raindrops.draw(self.screen)
        pygame.display.flip()

    def _update_raindrops(self):
        self._check_fleet_edges()
        self.raindrops.update()
        if not self.raindrops:
            self._create_fleet()


    def check_events(self):
        """Check keyboard key presses and mouse events."""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_q:
                    sys.exit()

    def _create_raindrop(self, raindrop_number, row_number):
        raindrop = Raindrop(self)
        raindrop_width, raindrop_height = raindrop.rect.size
        raindrop.x = 1 * raindrop_width * raindrop_number
        raindrop.rect.x = raindrop.x
        raindrop.y = raindrop.rect.height + raindrop.rect.height * row_number
        raindrop.rect.y = raindrop.y
        self.raindrops.add(raindrop)

    def _create_fleet(self):
        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 // (1 * raindrop_width)
        available_space_y = (self.settings.screen_height - 5 * 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 _check_fleet_edges(self):
        for raindrop in self.raindrops.sprites():
            if raindrop.check_edges():
                self.raindrops.remove(raindrop)

if __name__ in '__main__':
    ai = RaindropsGame()
    ai.run_game()

and

Instance of raindrop:

import pygame
from pygame.sprite import Sprite
from settings import Settings


class Raindrop(Sprite):
    """Class to manage raindrops"""

    def __init__(self, ai_game):
        super().__init__()
        self.settings = ai_game.settings
        self.screen = ai_game.screen
        self.image = pygame.image.load('images/raindrop.bmp').convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.y = self.rect.height
        self.y = float(self.rect.y)


    def check_edges(self):
        screen_rect = self.screen.get_rect()
        if self.rect.bottom >= screen_rect.bottom or self.rect.top <=0:
            return True


    def update(self):
        """Move the raindrop down."""
        self.y += self.settings.raindrop_speed
        self.rect.y = self.y

    def blitme(self):
        self.screen.blit(self.image, self.rect)

I would really appreciate any help with this, and if needed here is a screenshot of the issue I am seeing:

screenshot showing problem

martineau
  • 119,623
  • 25
  • 170
  • 301
Emm
  • 13
  • 3
  • 1
    you have to clear `screen` in every loop. You should see it in every tutorial. ie. `screen.fill( (0, 0, 0) )` to fill with black color. OR you have to draw bacground in every loop. – furas Feb 18 '21 at 00:21
  • use `print()` to see values in variables and which part of code is executed - maybe you don't draw background, or maybe you add new raindrops but you don't remove old one. You should only move existing raindrops but probably you create new one in new places. You could create all raindrops only once - at start - and later only move them. When they move to the bottom then move them to top. – furas Feb 18 '21 at 00:31

1 Answers1

0

I tested it and problem is because you create new Raindrops in every loop because you have self._create_fleet() inside loop. You have to use it before loop to create only first raindrops.

def run_game(self):
    clock = pygame.time.Clock()

    self._create_fleet()

    while True:
        self._update_screen()
        self.check_events()
        self._update_raindrops()

        clock.tick(25)  # the same speed 25 FPS (Frames Per Seconds) on old and new computers

EDIT:

My version with few other changes

  • I use Clock() to have always 25 FPS
  • I don't check if raindrop leave screen at the top
  • I check if top of raindrop (not bottom of raindrom) leaves screen so it looks better
  • I don't remove raindrops but move to the top

enter image description here

import os
import pygame

APP_HOME = os.path.dirname(os.path.abspath(__file__))


class Settings():
    screen_width = 800
    screen_height = 600
    raindrop_speed = 5

    
class Raindrop(pygame.sprite.Sprite):
    """Class to manage raindrops"""

    def __init__(self, ai_game):
        super().__init__()
        self.settings = ai_game.settings
        self.screen = ai_game.screen
        self.image = pygame.image.load(os.path.join(APP_HOME, 'images', 'raindrop.bmp')).convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.y = self.rect.height
        self.y = float(self.rect.y)


    def check_edges(self):
        screen_rect = self.screen.get_rect()
        #if self.rect.bottom >= screen_rect.bottom or self.rect.top <= 0:
        #    return True
        
        # check only bottom edge
        # and use `top` instead of `botton` check when full dropdown leaves screen
        if self.rect.top >= screen_rect.bottom:
            return True

    def update(self):
        """Move the raindrop down."""
        self.y += self.settings.raindrop_speed
        self.rect.y = self.y

    def blitme(self):
        self.screen.blit(self.image, self.rect)


class RaindropsGame:
    """Overall class to manage game"""

    def __init__(self):
        pygame.init()
        self.settings = Settings()
        self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
        self.raindrops = pygame.sprite.Group()
        self.screen_caption = pygame.display.set_caption("Raindrops")
        self.BackGround = pygame.image.load(os.path.join(APP_HOME, 'images', 'clouds.bmp'))

    def run_game(self):
        clock = pygame.time.Clock()
        self._create_fleet()
        while True:
            self._update_screen()
            self.check_events()
            self._update_raindrops()
            clock.tick(25)  # 25 FPS


    def _update_screen(self):
        self.screen.blit(self.BackGround, (0, 0))
        self.raindrops.draw(self.screen)
        pygame.display.flip()

    def _update_raindrops(self):
        self._check_fleet_edges()
        self.raindrops.update()
        if not self.raindrops:
            self._create_fleet()


    def check_events(self):
        """Check keyboard key presses and mouse events."""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                exit()
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_q:
                    exit()

    def _create_raindrop(self, column_number, row_number):
        raindrop = Raindrop(self)
        raindrop_width, raindrop_height = raindrop.rect.size
        
        raindrop.x = (1.1 * raindrop_width) * column_number
        raindrop.rect.x = raindrop.x
        
        raindrop.y = (1.2 * raindrop_height) * row_number
        raindrop.rect.y = raindrop.y
        
        self.raindrops.add(raindrop)

    def _create_fleet(self):
        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 // (1 * raindrop_width)
        
        available_space_y = self.settings.screen_height - (5 * raindrop_height)
        number_rows = available_space_y // (1 * 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 _check_fleet_edges(self):
        for raindrop in self.raindrops.sprites():
            if raindrop.check_edges():
                #self.raindrops.remove(raindrop)
                
                # move to top instead of removing
                raindrop.rect.bottom = 0
                raindrop.y = raindrop.rect.y
                
if __name__ in '__main__':
    ai = RaindropsGame()
    ai.run_game()

Images:

clouds.bmp

enter image description here

raindrop.bmp

enter image description here

furas
  • 134,197
  • 12
  • 106
  • 148
  • Thanks, that solved it. Should have figured that out earlier, I was creating the drops in a while loop. – Emm Feb 18 '21 at 11:52