Example solution

This example has several interesting pieces of code. The techniques used by the code are interesting, but the code itself is relatively long and straightforward, so I'll only show a little of it here. Instead, I'll describe the key pieces and you can download the example solution to see the details.

The program uses MouseDown, MouseMove, and MouseUp event handlers to allow the user to drag and release the shot. These work much as similar event handlers work in other programs with a few additional tests.

First, the MouseDown event handler calculates the distance from the mouse to the shot's center. If the mouse is not over the shot, then the event handler exits and does not start a drag.

Once a drag begins, the MouseMove event handler verifies that the user has not dragged the shot more than a given distance from the slingshot. If the user has dragged the shot too far, the event handler uses the following code fragment to adjust the shot's position:

// Adjust the shot position so it's inside the circle.
float dx = (e.Location.X - CrossCenter.X) * MaxR / (float)dist;
float dy = (e.Location.Y - CrossCenter.Y) * MaxR / (float)dist;
ShotPosition = new PointF(
CrossCenter.X + dx,
CrossCenter.Y + dy);

The event handler's e.Location parameter gives the mouse's position, CrossCenter gives the position of the center of the slingshot's crossbar, and MaxR is the maximum distance the user should be able to drag the shot. Finally, dist is the actual distance between the mouse and CrossCenter.

This code takes the difference between the mouse's and shot's X and Y coordinates. It multiplies the results by MaxR / dist to scale the coordinate differences. The final differences, dx and dy, have the same ratio as the original unscaled differences, so they give the same direction from the slingshot's center, but they can be at most MaxR distance from the center.

The code then uses the dx and dy values to adjust the shot's center.

The MouseUp event handler ends the drag operation just as any MouseUp event handler does, but it also launches the shot. The force exerted by normal springs (and the rubber bands used by a slingshot) depends linearly on the amount by which the spring is deformed. If you stretch a spring twice as far, it provides twice the force. For this program, that means the farther back the user pulls the shot, the faster it initially moves. The following code shows how the MouseUp event handler launches the shot:

// Stop dragging and launch the shot.
private void scenePictureBox_MouseUp(object sender, MouseEventArgs e)
{
if (!Dragging) return;
Dragging = false;

// Get the shot's horizontal and vertical velocity components.
ShotVx = VelocityScale * (CrossCenter.X - ShotPosition.X);
ShotVy = VelocityScale * (CrossCenter.Y - ShotPosition.Y);

// Start the timer.
moveTimer.Enabled = true;
}

The ShotVx and ShotVy variables hold the shot's X and Y velocity components. The code sets them equal to the difference in the components between the shot's launch position and the slingshot's center. It then multiplies those values by a scale factor that was chosen to produce a nice result. In general, you may need to experiment to find the right scale factor for this kind of simulation.

After calculating the shot's velocity, the code enables the program's movement timer. The following code executes when the timer's Tick event occurs:

// Acceleration due to gravity.
private const float AccY = 3.2f * VelocityScale;

// Move the shot.
private void moveTimer_Tick(object sender, EventArgs e)
{
// Update the velocity components.
ShotVy += AccY;

// Calculate the shot's new position.
ShotPosition.X += ShotVx;
ShotPosition.Y += ShotVy;

// See if the shot has left the scene.
if ((ShotPosition.X < 0) ||
(ShotPosition.X > scenePictureBox.ClientSize.Width) ||
(ShotPosition.Y > GroundRect.Top))
{
// Stop.
moveTimer.Enabled = false;

// Reset the shot.
ShotPosition = CrossCenter;
}

// Redraw.
scenePictureBox.Refresh();
}

The AccY constant represents acceleration due to gravity. In C#, Y coordinates increase downward, so AccY is set to a positive value to indicate acceleration downward. Like the VelocityScale value, AccY was set by trial and error to create a reasonable appearance.

The timer's Tick event handler adds AccY to the shot's current Y velocity component. The shot's X velocity component remains unchanged.

Next, the code adds the shot's velocity components to its current location to move the shot.

If the shot has reached the ground or left the sides of the form, the code disables the movement timer. Otherwise, if the shot should continue moving, the code refreshes the program's PictureBox to show the shot's new position.

The rest of the program is reasonably straightforward. Download the Slingshot example solution to see additional details.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset