One of the useful aspects of object-oriented programming is that it often lends itself to easily defining real-world objects in terms of code classes and objects. As a part of that definition, we can define fields in a class that describe the physics of motion of scene objects.
Excercise 4-1.
Write code to build an animated scene. First, create a new project folder named Fireworks using the NewProject program.
  1. Create a new unit called FlareClass (using File -> New and then selecting Text) and inside this unit create the template for a new class called TFlare).
  2. Add the fields Position, Velocity, and Acceleration to the class. They should each be of type TVector2D which means you'll need to add FTGraphics to the uses clause. These fields will control the motion of the flare. Add the field Intensity and Hue of type Byte because we're going to make flares that are multi-colored and that burn out over time. Create an enumeration called TFlareType that has two values: ftDetonator and ftBurnOut. Add FlareType of type TFlareType, Count of type Integer, and DetonationTime of type Integer. As you might guess, these three variables are going to control the detonation of the flare.
  3. Add a constructor called Create and a procedure called StepAndDraw. You'll also need to write the basic implementation template code (i.e., the begin-end block). Additionally, define the variable Flares of type TList in the interface section so that it will be visible to anything that uses the FlareClass unit. While we're at it, we should also add FTGraphics and FTFlareGraphics to the uses clause since we will need those later.
  4. Now switch back to the main Fireworks program source file. You will need to add FlareClass to the uses clause. In Initialize we want to call Randomize (this resets the random number generator). Then we want to call InitFlareBackground because we'll be using the flare convolution filter effect. Finally we want to create an instance of TList and store it in Flares.
  5. Inside of OnEnterFrame we want to do a few things. First, if the mouse button is clicked we want to create a new flare. You will of course want to add the newly created flare to the Flares list. Regardless of whether or not the mouse is clicked, we should always loop through the list and draw any flares. Remember that the references in TList are initially untyped. This means that you will have to type cast to TFlare and then call StepAndDraw. After the loop, you should call Flares.Pack to remove any elements in the list that might have been nullified due to the detonation of a flare (we'll explain this part a little later). Finally, we have to call DoConvolutionFilter to create the actual effect.
  6. Back in the FlareClass unit let's implement the functionality for drawing, moving, and detonating flares. First, in the Create constructor we should initialize the basic fields of the object. For Position, Velocity, and Acceleration the Vector(X,Y) function (defined in FTGraphics) can be used to create a TVector2D record. We want the flares to start at the bottom of the screen (so the Y coordinate is -10) and to occur anywhere randomly between -14 and 14. Next we want the initial velocity to point upward (let's make it random between 10 and 20) and vary horizontally by some random amount that is positive if the initial X position is negative and negative if the initial X position is positive (in other words, so the flare always travels toward the Y axis). The acceleration should mimic gravity. It shouldn't be -9.8 because the units here are arbitrary. It should only be some constant that points downward along Y (try using a magnitude of about 5 although you can experiment with this later). Also set the DetonationTime to 1 for now, the FlareType to ftDetonator, the Intensity to 255, and the Hue to some random number less than 256.
  7. Now let's implement StepAndDraw. As the name suggests, this procedure is called on every time step which means we first must update the velocity and position (based on the constant acceleration as well as initial velocity and position). Instantaneous final velocity () and displacement () are given by the following:
      The velocity formula is fairly obvious. Since acceleration is in distance units per second per second and velocity is in distance units per second we simply need to multiply the size of the time step () by the constant acceleration () and add that to the inital velocity (). The displacement formula is a little more complex. The first two terms are fairly obvious (taking initial position and adding to it the change in position given by or velocity times some amount of time which of course gives distance traveled). The accounts for the change in velocity due to constant acceleration that occurs within the time step. Its origin is clear if you know calculus (the mathematics of continuous change), but we will not derive it here. In fact, the term is basically inconsequential here because we assume a frame rate of 30 frames per second which means each time step is 1/30 of a second. which is so small that the entire term is dwarfed by . Thus, we can omit it entirely. Not only does this simplify the code, it also makes it faster since there is one less term to compute. So in pseudocode, we have something like this as our updates for position and velocity:
      Implement these formulas now. When you multiply the time step, think of a way we can write the code to make it more computationally efficient (remember that divisions are expensive).

    Finally, draw the flare given the flare's position, hue, and intensity. Remember that the Flare procedure is defined in FTFlareGraphics as Flare(X,Y,Hue,Intensity).
  8. At this point you could run the program and see the basic result. Remember, nothing will happen until you click the mouse on the window. If nothing happens even after you click the mouse then there is a bug somewhere in your code and you should go back and find it before moving one.

    Now that the drawing and movement of the flare is taken care of, we should implement the detonate or burn out behavior that is implied by ftDetonator or ftBurnOut. Create a conditional statement that would execute code related only to ftDetonator or ftBurnOut given the field FlareType. Also, implement the Count field so that it increases by 1 every time the procedure StepAndDraw is called.
  9. Let's add a new procedure called Detonate to the class. Then for a detonating flare, call detonate if Count is greater than DetonationTime*30 (because we assume DetonationTime is in seconds and Count is in frames). For burn out flares, decrement the intensity by 5 on each step unless the Intensity becomes less than 5 at which the Intensity is automatically set to 0.
  10. Now let's implement the Detonate procedure. How this is done is relatively arbitrary, but there are a few requirements to make it work properly. First, we must remove the current flare from the Flares list. To do this, we need to know the index of the current object in order to execute something like Flares[Index] := nil which will nullify the reference to the object. We can call Flares.IndexOf and pass a reference to the current object (the Self variable) in order to determine this index. Implement this as the first part of the Detonate procedure.
  11. The rest is relatively arbitrary. Experiment a bit on your own. Essentially what we want to happen when a flare detonates is that more flares are generated from the detonated flare's current position. In order for the detonation to look like a firework, the new flares should have velocities that point out symmetrically and radially from the point of detonation (think about how to do this with sin and cos). Although the new flares will have velocities that point in different directions, they should all have the exact same magnitude. The new flares should have the same hue as the old flare and they should be of the type that burns out. They also must be added to the Flares list and you should call StepAndDraw to get them moving (otherwise nothing will appear to happen at the point of detonation). Try to get this to work by running and revising the program until the desired fireworks effect is attained.
  12. The one thing you will notice is that the fireworks do not make contiuous flare paths. Instead the path is dotted and this gets worse as the flare's velocity increases. The problem is that discrete steps are taken which are larger than a pixel. Draw multiple flares on each call to StepAndDraw in order to prevent this (drawing 10 flares per step should do it).