Create Particle Systems Dynamically
October 10, 2000
by Charles Forman
Particle systems can simulate rain, fire, fireworks, explosions, smoke, clouds and other gaseous effects. In a particle system, one particle exists for each raindrop or bit of smoke. That particle moves, rotates, and blends in a certain way to simulate a real raindrop or smoke particle. In a traditional particle system rendering engine, each particle is very small, with a unique behaviour based on physics and its interaction with the other particles. Unfortunately, that process can take anywhere from one second to one hour per frame, which makes it impossible to create a truly accurate real-time particle system.
Go and rent a Die Hard movie (or other action thriller) to study the explosions. With little explosions, notice that one mass of fire expands and spirals out into nothingness. With huge climactic explosions, notice that there appear to be several masses of fire, each one acting similarly but uniquely.
The key to creating real-time particle systems in Director is to understand that particles can be averaged into larger groups of particles. Because Director would not be able to handle keeping track of and displaying a billion particles, it is important to average groups of particles. One particle in this demonstration represents billions of particles in a real life system. The more particles you display, the more realistic your simulation becomes.
This is actually very easy to comprehend when you think about the life of a particle. The particle starts out very small, and invisible at its originating point. It begins to grow and rotate, becoming more opaque as it moves away from its originating point. Just as the particle reaches its most opaque form, it begins to dissipate and slow down. Gravity, momentum, wind, and other forces can affect the movement. You can make the particle system as simple or complex as you want.
Director 8 download for Mac or Windows.
In Director, because sprites are so easily manipulated, I chose to have one sprite represent each particle in the system. Each particle sprite has a behavior script that tells the particle how to act. Here's how it's done.
property PointX, PointY, SpinSpeed, Momentum, Radius, Angle
property SpriteBlend, BlendMomentum, Scale
on beginsprite me
PointX = the MouseH
PointY = the MouseV
SpinSpeed = Random(60)-30
Momentum = Random(Value(field("Momentum")))+25
Radius = 5
Angle = Random(Value(field("Spread")))+ Value(field("Angle"))
SpriteBlend = 0
BlendMomentum = Value(field("BlendMomentum"))
Scale = 0
sprite(me.spritenum).MemberNum = Member("Fire" & Random(4)).number
end
The beginSprite event sets the values at the birth of the particle: the initial location, momentum, distance, direction, blend and color. Note the liberal use of random() in creating values for the properties of the system. Random movements make the fire look organic and realistic.
on exitframe me
Momentum = Momentum * Value(field("MomentumDampen"))
Radius = Radius + Momentum
Scale = Scale + Momentum
SpriteBlend = SpriteBlend + BlendMomentum
sprite(me.spritenum).width = Scale
sprite(me.spritenum).height = Scale
sprite(me.spritenum).rotation = sprite(me.spritenum).rotation + SpinSpeed
sprite(me.spritenum).blend = min(max(SpriteBlend, 0), 100)
sprite(me.spritenum).locH = Radius*cos(Angle*pi()/180) + PointX
sprite(me.spritenum).locV = Radius*sin(Angle*pi()/180) + PointY
if SpriteBlend > 100 then BlendMomentum = -(Random(16) + 4)
if SpriteBlend < 0 then BeginSprite me
end
The exitFrame event affects the properties of the particle as each frame passes. When setting the blend I use the min and max functions, because the spriteBlend property will often be a little more than 100 or less than 0. If the particle's spriteBlend has become greater than 100, the blendMomentum is set to a negative number and the particle will fade out. When the particle fades out completely, it recycles itself.
The Particle Image
The actual particle image is designed to appear very much like a puff of smoke. It is circular and has feathered edges, while having some texture. I created a 200x200 pixel Photoshop file with a white background, then used the airbrush tool to dab small dots on the canvas until I achieved the desired effect. It is important that the image has no distinct features. Remember that the image will be displayed over and over again; each particle should appear to be unique. Plus, the particles will be scaled upwards of at least 300%; distinct features tend to look very pixilated, scaled and rotated.
Dynamic Particle Image Creation
The particle's Cast member is assigned as follows:
sprite(me.spritenum).MemberNum = Member("Fire" & Random(4)).number
If you examine the cast before running, you will notice that the members Fire1 through Fire4 does not exist. They are dynamically created at run time to conserve disk space and file transfer times. The 8-bit member ParticleAlpha is the only image. Because ParticleAlpha is the alpha channel that will be used for all particles, Imaging Lingo can be used to draw new images, based on the member ParticleAlpha, on the fly.
on createFireImages
-- Set the four colors for the particles
color1 = rgb(133, 133, 133)
color2 = rgb(255, 240, 0)
color3 = rgb(255, 186, 0)
color4 = rgb(255, 90, 0)
repeat with i = 1 to 4
-- Set the alpha
BaseImageAlpha = member("ParticleAlpha").image
-- Create new cast member, name it, make image
TargetMember = new(#bitmap)
TargetMember.name = "Fire" & i
TargetMember.image = image(member("ParticleAlpha").image.width, member("ParticleAlpha").image.height, 32)
-- Fill the image with the color
TargetMember.image.fill(TargetMember.image.rect, value("color" & i))
-- Set alpha to nothing and make a copy of it (seems asinine, but
-- otherwise, the resulting images alpha has noise (BUG??))
TargetMember.image.setAlpha(0)
emptyAlpha = targetMember.image.extractAlpha()
newAlpha = duplicate(emptyAlpha)
-- Use copyPixels to apply the ParticleAlpha to the copied alpha
newAlpha.copyPixels(BaseImageAlpha,BaseImageAlpha.rect,BaseImageAlpha.rect)
-- Set the Alpha
targetMember.image.setAlpha(newAlpha)
targetMember.useAlpha = true
end repeat
end
ParticleAlpha member is 76.1K uncompressed, and I used JPEG compression at 80%.
Each dynamically created cast member is 64.9K. There are four, plus the alpha, which comes to a total of 226.6K. If you didn't dynamically create the cast members, the difference in size would be huge. Granted, JPEG compression will keep the file size down. But what if you wanted 64 different colors of the particles? The time it would take to dynamically generate the 64 members would be less than a second.
Check out this quick example I made. The game is a cheap ripoff of RobotDuck's Yard Invaders (a very nice game). Notice how the smoke is used in the game.
Copyright 1997-2025, Director Online. Article content copyright by respective authors.