Creating snow with Flint

Introduction

This first tutorial will introduce the basics of Flint through a project to create a snow effect. Our effect will be snow flakes drifting down the screen. Each snow flake will be a particle in our particle system. If you want to see the final effect, scroll to the bottom of the page. This tutorial works with version 4.0.0 of the Flint library. This example creates a 2D particle effect. For 3D particle effects the principles are the same but you use a 3D emitter and 3D behaviours.

Because this is an introduction, I’ll work from the assumption that the user is writing frame scripts in Flash CS3. At the end I’ll summarize how to modify the script to use it within class based development in Flash CS3 or Flex.

Getting started

First create a new Flash file (Actionscript 3). Set the stage size to 500 pixels wide and 400 pixels high, set the frame rate to 30 fps and set the background color to black.

We’ll not need to add anything other than code to the flash movie but you do need to make sure that the flint library is in the classpath for the project. See the Flash help for info on how to do this.

Open the actions window to create a script in the first frame of the movie. Our script will need a lot of imports so I’ll just get them all out of the way at the beginning here. So place the following code at the start of the script so that Flash can find all the class files it needs from the Flint library.

import flash.geom.Point;
import org.flintparticles.common.counters.*;
import org.flintparticles.common.displayObjects.RadialDot;
import org.flintparticles.common.initializers.*;
import org.flintparticles.twoD.actions.*;
import org.flintparticles.twoD.emitters.Emitter2D;
import org.flintparticles.twoD.initializers.*;
import org.flintparticles.twoD.renderers.*;
import org.flintparticles.twoD.zones.*;

Now the code…

The Emitter

In Flint, particles are created by emitters. We don’t create the particles, we create the emitter and the emitter creates the particles. So lets create an emitter.

var emitter:Emitter2D = new Emitter2D();

This creates an emitter for creating 2D particle effects. We’ll configure the emitter later, but first…

The Renderer

Our emitter needs a renderer. A renderer draws the particles on the screen. Flint contains different types of renderers. The different types of renderers use different techniques to draw the particles. In this tutorial we’ll use a DisplayObjectRenderer. The renderer is itself a DisplayObject so we can simply add it to the display list just like any other display object. So lets create our renderer and place it on the stage.

var renderer:DisplayObjectRenderer = new DisplayObjectRenderer();
addChild( renderer );

If we want to reposition the renderer we can do so by simply setting its x and y properties, as with any other display object. For this project, we’ll leave it at the default position – 0, 0.

We add the emitter to the renderer so that the renderer will draw the emitter’s particles.

renderer.addEmitter( emitter );

You can add any number of emitters to one renderer to create complex particle effects, and you can also add an emitter to any number of renderers to create multiple views of the same particle effect.

The Counter

Our emitter needs to know how often to create a new particle. To do this we add a counter to the emitter. Flint has a number of counters, for creating a steady stream of particles, a single blast of particles, and various other types of activity. We want to keep creating new snowflakes so we’ll use a Steady counter to create a steady supply of snowflakes. We add the counter to the emitter using the counter property of the emitter, like this…

emitter.counter = new Steady( 100 );

The 100 indicates we want our emitter to create one hundred particles every second.

Particle Initializers

We have to tell the emitter how to set up our particles. The sort of things we want to set up are the particle’s position, velocity, image and color. To set up the particles we add initializers to the emitter. Each initializer sets some property or properties of the particle.

The first thing we’ll do is tell the emitter what the particles should look like. We’re using a DisplayObjectRenderer so our renderer will need a new DisplayObject for each particle. We’ll use the ImageClass initializer to tell the emitter what type of DisplayObject to create for each particle.

If we had an image in our library that we want to use we would export the image for use in Actionscript and pass the Class it’s exported as to the ImageClass constructor like this (in Flex, we’d embed the image in the project and use the associated class in the same way).

var imgClass:ImageClass = new ImageClass( MyImageClass );

However, I’m going to use the RadialDot class that is in the Flint library. The RadialDot class is a simple circular image with a gradient fill. The constructor for the dot class takes an argument which is the radius of the dot, which we’ll set to 2 pixels, so we also have to pass this value to the ImageClass constructor like this…

var imgClass:ImageClass = new ImageClass( RadialDot, [2] );

We then add the ImageClass initializer to the emitter like this…

emitter.addInitializer( imgClass );

Alternatively, we could combine the previous two lines of code into one line like this…

emitter.addInitializer( new ImageClass( RadialDot, [2] ) );

Using Zones

Next we add a Position initializer to place the particle in the right position on screen. The Position initializer uses a Zone. A zone is a defined area of the particle system. Flint has classes to define various shapes of zone in the org.flintparticles.twoD.zones package. When you pass a zone to the Position initializer you are telling the initializer to place the particle at a random point within that zone.

We want all our particles to be placed just above the top of the stage so that they fall into the stage from above. To do this we use a LineZone placed just above the top of the stage so that the particles are placed anywhere along this line.

var zone:LineZone = new LineZone( new Point( -5, -5 ), new Point( 505, -5 ) );
var position:Position = new Position( zone );
emitter.addInitializer( position );

The LineZone is five pixels above the top of the stage and extends five pixels to either side of the stage (remember our stage is 500 pixels wide). The particles will start from a random position along this line. Once again, if we wish we can combine these three lines of code into one longer line like this…

emitter.addInitializer( new Position( new LineZone( new Point( -5, -5 ), new Point( 505, -5 ) ) ) );

Now our particles are placed properly, we need to make them move. For them to move they need to have a velocity and to do this we add a Velocity initializer to the emitter.

Perhaps surprisingly, the Velocity initializer also uses a zone to obtain its value. To see how this works, imagine a line zone as used above. Then imagine that the particle is at point 0,0 and that we have a random point within the zone. The particle will be given an initial velocity such that it will reach that point within one second. In other words the velocity vector is the vector from the origin to a random point inside the zone.

For now, we want all our particles to drop down the screen at exactly the same speed, so we use the simplest of zones – a PointZone. Because a PointZone only contains a single point, it will always set the velocity to the coordinates of that point. To fall down the screen, we want the x value in the velocity to be zero and the y value to be something bigger than zero.

So we set up the Velocity initializer like this…

var zone2:PointZone = new PointZone( new Point( 0, 65 ) );
var velocity:Velocity = new Velocity( zone2 );
emitter.addInitializer( velocity );

Or, in a single line, like this…

emitter.addInitializer( new Velocity( new PointZone( new Point( 0, 65 ) ) ) );

Using zones to define velocity may look complicated, but it enables us to create some great effects like the logo firework in the examples section.

Using Actions

If we ran the movie now, we’d still not see any particles. To round things off we need to add some actions to the emitter.

While initializers modify the properties of a particle once, when it is created, actions modify the properties of the particles continuously after they have been created.

To get started, we only need to add one action to our emitter. Although the particles have a position and a velocity, we have to add an action to update the particle’s position every frame, based on the value of its velocity. To do this we use the Move action.

We add the Move action like this…

var move:Move = new Move();
emitter.addAction( move );

Or, in a single line, like this…

emitter.addAction( new Move() );

Starting the emitter

Now that the emitter knows when to create particles, how to initialize them and what actions to perform on them while they’re there we’re only left with the job of starting the emitter.

emitter.start();

Your code should now look something like this…

import flash.geom.Point;
import org.flintparticles.common.counters.*;
import org.flintparticles.common.displayObjects.RadialDot;
import org.flintparticles.common.initializers.*;
import org.flintparticles.twoD.actions.*;
import org.flintparticles.twoD.emitters.Emitter2D;
import org.flintparticles.twoD.initializers.*;
import org.flintparticles.twoD.renderers.*;
import org.flintparticles.twoD.zones.*;

var emitter:Emitter2D = new Emitter2D();

var renderer:DisplayObjectRenderer = new DisplayObjectRenderer();
addChild( renderer );
renderer.addEmitter( emitter );

emitter.counter = new Steady( 100 );

emitter.addInitializer( new ImageClass( RadialDot, [2] ) );
emitter.addInitializer( new Position( new LineZone( new Point( -5, -5 ), new Point( 505, -5 ) ) ) );
emitter.addInitializer( new Velocity( new PointZone( new Point( 0, 65 ) ) ) );

emitter.addAction( new Move() );

emitter.start();

and when you test the flash movie you should see lots of white snowflakes falling down the screen.

Removing the particles

With the current code we never remove the particles. When they get to the bottom of the screen they just keep going. Eventually the flash movie will slow down and maybe even crash because it has too many particles to manage, so we need to remove the particles when they leave the screen. To do this we use the dramatically titled DeathZone action.

The DeathZone action checks whether particles are outside (or inside) a defined zone and destroys them if they are. We’ll use a rectangular zone slightly bigger than the stage and destroy all particles that are outside this zone. The code looks like this…

var dzone:RectangleZone = new RectangleZone( -10, -10, 520, 420 );
var deathZone:DeathZone = new DeathZone( dzone, true );
emitter.addAction( deathZone );

Or, in a single line, like this…

emitter.addAction( new DeathZone( new RectangleZone( -10, -10, 520, 420 ), true ) );

Making it more realistic

We’ll add a couple more behaviours to the emitter to make the snow look more realistic. First we add another initializer. This will vary the size of the particles so that not all the snow flakes are the same size. We use the ScaleImageInit initializer.

The ScaleImageInit adjusts the size of the image based on a scaling value. The ScaleImageInit constructor takes two parameters – the smallest and largest scale factors to use. It will scale each particle using a random value between these two parameters. Since a scale factor of one is normal size, we’ll use values just below and above that…

var scaleImage:ScaleImageInit = new ScaleImageInit( 0.75, 2 );
emitter.addInitializer( scaleImage );

Or in one line of code

emitter.addInitializer( new ScaleImageInit( 0.75, 2 ) );

Finally, we’ll add some random drifting movement to the snow flakes. Because snow flakes are light they tend to drift about in the air currents. We can use the RandomDrift action to make the snow flakes drift around. The RandomDrift constructor takes two parameters; the maximum horizontal drift per second and the maximum vertical drift per second. We add it to the emitter like this…

var drift:RandomDrift = new RandomDrift( 15, 15 );
emitter.addAction( drift );

Or in one line of code

emitter.addAction( new RandomDrift( 15, 15 ) );

Running ahead

If you test the movie now it should look a lot more realistic. The only problem is, the screen really ought to be full of snowflakes when the movie starts. To make this happen, we tell the emitter to run on ahead a few seconds at the start, like this…

emitter.runAhead( 10 );

This causes the emitter to process ten seconds of movement immediately, so we start ten seconds in with a screen full of particles.

Finished

The complete code looks like this (I’ve rearranged the order a little for clarity)

import flash.geom.Point;
import org.flintparticles.common.counters.*;
import org.flintparticles.common.displayObjects.RadialDot;
import org.flintparticles.common.initializers.*;
import org.flintparticles.twoD.actions.*;
import org.flintparticles.twoD.emitters.Emitter2D;
import org.flintparticles.twoD.initializers.*;
import org.flintparticles.twoD.renderers.*;
import org.flintparticles.twoD.zones.*;

var emitter:Emitter2D = new Emitter2D();

emitter.counter = new Steady( 100 );

emitter.addInitializer( new ImageClass( RadialDot, [2] ) );
emitter.addInitializer( new Position( new LineZone( new Point( -5, -5 ), new Point( 505, -5 ) ) ) );
emitter.addInitializer( new Velocity( new PointZone( new Point( 0, 65 ) ) ) );
emitter.addInitializer( new ScaleImageInit( 0.75, 2 ) );

emitter.addAction( new Move() );
emitter.addAction( new DeathZone( new RectangleZone( -10, -10, 520, 420 ), true ) );
emitter.addAction( new RandomDrift( 15, 15 ) );

var renderer:DisplayObjectRenderer = new DisplayObjectRenderer();
addChild( renderer );
renderer.addEmitter( emitter );

emitter.start();
emitter.runAhead( 10 );

And the resulting flash movie should look like this

Using classes

If you want to program this with classes you have two options. The first is simply to place the above code in the constructor for you document class. The second is to create a custom emitter for the snow effect. Here’s what that custom emitter would look like…

package
{
  import flash.geom.Point;  
  import org.flintparticles.common.counters.*;
  import org.flintparticles.common.displayObjects.RadialDot;
  import org.flintparticles.common.initializers.*;
  import org.flintparticles.twoD.actions.*;
  import org.flintparticles.twoD.emitters.Emitter2D;
  import org.flintparticles.twoD.initializers.*;
  import org.flintparticles.twoD.zones.*;

  public class Snowfall extends Emitter2D
  {
    public function Snowfall()
    {
      counter = new Steady( 100 );
      
      addInitializer( new ImageClass( RadialDot, [2] ) );
      addInitializer( new Position( new LineZone( new Point( -5, -5 ), new Point( 505, -5 ) ) ) );
      addInitializer( new Velocity( new PointZone( new Point( 0, 65 ) ) ) );
      addInitializer( new ScaleImageInit( 0.75, 2 ) );
      
      addAction( new Move() );
      addAction( new DeathZone( new RectangleZone( -10, -10, 520, 420 ), true ) );
      addAction( new RandomDrift( 15, 15 ) );
    }
  }
}

You then create an instance of this class, add it to a renderer and start it off like this…

import flash.geom.Point;
import org.flintparticles.twoD.renderers.*;

var emitter:Snowfall = new Snowfall();

var renderer:DisplayObjectRenderer = new DisplayObjectRenderer();
renderer.addEmitter( emitter );
addChild( renderer );

emitter.start();
emitter.runAhead( 10 );