One of the most common tools used in particle effects are pixel or bitmap particles; that is, tiny one pixel dots. In this tutorial we explain the nature of pixel particles. Curiously, such particles are not display objects. We give a simple example that shows how to create and move particles and how to make particles leave shadowy trails. Click the screen shot below or this link to open the effect in a new window:
Download
Download the well-commented source files corresponding to the effect above:
Comments
The main point that needs to be emphasized is that a 'pixel particle' is not a display object. A particle has has a 32-bit 0xAARRGGBB color and an xy-position, and that is it. A particle appears on the screen because we assign its color to the pixel at the corresponding position in a Bitmap which is on the Display List. In our code this Bitmap is named 'bitmap'.
Coloring a pixel in a Bitmap is done via its BitmapData object and BitmapData.setPixel32 method. In our code the BimapData of 'bitmap' is named 'bmpData'.
The effect of motion is created by changing a particle's xy-position and coloring the pixel corresponding to the new position in 'bitmap'. The previously drawn particles blur and fade out thanks to a filter and a color transform applied to 'bmpData'.
To keep together all properties of each pixel it is convenient to create a custom AS3 class. We call our class Particle2D. One of the properties of the Particle2D class is 'next'. We use this property to create a 'list' of particles: firstParticle, its 'next' particle etc. We use such list instead of an array. With large number of particles, lists are faster than arrays.
In the simple example presented here, we use 1500 particles. You can set that number to 200,000 particles and the application will run smoothly.
Code
Below is the Timeline code in pixtut.fla. We keep comments within the code for greater clarity.
import com.flashandmath.particles.Particle2D;
/*
We will create 1500 particles. This little application will run fine
with as many as 200,000 particles.
*/
var numParticles:Number = 1500;
var firstParticle:Particle2D;
/*
The display objects that will be created programmatically: a container 'display',
a Bitmap 'bitmap', its bitmapData 'bmpData', and the background 'back'.
*/
var display:Sprite;
var bmpData:BitmapData;
var bitmap:Bitmap;
var back:Shape;
var displayWidth:Number;
var displayHeight:Number;
/*
Variables related to filters and transforms that we will apply to 'bmpData' on ENTER_FRAME
to create trailing and fading effects.
*/
var origin:Point;
var blur:BlurFilter;
var colorTrans:ColorTransform;
/*
The next two variables are responsible for pausing and resuming the
animation and the color of the background.
Within the function 'init' we initialize all variables.
*/
var particlesGo:Boolean;
var bgColor:uint;
init();
function init():void {
display = new Sprite();
this.addChild(display);
display.x=50;
display.y=40;
//Size of the bitmap:
displayWidth = 400;
displayHeight = 300;
//'bitmap' is the bitmap that we will see, 'bmpData' is its bitmapData
//into which we will draw particles. 'bmpData' supports transparent pixels (true)
//and intially contains completely transparent black pixels (0x00000000).
bmpData = new BitmapData(displayWidth,displayHeight,true,0x00000000);
bitmap = new Bitmap(bmpData);
//A background to put underneath 'bitmap':
bgColor=0x000000;
back = new Shape();
drawBack(bgColor);
display.addChild(back);
display.addChild(bitmap);
blur = new BlurFilter(6,6);
blur.quality = BitmapFilterQuality.LOW;
colorTrans=new ColorTransform(1,1,1,1,0,0,0,-2);
origin = new Point(0,0);
//The function 'createParticles' creates a 'list' of 1500 particles.
createParticles();
this.addEventListener(Event.ENTER_FRAME, onEnter);
display.addEventListener(MouseEvent.CLICK,onClick);
particlesGo=true;
}
/*
On each ENTER_FRAME, we apply blur and a color transform to 'bmpData' to gradually
blur and fade previously drawn particles. Then we loop through the list of particles.
We calulate a new position p.x, p.y for each particle and then draw the particle at
its new position into 'bmpData' via setPixel32 method.
The simple equations of motion for p.x, p.y create a circular motion for each
particle, with direction and angular velocity that depend on p.wc property
of each particle (set in 'createParticles').
*/
function onEnter(evt:Event):void {
if(!particlesGo){return;}
var p:Particle2D = firstParticle;
bmpData.applyFilter(bmpData,bmpData.rect,origin,blur);
bmpData.colorTransform(bmpData.rect, colorTrans);
bmpData.lock();
p = firstParticle;
do {
p.y += p.wc*(p.x-displayWidth/2);
p.x += -p.wc*(p.y-displayHeight/2);
bmpData.setPixel32(p.x, p.y, p.color);
p = p.next;
} while (p != null)
bmpData.unlock();
}
function onClick(evt:MouseEvent):void {
particlesGo=!particlesGo;
}
/*
Within 'createParticles' we create a 'list' of particles: firstParticle, its 'next' etc.
To each particle we assign a 32-bit, 0xAARRGGBB color with alpha 255 (0xFF), and the red, green and blue
components chosen randomly. Thus, each of our particles is completely opaque and has random color.
The wildcard property, p.wc, will determine the angular velocity for each particle.
*/
function createParticles():void {
var c:uint;
var lastParticle:Particle2D;
var i:int;
var r:uint;
var g:uint;
var b:uint;
var a:uint=0xFF;
for (i=0; i<numParticles; i++) {
r=Math.random()*0xFF;
g=Math.random()*0xFF;
b=Math.random()*0xFF;
c=((a << 24) | (r << 16) | (g << 8) | b);
var thisP:Particle2D = new Particle2D(c);
thisP.x = displayWidth/2*Math.random()+displayWidth/4;
thisP.y = displayHeight/2*Math.random()+displayHeight/4;
//We will use the wildcard property, 'wc', of the Particle2D class
//to adjust the speed and the direction of rotation of each particle.
thisP.wc = (2*Math.random()-1)/30;
//Create a 'list' of particles using the 'next' property of Particle2D:
if (i == 0) {
firstParticle = thisP;
}
else {
lastParticle.next = thisP;
}
lastParticle = thisP;
}
}
function drawBack(c:uint):void {
back.graphics.lineStyle(1,0x999999);
back.graphics.beginFill(c);
back.graphics.drawRect(0,0,displayWidth,displayHeight);
back.graphics.endFill();
}
Within the function createParticles and in the Particle2D class, we use bitwise shifts. For a simple tutorial on bitwise operators see: Extracting and Combining RGB Components, Custom Color Picker.
Particle2D Class
package com.flashandmath.particles {
import flash.geom.Point;
public class Particle2D extends Point {
//Linking:
public var next:Particle2D;
//Velocity and acceleration vectors
public var vel:Point = new Point();
public var accel:Point = new Point();
//Color attributes
public var color:uint;
public var red:uint;
public var green:uint;
public var blue:uint;
public var alpha:uint;
//Luminance
public var lum:Number;
//A wildcard property that you can use in your application if you need it.
public var wc:Number;
public function Particle2D(thisColor=0xFFFFFFFF){
this.color = thisColor;
this.red = ((thisColor >> 16) & 0xFF);
this.green = ((thisColor >> 8) & 0xFF);
this.blue = (thisColor & 0xFF);
this.alpha=((thisColor >> 24) & 0xFF);
this.lum = 0.2126*this.red + 0.7152*this.green + 0.0722*this.blue;
}
}
}
For another simple particle tutorial in which a particle is a Sprite written to a Bitmap see: Fading Trail Effect for Flash AS3 Particle Animations - a Simple Example.
A Few of Our Particle Effects
