Articles Archive
Articles Search
Director Wiki
 

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.

Gary Rosenzweig is the Chief Engineer, founder, and owner of CleverMedia, a game and multimedia development company in Denver, Colorado. He is the author of ten books on Macromedia Director and Flash, including his latest, Special Edition Using Director MX.

Copyright 1997-2024, Director Online. Article content copyright by respective authors.