Timers by pengxiuhui

VIEWS: 9 PAGES: 9

									Chapter 9: Random Numbers, Timers, and Simple Animation                                         9-11


Project 9.3: Target Shooting

Many popular video games involve target shooting in some form or another. For this project,
we’ll develop a program that simulates a military cannon firing at randomly placed targets.
Mouse movement will be used to control the cannon’s angle of elevation. Each time a mouse
button is clicked, a shot will be fired and tracked until it either hits the target, or goes off the
screen. To add to the realism of this process, our shots will follow a parabolic path, characteristic
of projectiles traveling under the influence of gravitational pull.


Step 1: Define the Basic Form Layout

The form for this project is quite simple:




The form’s background color should be changed to white; and 3 timer components should be
placed on the form. Also, the form’s WindowState and FormBorderStyle attributes should be
set to Maximized and FixedSingle respectively.

The first timer will be used to trigger initialization of the form, while the second will be used to
generate another target on the screen, and the third will be used to track the flight of the cannon’s
shot toward the target. Initially, only Timer1 should be enabled. The Interval attributes for the
three timers should be set to 25, 250, and 40, respectively.


Step 2: Create the “Cannon” Class

The heart of this project will be a group of three classes – one for the cannon, one for the target,
and one for the shell. These three classes will be used to manage the corresponding components
of the game, as well as the interaction between these components.

The first of these classes is the Cannon class, which creates a rectangular “cannon” and allows
the cannon to be pivoted up and down in response to mouse movements. The full definition of
this class follows:

public class Cannon
{
    int screenWidth, screenHeight;                     // The size of the screen
    double angle;                                      // Current angle of elevation
9
9-12                              Introduction to Computer Programming Using C#

       Point[] corners;                // Corners of the cannon
       const int barrelLength = 50,    // Size of the cannon
                 barrelWidth = 16;

       // Create a cannon on a screen of the specified size

       public Cannon(Graphics g, int width, int height)
       {
           screenWidth = width;
           screenHeight = height;
           corners = new Point[4];
           angle = 0;
           ComputeCorners();
           Draw(g);
       }

       // Draw the cannon on the screen

       public void Draw(Graphics g)
       {
           Brush b;
           using (b = new SolidBrush(Color.Black))
               g.FillPolygon(b, corners);
       }

       // Set the cannon’s elevation angle

       public void setAngle(double angle)
       {
           this.angle = angle;
           ComputeCorners();
       }

       // Determine where the corners of the cannon should be,
       // based on the current angle of elevation

       private void ComputeCorners()
       {
           corners[0].X = 20;
           corners[0].Y = screenHeight - 20;
           corners[1].X = corners[0].X + (int)(barrelLength *
                          Math.Cos(Math.PI * angle / 180));
           corners[1].Y = corners[0].Y –
             (int)(barrelLength*Math.Sin(Math.PI * angle / 180));
           corners[3].X = corners[0].X + (int)(barrelWidth *
                          Math.Cos(Math.PI * (angle + 90) / 180));
Chapter 9: Random Numbers, Timers, and Simple Animation                                       9-13

            corners[3].Y = corners[0].Y - (int)(barrelWidth *
                           Math.Sin(Math.PI * (angle + 90) / 180));
            corners[2].X = corners[1].X +
                           corners[3].X - corners[0].X;
            corners[2].Y = corners[1].Y +
                           corners[3].Y - corners[0].Y;
      }

      // Retrieve the size and location of the cannon barrel
      // in order to determine the starting point and angle
      // of the cannon shell when it is fired

      public void getBarrel(out double barrelX, out                         double barrelY,
                            out double caliber, out                         double angle)
      {
          caliber = barrelWidth - 2;
          angle = this.angle;
          barrelX = (corners[1].X + corners[2].X) /                         2 + caliber/2;
          barrelY = (corners[1].Y + corners[2].Y) /                         2 - caliber/2;
      }
}


Step 3: Create the “Target” Class

The Target class is used to randomly position a target on the screen, and also for determining if
a shot has hit the target, or gone off the screen. Its complete definition follows:

public class Target
{
    int targetX, targetY, screenWidth, screenHeight;
    const int targetSize = 30;
    public enum Result {HIT, MISSED, UNCERTAIN};

      // Create a new target to shoot at

      public Target(Graphics g, int width, int height, Random rand)
      {

            // Select a screen location for the next target

            targetX = width / 2 + rand.Next(width / 2 - targetSize);
            targetY = targetSize +
                      rand.Next(height - 2 * targetSize);
            screenWidth = width;
            screenHeight = height;
9
9-14                                            Introduction to Computer Programming Using C#

            Draw(g);
       }

       // Draw (or redraw) the target

       public void Draw(Graphics g)
       {
           Pen p;
           using (p = new Pen(Color.Red, 2))
               g.DrawEllipse(p,
                     targetX - targetSize, targetY - targetSize,
                     2 * targetSize, 2 * targetSize);
       }

       // Test to see if the shell has hit the target, or
       // gone off of the screen

       public Result shotStatus(int shellX, int shellY,
                                int shellRadius)
       {
           if (shellX >= screenWidth || shellY >= screenHeight)
               return Result.MISSED;

            if (Math.Pow(targetX - shellX, 2) +
                Math.Pow(targetY - shellY, 2) <
                              Math.Pow(targetSize, 2))
                return Result.HIT;
            return Result.UNCERTAIN;
       }
}


Step 4: Create the “Shell” Class

The final support class, Shell, is responsible for first creating a cannon shell, which is initially
positioned at the mouth of the cannon’s barrel, and then for moving (and redrawing) that shell
across the screen in its flight toward the target. The full definition of this class follows:

public class Shell
{
    int time;          // How long has the shell been in flight?
    double shellX, shellY; // Where the shell’s flight started
    double shotX, shotY;    // Where the shell is now located
    double radius,          // The size of the shell
           angle;           // The cannon’s angle of elevation
Chapter 9: Random Numbers, Timers, and Simple Animation        9-15

     // These parameters control the speed and "arc" of the shell

     const double Velocity = 25, Gravity = 0.35;

     // The constructor for the shell variable

     public Shell(Graphics g, double barrelX, double barrelY,
                               double caliber, double barrelAngle)
     {
         time = 0;
         shellX = barrelX;
         shellY = barrelY;
         radius = caliber / 2;
         angle = barrelAngle;
         Draw(g);
     }

     // Advance the shell’s time in flight

     public void Tick()
     {
         time++;
     }

     // Get the current position and size of the shell

     public void getBounds (out int shellX, out int shellY,
                            out int shellRadius)
     {
         shellX = (int)shotX;
         shellY = (int)shotY;
         shellRadius = (int)radius;
     }

     // Draw the shell on the screen

     public void Draw(Graphics g)
     {
         Brush b;

          // Figure out where the shell is now

          shotX = shellX + Velocity * time *
                           Math.Cos(Math.PI * angle / 180);
          shotY = shellY - Velocity * time *
                           Math.Sin(Math.PI * angle / 180) +
9
9-16                                              Introduction to Computer Programming Using C#

                                        Gravity * Math.Pow(time, 2) / 2;

             // Draw the shell as a black circle

             using (b = new SolidBrush(Color.Black))
                 g.FillEllipse(b, (int)(shotX - radius),
                                  (int)(shotY - radius),
                                  (int)(2 * radius),
                                  (int)(2 * radius));
        }
}


Step 5: Define the Main Program’s Variables

Your program will need a total of 5 variables – one for each of the 3 classes, a random number
generator (for positioning the target), and a variable for keeping track of when a shell is “in the
air.”

You should declare these variables at the start of your form definition, ahead of the constructor
for Form1:

        cannon, a variable of type Cannon
        target, a variable of type Target
        shell, a variable of type Shell
        rand, a variable of type Random
        shooting, a variable of type bool; this variable should be initialized to false

Within the constructor for Form1, add the instruction to initialize the random number generator:

    rand = new Random();


Step 6: Implement the First “Timer Tick” Event Handler

When timer1 generates a Tick event, your program needs to carry out the following actions:

        Declare a variable g, of type Graphics
        Disable timer1
        Create the variable cannon, like this:

         using (g = this.CreateGraphics())
             cannon = new Cannon(g, this.Width, this.Height - 50);

        Enable timer2
Chapter 9: Random Numbers, Timers, and Simple Animation                                             9-17


Step 7: Enable Cannon Movement

As you recall, the cannon is supposed to pivot in response to mouse movements. Specifically, the
y-coordinate of the mouse will be used to set the cannon’s angle of elevation. In order to show
that the cannon’s position has changed, though, it will also be necessary to implement the Paint
event handler, in addition to the Mouse Move event handler, to update the information displayed
on the screen.

The event handler for mouse movement is very simple. The cannon is only allowed to move if
the shell has not yet been fired (moving the cannon won’t affect the path of a shell that’s in
flight), so, as long as a shot is not in the air, then, if the cannon has already been created, it can be
pivoted, like this:

if (!shooting && cannon != null)
    cannon.setAngle(90 * (1 - (double)(e.Y) /
                            (this.Height - 50)));
            Refresh();

The Paint event handler isn’t much more difficult. It needs to check to see if the individual items
currently “exist,” and, if they do, it issues a request to draw them:

Graphics g;

using (g = this.CreateGraphics())
{
    if (cannon != null)
        cannon.Draw(g);
    if (target != null)
        target.Draw(g);
    if (shooting)
        shell.Draw(g);
}


Step 8: Implement the Event Handler for timer2

When timer2 generates a Tick event, it indicates that a new target should be created, and placed
on the screen in preparation for the next shot to be fired. To do this:

       Declare a variable g of type Graphics
       Disable timer2
       Create a new target, like this:

        using (g = this.CreateGraphics())
            target = new Target(g, this.Width, this.Height, rand);
9
9-18                                               Introduction to Computer Programming Using C#


Step 9: Enable Cannon Firing

When a mouse click is detected, it triggers a request to fire the next shell. Since you can’t have
two shells flying at the same time, the event handler must first check to ensure that there isn’t
already a shell in flight. If the value of the variable shooting is false, these actions should be
carried out:

      4 variables of type double – barrelX, barrelY, caliber, and angle - should be declared,
       along with a variable g, of type Graphics
      The value of shooting should be set to true
      The current location and angle of the cannon should be found, like this:

       cannon.getBarrel(out barrelX, out barrelY,
                        out caliber, out angle);

      A new shell should be created, like this:

       using (g = this.CreateGraphics())
           shell = new Shell(g, barrelX, barrelY, caliber, angle);

      timer3 should be enabled; this timer will be used to track the flight of the cannon shell


Step 10: Implement the Event Handler for timer3

Each time timer3 triggers, the shell should be advanced to its next position, and the program
should test to see if either the target has been hit, or if the shell has gone off of the screen. To do
this, your event handler for timer3 should:

      Declare the variables, shellX, shellY, and shellRadius, of type int, along with a variable
       result, of type Target.Result
      Advance the shell’s clock, using the instruction shell.Tick();
      Trigger a “repaint” operation, using the instruction Refresh();
      Get the current position of the shell, using this instruction:

       shell.getBounds(out shellX, out shellY, out shellRadius);

      Check to see if the shell has hit the target:

       result = target.shotStatus(shellX, shellY, shellRadius);

      If the shell went off of the screen or hit the target, disable timer3, and set the variable
       shooting to false. to test, use this if statement:

       if (result != Target.Result.UNCERTAIN)
Chapter 9: Random Numbers, Timers, and Simple Animation                                      9-19


      If the shell hit the target, use a MessageBox to display a congratulatory message, and
       enable timer2. To test for this, use the if statement:

       if (result == Target.Result.HIT)

      If the shell went off the screen, use a MessageBox to insult the player, and enable timer2.
       To test for this, the correct if statement would be:

       else if (result == Target.Result.MISSED)

At this point, your program should be complete, although there are certainly a number of ways in
which you could “enhance the playing experience.”

								
To top