# This page is out of date

You've reached a page on the Ren'Py wiki. Due to massive spam, the wiki hasn't been updated in over 5 years, and much of the information here is very out of date. We've kept it because some of it is of historic interest, but all the information relevant to modern versions of Ren'Py has been moved elsewhere.

Some places to look are:

# Creating a more realistic snow effect

This recipe lets you implement a more realistic snowfall effect than the one you can get using SnowBlossom. Code is pretty much commented, and should work with Ren'Py 6.9.0+ (it'll work in early versions of Ren'Py if you change line #116 to use im.FactorZoom instead of im.FactorScale)

``````init python:

#################################################################
# Here we use random module for some random stuffs (since we don't
# want Ren'Py saving the random number's we'll generate.
import random

# initialize random numbers
random.seed()

#################################################################
# Snow particles
# ----------------
def Snow(image, max_particles=50, speed=150, wind=100, xborder=(0,100), yborder=(50,400), **kwargs):
"""
This creates the snow effect. You should use this function instead of instancing
the SnowFactory directly (we'll, doesn't matter actually, but it saves typing if you're
using the default values =D)

@parm {image} image:
The image used as the snowflakes. This should always be a image file or an im object,
since we'll apply im transformations in it.

@parm {int} max_particles:
The maximum number of particles at once in the screen.

@parm {float} speed:
The base vertical speed of the particles. The higher the value, the faster particles will fall.
Values below 1 will be changed to 1

@parm {float} wind:
The max wind force that'll be applyed to the particles.

@parm {Tuple ({int} min, {int} max)} xborder:
The horizontal border range. A random value between those two will be applyed when creating particles.

@parm {Tuple ({int} min, {int} max)} yborder:
The vertical border range. A random value between those two will be applyed when creating particles.
The higher the values, the fartest from the screen they will be created.
"""
return Particles(SnowFactory(image, max_particles, speed, wind, xborder, yborder, **kwargs))

# ---------------------------------------------------------------
class SnowFactory(object):
"""
The factory that creates the particles we use in the snow effect.
"""
def __init__(self, image, max_particles, speed, wind, xborder, yborder, **kwargs):
"""
Initialize the factory. Parameters are the same as the Snow function.
"""
# the maximum number of particles we can have on screen at once
self.max_particles = max_particles

# the particle's speed
self.speed = speed

# the wind's speed
self.wind = wind

# the horizontal/vertical range to create particles
self.xborder = xborder
self.yborder = yborder

# the maximum depth of the screen. Higher values lead to more varying particles size,
# but it also uses more memory. Default value is 10 and it should be okay for most
# games, since particles sizes are calculated as percentage of this value.
self.depth = kwargs.get("depth", 10)

# initialize the images
self.image = self.image_init(image)

def create(self, particles, st):
"""
This is internally called every frame by the Particles object to create new particles.
We'll just create new particles if the number of particles on the screen is
lower than the max number of particles we can have.
"""
# if we can create a new particle...
if particles is None or len(particles) < self.max_particles:

# generate a random depth for the particle
depth = random.randint(1, self.depth)

# We expect that particles falling far from the screen will move slowly than those
# that are falling near the screen. So we change the speed of particles based on
# its depth =D
depth_speed = 1.5-depth/(self.depth+0.0)

return [ SnowParticle(self.image[depth-1],      # the image used by the particle
random.uniform(-self.wind, self.wind)*depth_speed,  # wind's force
self.speed*depth_speed,  # the vertical speed of the particle
random.randint(self.xborder, self.xborder), # horizontal border
random.randint(self.yborder, self.yborder), # vertical border
) ]

def image_init(self, image):
"""
This is called internally to initialize the images.
will create a list of images with different sizes, so we
can predict them all and use the cached versions to make it more memory efficient.
"""
rv = [ ]

# generate the array of images for each possible depth value.
for depth in range(self.depth):
# Resize and adjust the alpha value based on the depth of the image
p = 1.1 - depth/(self.depth+0.0)
if p > 1:
p = 1.0

rv.append( im.FactorScale( im.Alpha(image, p), p ) )

return rv

def predict(self):
"""
This is called internally by the Particles object to predict the images the particles
are using. It's expected to return a list of images to predict.
"""
return self.image

# ---------------------------------------------------------------
class SnowParticle(object):
"""
Represents every particle in the screen.
"""
def __init__(self, image, wind, speed, xborder, yborder):
"""
Initializes the snow particle. This is called automatically when the object is created.
"""

# The image used by this particle
self.image = image

# For safety (and since we don't have snow going from the floor to the sky o.o)
# if the vertical speed of the particle is lower than 1, we use 1.
# This prevents the particles of being stuck in the screen forever and not falling at all.
if speed <= 0:
speed = 1

# wind's speed
self.wind = wind

# particle's speed
self.speed = speed

# The last time when this particle was updated (used to calculate the unexpected delay
# between updates, aka lag)
self.oldst = None

# the horizontal/vertical positions of this particle
self.xpos = random.uniform(0-xborder, renpy.config.screen_width+xborder)
self.ypos = -yborder

def update(self, st):
"""
Called internally in every frame to update the particle.
"""

# calculate lag
if self.oldst is None:
self.oldst = st

lag = st - self.oldst
self.oldst = st

# update the position
self.xpos += lag * self.wind
self.ypos += lag * self.speed

# verify if the particle went out of the screen so we can destroy it.
if self.ypos > renpy.config.screen_height or\
(self.wind< 0 and self.xpos < 0) or (self.wind > 0 and self.xpos > renpy.config.screen_width):
return None

# returns the particle as a Tuple (xpos, ypos, time, image)
# since it expects horizontal and vertical positions to be integers, we have to convert
# it (internal positions use float for smooth movements =D)
return int(self.xpos), int(self.ypos), st, self.image``````

## Example

``````init:
image snow = Snow("snowflake.png")

label start:
show snow
"Hey, look! It's snowing."``````