Tutorial: Create A Rain Effect

This tutorial will attempt to explain how to create a rain effect, using multiple vents inside of a VentGroup and onDeath execution for vents. If you have any questions at the end, feel free to ask in a comment.

Things you’ll need before doing this tutorial:

    - A glow texture (I used “glow-1.png” from CBTextures)

    - A cloud texture (I used “cloud-1.png” from CBTextures)

    - A copy of CBEffects, put into your root folder and require()-ed at the top of your code

    - Basic knowledge of using CBEffects

    - The following configuration values for your project:

        FPS - 60

        Screen Width - 768

        Screen Height - 1024

        Orientation - Landscape (either direction)

To begin with, you’ll need to create a VentGroup. We’ll start with a single vent - the rain - to make things easier.

[lua]

local heavy_rain=CBE.VentGroup{

    {

        preset=“rain”,

        title=“rain”, – Make sure you title your vents!

        build=function()

            return display.newImageRect(“glow-1.png”, 10, 80)

        end

    }

}

heavy_rain:start(“rain”)

[/lua]

Reload the Corona simulator, and there you have it. Some rain! Because of the nature of the “rain” preset, there is a bit of motion variation - you should notice some particles going a bit farther to the left or right than others. We don’t want that. It’s easily set, using the “angles” parameter in the physics table.

[lua]

physics={

    autoAngle=false,

    angles={260}

}

[/lua]

Now you can stick that bit into your main vent parameters to get this:

[lua]

local heavy_rain=CBE.VentGroup{

    {

        preset=“rain”,

        title=“rain”, – Title all vents, every time!

        build=function()

            return display.newImageRect(“glow-1.png”, 10, 80)

        end,

        physics={

            autoAngle=false,

            angles={260}

        }

    }

}

[/lua]

Note the “autoAngle” parameter. If you set that to false, the angles in the angle table are single values (like our angle - 260). Otherwise, CBEffects automatically creates an angle range. You can use that to get things like the angles from 0-180, for example, which would be a pain to write out manually.

If you reload the simulator at this time, you’ll notice that now all the raindrops are falling in a uniform direction. Double the amount of them with this:

[lua]

perEmit=2

[/lua]

Now we have a uniform rainfall, but it stops at that. You could make do with that (if you’re doing something like “rain through a window” for your app), but why not enhance it some more? What we’ll do is add a “plink” effect at the end of each raindrop.

Firstly, you’ll never see the plink effect if you can’t see the particles get removed. So let’s set the life delay to a bit higher, and the life span to a bit lower, so that particles die right at the edge of the screen. Playing around a bit with the numbers, I got 1000 and 100.

[lua]

lifeStart=1000,

lifeSpan=100

[/lua]

Add that into your vent parameters somewhere, reload, and you’ll see particles die near the bottom edge of the screen. We want a bit of dying position variation, so make them appear in a randomized location from the top. That way, particles have the same life span (there’s an issue with using variated life spans that I’m currently looking into), but some are in a different position, so they die above others. For that, we’ll need to set the position type, and some positioning parameters.

[lua]

positionType=“inRect” – Particles will appear inside a rectangle

rectLeft=50, – A little over to accommodate the movement angle 

rectTop=-150,

rectWidth=1024,

rectHeight=150

[/lua]

Now particles will appear inside a 1024x150 px rectangle that’s above the screen. You’ll have a 150 px variation for the dying position for each particle, if you reload and check.

They’re still a little slow, though. So a simple speeding up (doubling the velocity and halving the life span and life start delay) should do it.

[lua]

{

    preset=“rain”,

    title=“rain”,

    positionType=“inRect”,

    rectLeft=50,

    rectTop=-150,

    rectWidth=1024,

    rectHeight=150,

    perEmit=2,

    build=function()

        return display.newImageRect(“glow-1.png”, 10, 80)

    end,

    lifeStart=500,

    lifeSpan=50,

    physics={

        autoAngle=false,

        angles={260},

        velocity=20 – Rain preset velocity is 10

    }

}

[/lua]

To catch up, here should be your complete code so far:

[lua]

local CBE=require(“CBEffects.Library”)

local heavy_rain=CBE.VentGroup{

    {

        preset=“rain”,

        title=“rain”,

        positionType=“inRect”,

        rectLeft=50,

        rectTop=-150,

        rectWidth=1024,

        rectHeight=150,

        perEmit=2,

        build=function()

            return display.newImageRect(“glow-1.png”, 10, 80)

        end,

        lifeStart=500,

        lifeSpan=50,

        physics={

            autoAngle=false,

            angles={260},

            velocity=20

        }

    }

}

heavy_rain:start(“rain”)

[/lua]

Now we’ve got our rainfall, so let’s add the plink part. Easily done with another vent. When you’ve gotten familiar with CBEffects (as I, the author, have!), it’s easier to find what preset to use. For this, we’ll use the “rain” preset (not necessarily the best one, but good enough for this tutorial). We’ll call this vent “plink”.

[lua]

local heavy_rain=CBE.VentGroup{

    {

        preset=“rain”,

        title=“plink”

    },

    {rain} – I’ve collapsed the rain vent for ease of viewing

}

[/lua]

We want the plink to occur at the end of each raindrop, so we’ll go into the rain vent parameters and add a trigger for it.

[lua]

onDeath=function(particle, vent)

    heavy_rain:translate(“plink”, particle.x, particle.y) – The “translate” command moves a vent to a specific X,Y position

    heavy_rain:emit(“plink”)

end

[/lua]

Notice how now we have some code that says, “when a particle dies, move the plink effect to its position and emit a plink”. Pretty cool. However, if you reload the simulator, you’ll get the message “attempt to index global ‘heavy_rain’”. That can be remedied by pre-declaring the storm vent group at the top of your code:

[lua]

local heavy_rain

heavy_rain=CBE.VentGroup{…}

[/lua]

Now reload it, and you’ll get rain that triggers more rain when it dies. Not very impressive.

That means we’ll have to edit the plink to stop being rain and start being a plink. Playing around with the parameters, I got this for the plink effect:

[lua]

preset=“rain”,

title=“plink”,

positionType=“atPoint”, – Appear directly at the vent’s X,Y position

build=function()

    return display.newImageRect(“glow-1.png”, 10, 10)

end,

alpha=0.3, – Remain at constant alpha

startAlpha=0.3,

endAlpha=0.3,

lifeStart=0, – No delay before life span

fadeInTime=0, – No fade in time

lifeSpan=100,

physics={

    sizeX=1.5,

    velocity=0 – Don’t move

}

[/lua]

Use that as the plink vent parameters, reload, and you should have a nice little “plink” at the end of each raindrop.

Catch up with the code:

[lua]

local CBE=require(“CBEffects.Library”)

local heavy_rain

heavy_rain=CBE.VentGroup{

    {

        preset=“rain”,

        title=“plink”,

        positionType=“atPoint”,

        build=function()

            return display.newImageRect(“glow-1.png”, 10, 10)

        end,

        alpha=0.3,

        startAlpha=0.3,

        endAlpha=0.3,

        lifeStart=0,

        fadeInTime=0,

        lifeSpan=100,

        physics={

            sizeX=1.5,

            velocity=0

        }

    },

    {

        preset=“rain”,

        title=“rain”,

        positionType=“inRect”,

        rectLeft=50,

        rectTop=-150,

        rectWidth=1024,

        rectHeight=150,

        perEmit=2,

        build=function()

            return display.newImageRect(“glow-1.png”, 10, 80)

        end,

        onDeath=function(particle, vent)

            heavy_rain:translate(“plink”, particle.x, particle.y)

            heavy_rain:emit(“plink”)

        end,

        lifeStart=500,

        lifeSpan=50,

        physics={

            autoAngle=false,

            angles={260},

            velocity=20

        }

    }

}

heavy_rain:start(“rain”)

[/lua]

Now we have a beautiful rainfall, but if you really want heavy rain, it needs clouds.

Make a new vent and title it “clouds”. We’ll use the “fluid” preset for it. Here’s the cloud configuration I came up with:

[lua]

{

    preset=“fluid”,

    title=“clouds”,

    color={

        {70}, – Dark grays

        {60},

        {50}

    },

    positionType=“inRect”,

    rectLeft=0,

    rectTop=0,

    rectWidth=1024,

    rectHeight=60, – Create a blanket of clouds at the top of the screen

    fadeInTime=500, – Low fade in time and life span

    lifeSpan=500,

    lifeStart=1000, – High life start delay

    build=function()

        local size=math.random(480, 512) – Particles are quite large

        return display.newImageRect(“cloud-1.png”, size, size)

    end,

    propertyTable={

        blendMode=“normal” – Normal fluid preset uses additive blending via the property table

    },

    physics={

        velocity=1.1, – Some minor velocity in left-right direction

        autoAngle=false,

        angles={180, 0}

    }

}

[/lua]

Now, at the bottom, where you start the rain vent, add in a “clouds” argument:

[lua]

heavy_rain:start(“rain”, “clouds”)

[/lua]

That starts both the rain and the clouds.

Reload and you’ll have a thick bank of dark gray clouds up at the top.

Full code:

[lua]

local CBE=require(“CBEffects.Library”)

local heavy_rain

heavy_rain=CBE.VentGroup{

    {

        preset=“rain”,

        title=“plink”,

        positionType=“atPoint”,

        build=function()

            return display.newImageRect(“glow-1.png”, 10, 10)

        end,

        alpha=0.3,

        startAlpha=0.3,

        endAlpha=0.3,

        lifeStart=0,

        fadeInTime=0,

        lifeSpan=100,

        physics={

            sizeX=1.5,

            velocity=0

        }

    },

    {

        preset=“rain”,

        title=“rain”,

        positionType=“inRect”,

        rectLeft=50,

        rectTop=-150,

        rectWidth=1024,

        rectHeight=150,

        perEmit=2,

        build=function()

            return display.newImageRect(“glow-1.png”, 10, 80)

        end,

        onDeath=function(particle, vent)

            heavy_rain:translate(“plink”, particle.x, particle.y)

            heavy_rain:emit(“plink”)

        end,

        lifeStart=500,

        lifeSpan=50,

        physics={

            autoAngle=false,

            angles={260},

            velocity=20

        }

    },

    {

        preset=“fluid”,

        title=“clouds”,

        color={

            {70}, – Dark grays

            {60},

            {50}

        },

        positionType=“inRect”,

        rectLeft=0,

        rectTop=0,

        rectWidth=1024,

        rectHeight=60, – Create a blanket of clouds at the top of the screen

        fadeInTime=500, – Low fade in time and life span

        lifeSpan=500,

        lifeStart=1000, – High life start delay

        build=function()

            local size=math.random(480, 512) – Particles are quite large

            return display.newImageRect(“cloud-1.png”, size, size)

        end,

        propertyTable={

            blendMode=“normal” – Normal fluid preset uses additive blending via the property table

        },

        physics={

            velocity=1.1, – Some minor velocity in left-right direction

            autoAngle=false,

            angles={180, 0}

        }

    }

}

heavy_rain:start(“rain”, “clouds”)

[/lua]

Well, now we’re at the end of this tutorial. I hope it’s taught you something about creating VentGroups. Play around with the parameters, edit this or that - if you create a really cool effect, please post in the CBEffects forum. It’s always cool to see what the users have created!

Caleb

April 16, 2013

Wow thank you Caleb, that is exactly what I was looking for!

Just a question for you with something I’m struggling now, what if I’m using the Ultimate LUA config file (for multiple devices), which variables will be affected? and how? can I change some of them (for the rect, the lifeSpan, lifeStart, velocity, etc) combined with system variables like display.contentWidth, etc? If I use retina for the iPad, should I change also glow-1.png size?

The thing is I’m working with your example and everytime I change the device, I see the droplets too high or outside the screen, etc.  

Thank you again for the tutorial, much clearer now! 

I use Corona’s built-in dynamic content scaling, which makes it render correctly across all devices. If you’re using the ultimate config.lua file (based on Rob Miracle’s blog post, I believe?), it’ll set the width and height according to the device. Off the top of my head, I’d say values that  change the position or size of a particle should be changed. So things like velocity, particle width/height, damping, gravity, position type values, etc. should be changed if you’re using auto-adjusting configuration. If you wanted, you could even set them to device-sensitive values using things like display.contentWidth and such and they’d automatically resize.

The glow-1.png file (along with the smoke-1.png file) was simply used as an example; it’s not all that high-resolution, I just used it for this tutorial because it was easy and fast to get to :slight_smile: If you’re going to be deploying to retina devices, yes, you should double the size of all images and save as an “@2x” version (or, as I do, just scale down a doubled image for normal devices).

C

Wow thank you Caleb, that is exactly what I was looking for!

Just a question for you with something I’m struggling now, what if I’m using the Ultimate LUA config file (for multiple devices), which variables will be affected? and how? can I change some of them (for the rect, the lifeSpan, lifeStart, velocity, etc) combined with system variables like display.contentWidth, etc? If I use retina for the iPad, should I change also glow-1.png size?

The thing is I’m working with your example and everytime I change the device, I see the droplets too high or outside the screen, etc.  

Thank you again for the tutorial, much clearer now! 

I use Corona’s built-in dynamic content scaling, which makes it render correctly across all devices. If you’re using the ultimate config.lua file (based on Rob Miracle’s blog post, I believe?), it’ll set the width and height according to the device. Off the top of my head, I’d say values that  change the position or size of a particle should be changed. So things like velocity, particle width/height, damping, gravity, position type values, etc. should be changed if you’re using auto-adjusting configuration. If you wanted, you could even set them to device-sensitive values using things like display.contentWidth and such and they’d automatically resize.

The glow-1.png file (along with the smoke-1.png file) was simply used as an example; it’s not all that high-resolution, I just used it for this tutorial because it was easy and fast to get to :slight_smile: If you’re going to be deploying to retina devices, yes, you should double the size of all images and save as an “@2x” version (or, as I do, just scale down a doubled image for normal devices).

C

Thank you Celeb, this tutorial is great. I’m new to CBE and it helps me understand a lots. Some functions have changed their name but I can manage to find the right one.

Glad you enjoyed it!

  • Caleb

Thank you Celeb, this tutorial is great. I’m new to CBE and it helps me understand a lots. Some functions have changed their name but I can manage to find the right one.

Glad you enjoyed it!

  • Caleb