'Why is this code not making a grid of raindrops?

So, I need help with Pygame for Python 3.6. This is my first semester taking a programming class and, yeah, it's a homework assignment I need help with. I had the code working when my raindrops were static, but once I put movement in for some reason it only loads one row of them now. I need a grid of raindrops that move down the screen and loads a new row each time the bottom row disappears off the screen. And yes, there's probably an easier way than having all these modules, but that's how the book wants it done. Here's what I've got so far: raindropgame.py

import sys
import pygame
from pygame.sprite import Group
from settings import Settings
from raindrop import Raindrop
import game_functions as gf

def run_game():
    # Initialize game and create a screen object.
    pygame.init()
    settings = Settings()
    screen = pygame.display.set_mode((settings.screen_width, 
settings.screen_height))
    pygame.display.set_caption("13-3")

    # Make raindrop group.
    raindrops = Group()

    # Create the raindrops.
    gf.create_raindrops(settings, screen, raindrops)

    # Start the main loop for the game.
    while True:

        gf.update_raindrops(raindrops)
        gf.update_screen(settings, screen, raindrops)

        # Watch for keyboard and mouse events.
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

run_game()

settings.py

class Settings():
    """A class to store all settings."""

    def __init__(self):
        """Initialize the game's settings."""
        # Screen settings
        self.screen_width = 1200
        self.screen_height = 800
        self.bg_color = (0, 0, 255)

        # Raindrop settings
        self.raindrop_speed_factor = 1

game_functions.py

import sys
import pygame
from raindrop import Raindrop

def update_screen(settings, screen, raindrops):
    """Update images on the screen and flip to the new screen."""
    # Redraw the screen during each pass through the loop.
    screen.fill(settings.bg_color)
    raindrops.draw(screen)

    # Make the most recently drawn screen visible.
    pygame.display.flip()

def get_number_raindrops_x(settings, raindrop_width):
    """Determine the number of raindrops that fit in a row."""
    available_space_x = settings.screen_width - 2 * raindrop_width
    number_raindrops_x = int(available_space_x / (2 * raindrop_width))
    return number_raindrops_x

def get_number_rows(settings, raindrop_height):
    """Determine the number of rows of raindrops that fit on the screen."""
    available_space_y = settings.screen_height - (2 * raindrop_height)
    number_rows = int(available_space_y / (2 * raindrop_height))
    return number_rows

def create_raindrop(settings, screen, raindrops, raindrop_number, row_number):
    """Create a raindrop and place it in the row."""
    raindrop = Raindrop(settings, screen)
    raindrop_width = raindrop.rect.width
    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
    raindrops.add(raindrop)

def create_raindrops(settings, screen, raindrops):
    """Create a full sky of raindrops."""
    # Create a raindrop and find the number of raindrops in a row.
    raindrop = Raindrop(settings, screen)
    number_raindrops_x = get_number_raindrops_x(settings, raindrop.rect.width)
    number_rows = get_number_rows(settings, raindrop.rect.height)

    # Create the full sky of raindrops.
    for row_number in range(number_rows):
        for raindrop_number in range(number_raindrops_x):
            create_raindrop(settings, screen, raindrops, raindrop_number, row_number)

def update_raindrops(raindrops):
    """Update the positions of all raindrops in sky."""
    raindrops.update()

raindrop.py

import pygame
from pygame.sprite import Sprite

class Raindrop(Sprite):
    """A class to represent a single raindrop in the sky."""

    def __init__(self, settings, screen):
        """Initialize the raindrop and set its starting position."""
        super(Raindrop, self).__init__()
        self.screen = screen
        self.settings = settings

        # Load the raindrop image and set its rect attribute.
        self.image = pygame.image.load('images/raindrop.png')
        self.rect = self.image.get_rect()

        # Start each new raindrop near the top left of the screen.
        self.rect.x = self.rect.width
        self.rect.y = self.rect.height

        self.y = float(self.rect.y)

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

    def blitme(self):
        """Draw the raindrop at its current location."""
        self.screen.blit(self.image, self.rect)

Can anyone tell me what I've done wrong, and perhaps how to make it create a new row upon the bottom row leaving, 'cause I have no idea. :/



Solution 1:[1]

In create_raindrop(), you need to change:

raindrop.rect.y = raindrop.rect.height + 2 * raindrop.rect.height * row_number 

to:

raindrop.y = raindrop.rect.height + 2 * raindrop.rect.height * row_number
raindrop.rect.y = raindrop.y

Solution 2:[2]

In Raindrop.update() you have to check position and change it.

If it leaves screen on the bottom

  if self.rect.y >= self.setting.screen_height:

then you have to move it to the top

  self.rect.y = 0 

or

  self.rect.top = 0 

Or you can move event above top border

  self.rect.bottom = 0 

def update(self):
    """Move raindrop down."""
    self.y += self.settings.raindrop_speed_factor

    # if it leaves bottom border
    if self.y >= self.settings.screen_height:
        # then put above top border
        self.y = -self.rect.height

    self.rect.y = self.y

I would be good to create two rows above top row. At start they will be outside screen but when you move down then the they will fill top row before you move bottom row to the top.

# Create the full sky of raindrops.
for row_number in range(-2, number_rows):

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 gammazero
Solution 2