How to ATL falling snow?

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
Lightworker
Regular
Posts: 104
Joined: Sun Dec 13, 2015 2:06 pm
Projects: Detroit.exe
Discord: Happiness+#1168
Contact:

How to ATL falling snow?

#1 Post by Lightworker »

I have the sprite, but how do I animate snowflakes falling down until the scene ends?

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: How to ATL falling snow?

#2 Post by xela »

You can look for SnowBlossom (without ATL), I don't really like how it's made but it works :)
Like what we're doing? Support us at:
Image

User avatar
xela
Lemma-Class Veteran
Posts: 2481
Joined: Sun Sep 18, 2011 10:13 am
Contact:

Re: How to ATL falling snow?

#3 Post by xela »

These are the two decent ways we have at the moment, one is guided manually by recalculating positions and is (I think) the original SnowBlossom recoded by Nyaatrap. The other is guided by ATL function which can be provided as one of the arguments to the UDD, that simplifies the code a bit and should allow faster modifications if desired:

Code: Select all

# Image definitions for snow particles:
image solid_snow_small = Transform(Solid("FFF", xysize=(3, 3)), rotate=45)
image solid_snow_normal = Transform(Solid("FFF", xysize=(5, 5)), rotate=45)
image solid_snow_large = Transform(Solid("FFF", xysize=(7, 7)), rotate=45)

# Definition for SnowBlossom2 particles by Nyaatrap (or at least used in his cool dress-up demo):
image snow = Fixed(
    SnowBlossom2("solid_snow_large", 50, xspeed=(20, 50), yspeed=(100, 200), start=100, fluttering=10),
    SnowBlossom2("solid_snow_normal", 75, xspeed=(15, 35), yspeed=(75, 150), start=100, fluttering=7),
    SnowBlossom2("solid_snow_small", 100, xspeed=(10, 25), yspeed=(50, 100), start=100, fluttering=5))

# Definition for Snowing UDD:
image snowing = Fixed(
    Snowing("solid_snow_large", speed=(5.0, 5.8), slow_start=(7, (0.6, 0.9))),
    Snowing("solid_snow_normal", speed=(4.8, 5.2), slow_start=(7, (0.8, 1.2))),
    Snowing("solid_snow_small", speed=(4.3, 4.7), slow_start=(5, (0.5, 0.7))))

# This bit is required for the SnowBlossom2 effect by Nyaatrap (or at least used in his cool dress-up demo)::
init -100 python:
    
    import random, math
    
    class SnowBlossomFactory2(renpy.python.NoRollback):
    
        rotate = False
        
        def __setstate__(self, state):
            self.start = 0
            vars(self).update(state)
            self.init()
    
        def __init__(self, image, count, xspeed, yspeed, border, start, fluttering, flutteringspeed, fast, rotate=False):
            self.image = renpy.easy.displayable(image)
            self.count = count 
            self.xspeed = xspeed
            self.yspeed = yspeed
            self.border = border        
            self.start = start
            self.fluttering = fluttering
            self.flutteringspeed = flutteringspeed
            self.fast = fast
            self.rotate = rotate
            self.init()
    
        def init(self):
            self.starts = [ random.uniform(0, self.start) for _i in xrange(0, self.count) ] # W0201
            self.starts.append(self.start)
            self.starts.sort()
        
        def create(self, particles, st):
    
            def ranged(n):
                if isinstance(n, tuple):
                    return random.uniform(n[0], n[1])
                else:
                    return n
    
            if not particles and self.fast:
                rv = [ ]
    
                for _i in xrange(0, self.count):
                    rv.append(SnowBlossomParticle(self.image,
                                                  ranged(self.xspeed),
                                                  ranged(self.yspeed),
                                                  self.border,
                                                  st,
                                                  self.fluttering,
                                                  self.flutteringspeed,
                                                  random.uniform(0, 100),
                                                  fast=True,
                                                  rotate=self.rotate))
                return rv
                
            
            if particles is None or len(particles) < self.count:
    
                # Check to see if we have a particle ready to start. If not,
                # don't start it.
                if particles and st < self.starts[len(particles)]:
                    return None
    
                return [ SnowBlossomParticle2(self.image,
                                             ranged(self.xspeed),
                                             ranged(self.yspeed),
                                             self.border,
                                             st,
                                             self.fluttering,
                                             self.flutteringspeed,
                                             random.uniform(0, 100),
                                             fast=False,
                                             rotate=self.rotate) ]
    
        def predict(self):
            return [ self.image ]
        
    
    class SnowBlossomParticle2(renpy.python.NoRollback):
    
        def __init__(self, image, xspeed, yspeed, border, start, fluttering, flutteringspeed, offset, fast, rotate):
    
            # safety.
            if yspeed == 0:
                yspeed = 1
    
            self.image = image
            self.xspeed = xspeed
            self.yspeed = yspeed
            self.border = border
            self.start = start
            self.fluttering = fluttering
            self.flutteringspeed = flutteringspeed
            self.offset = offset
            self.rotate = rotate
            self.angle = 0
            
            
            if not rotate:
                sh = renpy.config.screen_height
                sw = renpy.config.screen_width
            else:
                sw = renpy.config.screen_height
                sh = renpy.config.screen_width
                
                
            if self.yspeed > 0:
                self.ystart = -border
            else:
                self.ystart = sh + border
            
    
            travel_time = (2.0 * border + sh) / abs(yspeed)
    
            xdist = xspeed * travel_time
    
            x0 = min(-xdist, 0)
            x1 = max(sw + xdist, sw)
    
            self.xstart = random.uniform(x0, x1)
    
            if fast:
                self.ystart = random.uniform(-border, sh + border)
                self.xstart = random.uniform(0, sw)
    
        def update(self, st):
            to = st - self.start
            self.angle += self.flutteringspeed
            
    
            xpos = self.xstart + to * self.xspeed + math.sin(self.angle)*self.fluttering
            ypos = self.ystart + to * self.yspeed
    
            if not self.rotate:
                sh = renpy.config.screen_height
            else:
                sh = renpy.config.screen_width
            
            if ypos > sh + self.border:
                return None
    
            if ypos < -self.border:
                return None
    
            if not self.rotate:
                return int(xpos), int(ypos), to + self.offset, self.image
            else:
                return int(ypos), int(xpos), to + self.offset, self.image
        
                
    def SnowBlossom2(d,
                    count=10,
                    border=50,
                    xspeed=(20, 50),
                    yspeed=(100, 200),
                    start=0,
                    fluttering=0,
                    flutteringspeed=0.01,
                    fast=False,
                    horizontal=False):
    
        """
        :doc: sprites_extra
    
        The snowblossom effect moves multiple instances of a sprite up,
        down, left or right on the screen. When a sprite leaves the screen, it
        is returned to the start.
    
        `d`
            The displayable to use for the sprites.
    
        `border`
            The size of the border of the screen. The sprite is considered to be
            on the screen until it clears the border, ensuring that sprites do
            not disappear abruptly.
    
        `xspeed`, `yspeed`
            The speed at which the sprites move, in the horizontal and vertical
            directions, respectively. These can be a single number or a tuple of
            two numbers. In the latter case, each particle is assigned a random
            speed between the two numbers. The speeds can be positive or negative,
            as long as the second number in a tuple is larger than the first.
    
        `start`
            The delay, in seconds, before each particle is added. This can be
            allows the particles to start at the top of the screen, while not
            looking like a "wave" effect.
            
        'fluttering'
            The width of fluttering in pixel.
            
        'flutteringspeed'
            The speed of fluttering.
            
        `fast`
            If true, particles start in the center of the screen, rather than
            only at the edges.
    
        `horizontal`
            If true, particles appear on the left or right side of the screen,
            rather than the top or bottom.
            """
        
        # If going horizontal, swap the xspeed and the yspeed.
        if horizontal:
            xspeed, yspeed = yspeed, xspeed
    
        return Particles(SnowBlossomFactory2(image=d,
                                            count=count,
                                            border=border,
                                            xspeed=xspeed,
                                            yspeed=yspeed,
                                            start=start,
                                            fluttering=fluttering,
                                            flutteringspeed=flutteringspeed,
                                            fast=fast,
                                            rotate=horizontal))



# This bit is required for the Snowing effect:
transform particle(d, delay, startpos, endpos, speed):
    subpixel True
    pause delay
    d
    pos startpos
    linear speed pos endpos

init python:
    class Snowing(renpy.Displayable, NoRollback):
        def __init__(self, d, interval=(0.2, 0.3), start_pos=((-200, config.screen_width), 0), end_pos=({"offset": (100, 200)}, config.screen_height), speed=4.0, slow_start=False, transform=particle, **kwargs):
            """Creates a 'stream' of displayable...
            
            @params:
            -d: Anything that can shown in Ren'Py.
            -interval: Time to wait before adding a new particle. Expects a tuple with two floats.
            -start_pos: x, y starting positions. This expects a tuple of two elements containing either a tuple or an int each.
            -end_pos: x, y end positions. Same rule as above but in addition a dict can be used, in such a case:
                *empty dict will result in straight movement
                *a dict containing an "offset" key will offset the ending position by the value. Expects an int or a tuple of two ints. Default is (100, 200) and attempts to simulate a slight wind to the right (east).
            -speed: A time before particle eaches the end_pos. Expects float or a tuple of floats.
            -slow_start: If not the default False, this will expect a tuple of (time, (new_interval_min, new_interval_max)):
                *This will override the normal interval when the Displayable is first shown for the "time" seconds with the new_interval.
            -transform: ATL function to use for the particles.
                
            The idea behind the design is to enable large amounts of the same displayable guided by instructions from a specified ATL function to
            reach end_pos from start_pos in speed amount of seconds (randomized if needs be). For any rotation, "fluff" or any additional effects different ATL funcs with parallel can be used to achieve the desired effect.
            """
            super(Snowing, self).__init__(**kwargs)
            self.d = renpy.easy.displayable(d)
            self.interval = interval
            self.start_pos = start_pos
            self.end_pos = end_pos
            self.speed = speed
            self.slow_start = slow_start
            self.transform = transform
            
            self.next = 0
            self.shown = {}
        
        def render(self, width, height, st, at):
                
            rp = store.renpy
                
            if not st:
                self.next = 0
                self.shown = {}
                
            render = rp.Render(width, height)
            
            if self.next <= st:
                speed = rp.random.uniform(self.speed[0], self.speed[1])  if isinstance(self.speed, (list, tuple)) else self.speed
                    
                posx = self.start_pos[0]
                posx = rp.random.randint(posx[0], posx[1]) if isinstance(posx, (list, tuple)) else posx
                
                posy = self.start_pos[1]
                posy = rp.random.randint(posy[0], posy[1]) if isinstance(posy, (list, tuple)) else posy
                
                endposx = self.end_pos[0]
                if isinstance(endposx, dict):
                    offset = endposx.get("offset", 0)
                    endposx = posx + rp.random.randint(offset[0], offset[1]) if isinstance(offset, (list, tuple)) else offset
                else:
                    endposx = rp.random.randint(endposx[0], endposx[1]) if isinstance(endposx, (list, tuple)) else endposx
                
                endposy = self.end_pos[1]
                if isinstance(endposy, dict):
                    offset = endposy.get("offset", 0)
                    endposy = posy + randint.randint(offset[0], offset[1]) if isinstance(offset, (list, tuple)) else offset
                else:
                    endposy = rp.random.randint(endposy[0], endposy[1]) if isinstance(endposy, (list, tuple)) else endposy
                
                self.shown[st + speed] = self.transform(self.d, st, (posx, posy), (endposx, endposy), speed)
                if self.slow_start and st < self.slow_start[0]:
                    interval = self.slow_start[1]
                    self.next = st + rp.random.uniform(interval[0], interval[1])
                else:
                    self.next = st + rp.random.uniform(self.interval[0], self.interval[1])
            
            for d in self.shown.keys():
                if d < st:
                    del(self.shown[d])
                else:
                    d = self.shown[d]
                    render.blit(d.render(width, height, st, at), (d.xpos, d.ypos))
                    
            rp.redraw(self, 0)
            
            return render
            
        def visit(self):
            return [self.d]
            
label start:
    scene black
    menu:
        "Pick one"
        "Test SnowBlossom":
            show snow
        "Test Snowing":
            show snowing
    pause
Like what we're doing? Support us at:
Image

Lightworker
Regular
Posts: 104
Joined: Sun Dec 13, 2015 2:06 pm
Projects: Detroit.exe
Discord: Happiness+#1168
Contact:

Re: How to ATL falling snow?

#4 Post by Lightworker »

What am I doing wrong here?

Code: Select all

I'm sorry, but errors were detected in your script. Please correct the
errors listed below, and try again.


File "game/script.rpy", line 4: invalid syntax
    ("snowflake1.png", xysize= (3, 3), rotate=45)
                              ^

File "game/script.rpy", line 5: invalid syntax
    ("snowflake2.png", xysize= (5, 5), rotate=45)
                              ^

File "game/script.rpy", line 6: invalid syntax
    ("snowflake3.png", xysize= (7, 7), rotate=45)
                              ^

Ren'Py Version: Ren'Py 6.99.8.949
I want to use my snowflake sprites, but what am I doing wrong here?

Post Reply

Who is online

Users browsing this forum: Yone28