Vector Member Fireworks
February 22, 2000
by Gary Rosenzweig
Several times over my game-building career I have needed to add fireworks animations to a game. These are usually just for an interesting effect, and not necessarily a real part of the game.
Most recently I had to do this for an introduction screen to a game. I decided to use Director 7's vector shape member to do this for the first time. In the past, I have relied on a series of bitmaps or using pixels to draw trails on the Stage.
My goal was to create a simple, reusable behavior. It doesn't necessarily need to be a realistic simulation of a fireworks explosion, but just an entertaining piece of eye candy. Like this:
Download this example movie in Mac or PC format. This is a D7 movie.
I ended up with the following behavior. Here are the property declarations and the on getPropertyDescriptionList handler. I tried to make it somewhat customizable.
property pNumParticles -- number of arms in the explosion property pVerticalPower -- initial force up property pHorizontalPower -- force out property pGravity -- acceleration due to gravity property pDuration -- how many steps until gone property pParticles -- list of particles property pStart -- time to explode property pStep -- which step the explosion is on on getPropertyDescriptionList me list = [:] addProp list, #pNumParticles, [#comment: "Number ¬ of Particles", #format: #integer, #range: [#min: 2, ¬ #max: 20], #default: 8] addProp list, #pVerticalPower, [#comment: "Vertical ¬ Power", #format: #float, #range: [#min: 0.0, ¬ #max: 5.0], #default: 2.0] addProp list, #pHorizontalPower, [#comment: ¬ "Horizontal Power", #format: #float, #range: ¬ [#min: 0.0, #max: 5.0], #default: 2.0] addProp list, #pGravity, [#comment: "Gravity", ¬ #format: #float, #range: [#min: 0.0, #max: ¬ 1.0], #default: .15] addProp list, #pDuration, [#comment: "Duration", ¬ #format: #integer, #range: [#min: 1, #max: ¬ 100], #default: 50] return list end
When the explosion occurs, a number of particles will be created. These will just be little lists that contain information about a particle of the explosion. Since fireworks are 3D objects, I will have an x, y and z coordinate. We will also need to know which angle the particle is flying away from the center of the explosion. The pNumParticles will tell us how many particles there are, and then we just need to spread that evenly over the 360 degrees of a circle (or 6.28 radians as is the case). The vertical speed of the particles will change over time as gravity pulls the particle down. However, this value will start as negative pVerticalPower. Negative because the particle begins by travelling upward. One last property of a particle will be the list of points that mark its path. This will start at point(0,0), the origin of the explosion and the center of our vector shape.
The on newExplosion handler will take care of setting up the particle list as well as the sprite. The sprite is set to a random location on the Stage, at least 50 pixels away from each side. A random color is chosen from a list of colors and applied to the vector shape member as well.
Here is the on beginSprite handler and the on newExplosion handler that it calls to set up and explosion.
on beginSprite me newFirework(me) end on newExplosion me -- create all particles pParticles = [] repeat with i = 1 to pNumParticles angle = 2.0*PI*i/pNumParticles add pParticles, [#a: angle, #x: 0.0, #y: 0.0, ¬ #z: 0.0, #dy: -pVerticalPower, #list: ¬ [[#vertex: point(0,0)]]] end repeat -- pick a random color c = getAt(["FFFFFF","FF0000","FFFF00","FF00FF",¬ "00FF00","00FFFF","0000FF"],random(7)) sprite(me.spriteNum).member.strokeColor = rgb(c) -- pick a random location x = random((the stage).rect.width-100)+50 y = random((the stage).rect.height-100)+50 sprite(me.spriteNum).loc = point(x,y) -- set up the vector shape sprite(me.spriteNum).member.centerRegPoint = FALSE sprite(me.spriteNum).member.vertexList = [] -- set up to a 3 second delay pStart = the ticks + random(180) -- reset steps pStep = 0 end
The purpose of the pStart property is to create a delay between explosions. Otherwise, each explosion will happen precisely when the previous one finished. Instead of the explosions looking somewhat random, like a real fireworks display, they will look precisely timed.
Most of the work is done by the on prepareFrame handler. It first checks to make sure that the predetermined delay time has been reached. Then, it will proceed to update the x, y, and z position of each particle in the list.
The variable vlist is created with Director 7's method of drawing vector shapes in mind. This means that the vector shape needs to be one long line -- no breaks. Each particle path is drawn from point(0,0), and then redrawn backward to point(0,0). This allows multiple arms to be drawn even though only one line is used.
As an additional effect, the blend of the sprite is set to an amount that starts at 100% at the beginning of the explosion, and then decreases to 0% when the last step of the explosion is taken. This mimics the look of fireworks as the burn out.
Finally, when the last step of the explosion is reached, the on newExplosion handler is called again. This will reset the particles and the vector shape member and set a delay for the beginning of the next explosion.
on prepareFrame me -- if still waiting for delay to end, do nothing if the ticks < pStart then exit -- loop through all particles and update -- positions and vectors repeat with i = 1 to pParticles.count pParticles[i].x = pParticles[i].x + pHorizontalPower¬ *cos(pParticles[i].a) pParticles[i].z = pParticles[i].z + pHorizontalPower¬ *sin(pParticles[i].a) pParticles[i].y = pParticles[i].y + pParticles[i].dy pParticles[i].dy = pParticles[i].dy + pGravity add pParticles[i].list, [#vertex: point(pParticles[i].x,¬ pParticles[i].z+pParticles[i].y)] end repeat -- create a vector list that is one long line vList = [] repeat with i = 1 to pParticles.count repeat with j = 1 to pParticles[i].list.count add vlist, pParticles[i].list[j] end repeat repeat with j = pParticles[i].list.count-1 down to 1 add vlist, pParticles[i].list[j] end repeat add vlist, [#vertex: point(0,0)] end repeat -- set the vector shape and blend sprite(me.spriteNum).member.vertexList = vlist -- set blend to fade as firework ends sprite(me.spriteNum).blend = 100-100*pStep/pDuration -- increase step pStep = pStep + 1 if pStep >= pDuration then newExplosion(me) end
To use this behavior, all you need to do is create a vector shape, drop it on the Stage, and then drop this behavior on top of it. You will probably want to set the ink of this sprite to background transparent, too. The vectors will draw faster, though, if they are copy ink and the background color of the vector shape member is the same as the background color of the Stage.
You can use this behavior to set up multiple fireworks sprites, but be sure to have a different vector shape member for each sprite. Otherwise, the behaviors will just be fighting for control of the same vector shape member.
When Director 8 comes out, this behavior will still work just fine, but you can speed it up a little by removing the part of the code that draws the particle path backwards, so that it reaches the point(0,0) before drawing the new particle path. Instead, you can just terminate the one path and begin the next by inserting a [#newCurve] element in the vertex list.
Copyright 1997-2024, Director Online. Article content copyright by respective authors.