[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] Rain Generator



Kai Kuehne wrote:
Looks similar to http://www.scriptedfun.com/pygame-starfield-rain/
but it's cool.


Yep, that's the site that inspired me. Mine is a different solution to the same problem, but the raindrops look similar, that's true. If you look closely, his are leaving trails behind - mine isn't, it's using an entire raindrop with a tail that 'peters out.'


Luke noticed the dirty rectangle code wasn't working quite correctly, so I've modified the code and reposted it at http://incarnation.danbo.com/ubbthreads/modifypost.php .
I also added some features, such as:


* Speeding up big raindrops so they appear closer
* Changing the translucency of raindrops so smaller ones are dimmer and look farther away
* Optimized the particle engine so the raindrops are created once when the generator is created (as opposed to being continually created and destroyed)
* Put the drops on a timer so they move at the desired speed regardless of how often you call the rain generator
* Made the left and right arrow keys decrease/increase the rain speed.


plus miscellaneous enhancements.

Or - read it here if you wish:

'''
Rain.py - a rain generator by Kamilche.

This rain generator creates the particles once when the generator is created, to save CPU time.
The bigger the raindrop, the faster it moves. The smaller the raindrop, the dimmer it is.


Parameters are:
screen = surface to draw rain on
height = height of raindrops (will vary from 40% to 100% of size specified)
speed = speed of raindrops (how fast they fall)
color = color of raindrops, including alpha level (opacity)
numdrops = number of raindrops


'''

import pygame
import random
import time

SCREENSIZE = 640, 480

class Rain(object):
    ' Rain generator'

def __init__(self, screen, height = 160, speed = 3, color = (180, 215, 228, 255), numdrops = 10):
'Create and reuse raindrop particles'
self.screen = screen
self.drops = []
self.height = height
self.speed = speed
self.color = color
self.numdrops = numdrops


for i in range(self.numdrops):
# Randomize the size of the raindrop.
raindropscale = random.randint(40, 100) / 100.0
w, h = 3, int(raindropscale * self.height)
# The bigger the raindrop, the faster it moves.
velocity = raindropscale * self.speed/10.0
pic = pygame.Surface((w, h), pygame.SRCALPHA, 32).convert_alpha()
colorinterval = float(self.color[3] * raindropscale)/h
r, g, b = self.color[:3]
for j in range(h):
# The smaller the raindrop, the dimmer it is.
a = int(colorinterval * j)
pic.fill( (r, g, b, a), (1, j, w-2, 1) )
pygame.draw.circle(pic, (r, g, b, a), (1, h-2), 2)
drop = Rain.Drop(self.speed, velocity, pic)
self.drops.append(drop)


    def Timer(self, now):
        ' Render the rain'
        dirtyrects = []
        for drop in self.drops:
            r = drop.Render(self.screen, now)
            if r:
                i = r.collidelist(dirtyrects)
                if i > -1:
                    dirtyrects[i].union_ip(r)
                else:
                    dirtyrects.append(r)
        return dirtyrects

    def AdjustSpeed(self, adj):
        newspeed = self.speed + adj
        newspeed = max(1, newspeed)
        newspeed = min(100, newspeed)
        self.speed = newspeed
        for drop in self.drops:
            drop.SetSpeed(newspeed)
        print 'Rain speed: %d' % newspeed


class Drop(object): ' Rain drop used by rain generator' nexttime = 0 # The next time the raindrop will draw interval = .01 # How frequently the raindrop should draw

def __init__(self, speed, scale, pic):
' Initialize the rain drop'
self.speed = speed
self.scale = scale
self.pic = pic
self.size = pic.get_size()
self.SetSpeed(speed)
self.pos = [random.random() * SCREENSIZE[0], -random.randint(-SCREENSIZE[1], SCREENSIZE[1])]
self.currentspeed = speed


        def SetSpeed(self, speed):
            ' Speed up or slow down the drop'
            self.speed = speed
            self.velocity = self.scale * self.speed/10.0

def Reset(self):
' Restart the drop at the top of the screen.'
self.pos = [random.random() * SCREENSIZE[0], -random.random() * self.size[1] - self.size[1]]
self.currentspeed = self.speed


def Render(self, screen, now):
' Draw the rain drop'
if now < self.nexttime:
return None
self.nexttime = now + self.interval
oldrect = pygame.Rect(self.pos[0], self.pos[1], self.size[0], self.size[1]+self.currentspeed)
self.pos[1] += self.currentspeed
newrect = pygame.Rect(self.pos[0], self.pos[1], self.size[0], self.size[1])
r = oldrect.union(newrect)
screen.blit(self.pic, self.pos)
self.currentspeed += self.velocity
if self.pos[1] > SCREENSIZE[1]:
self.Reset()
return r


def main():
    # Initialize pygame
    pygame.init()
    pygame.key.set_repeat(500, 30)
    screen = pygame.display.set_mode(SCREENSIZE, 0, 32)

    # Create rain generator
    rain = Rain(screen)
    print 'right arrow to increase speed, left arrow to decrease speed.'

    # Main loop
    quitgame = 0
    while not quitgame:

        # Emulate CPU usage.
        # Commenting this out will no longer matter,
        # as the raindrops update on a timer.
        time.sleep(.01)

        # Draw rain
        dirtyrects = rain.Timer(time.time())

        # Update the screen for the dirty rectangles only
        pygame.display.update(dirtyrects)

        # Fill the background with the dirty rectangles only
        for r in dirtyrects:
            screen.fill((0, 0, 0), r)

        # Look for user events
        pygame.event.pump()
        for e in pygame.event.get():
            if e.type in [pygame.QUIT, pygame.MOUSEBUTTONDOWN]:
                quitgame = 1
                break
            elif e.type == pygame.KEYDOWN:
                if e.key == 27:
                    quitgame = 1
                    break
                elif e.key in [pygame.K_LEFT, pygame.K_UP]:
                    rain.AdjustSpeed(-1)
                elif e.key in [pygame.K_RIGHT, pygame.K_DOWN]:
                    rain.AdjustSpeed(1)

    # Terminate pygame
    pygame.quit()

if __name__ == "__main__":
    main()