Title: Make a cannon game in C#
This simple cannon game randomly positions a target house. Then when you click the Shoot button, it gets angle and speed information for the cannon and enables a timer. To keep the graphics and timing simple, the program uses a scale of 1 pixel = 1 meter.
If you set the compile-time variable DEBUGGING to true, then the program draws the position where the cannon ball should ideally cross the Y coordinate where it was launched. If the ball started at ground level and the ground were flat, this is where the ball would land. It calculates this position using the formula:
Distance = 2 * V * V * Sin(T) * Cos(T) / g
Here g is the acceleration due to gravity, which is 9.8 meters per second. The program redraws TICKS_PER_SECOND times per second so the acceleration per tick is 9.8 /(TICKS_PER_SECOND * TICKS_PER_SECOND) meters per second squared.
The following code shows how the cannon game sets up when you click the Shoot button.
// Launch a cannonball.
private void btnShoot_Click(object sender, EventArgs e)
{
// Redraw.
using (Graphics gr = picCanvas.CreateGraphics())
{
DrawField(gr);
}
// Get the speed.
float speed;
try
{
speed = float.Parse(txtSpeed.Text);
}
catch
{
MessageBox.Show("Invalid speed", "Invalid Speed",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
if (speed < 1)
{
MessageBox.Show("Speed must be at least 1 mps",
"Invalid Speed", MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
return;
}
// Get the speed components in meters per tick.
Vx = speed * Math.Cos(Theta) / TICKS_PER_SECOND;
// Negative to go up.
Vy = -speed * Math.Sin(Theta) / TICKS_PER_SECOND;
// Disable UI elements.
btnShoot.Enabled = false;
txtDegrees.Enabled = false;
txtSpeed.Enabled = false;
Cursor = Cursors.WaitCursor;
Application.DoEvents();
#if DEBUGGING
// Draw the location where the cannon ball
// should pass the Y position where it started.
// Distance =
// 2 * V^2 * Sin(T) * Cos(T) / g = V^2 * Sin(2*T) / g
gr.DrawEllipse(Pens.Blue,
(float)(BulletX + 2 * speed * speed * Math.Sin(Theta) *
Math.Cos(Theta) / 9.8),
(float)(BulletY), CANNON_HGT, CANNON_HGT);
#endif
// Start moving the cannon ball.
tmrMoveShot.Enabled = true;
}
The cannon game keeps track of the cannonball's position and velocity with the variables BulletX, BulletY, Vx, and Vy. When the timer event occurs, the program moves the cannonball. It uses the velocity components to update the ball's position and then redraws the ball.
private double Theta, BulletX, BulletY, Vx, Vy;
private const int TICKS_PER_SECOND = 10;
// Acceleration in meters per tick squared.
private const double YAcceleration =
9.8 / (TICKS_PER_SECOND * TICKS_PER_SECOND);
private void tmrMoveShot_Tick(object sender, EventArgs e)
{
// Erase the cannon ball's previous position.
using (Graphics gr = picCanvas.CreateGraphics())
{
using (Brush br = new SolidBrush(picCanvas.BackColor))
{
gr.FillEllipse(br, (float)(BulletX), (float)(BulletY),
CANNON_HGT, CANNON_HGT);
}
// Move the cannon ball.
Vy += YAcceleration;
BulletX += Vx;
BulletY += Vy;
// Draw the new cannon ball.
gr.FillEllipse(Brushes.Black,
(float)(BulletX), (float)(BulletY),
CANNON_HGT, CANNON_HGT);
// See if we should stop.
if ((BulletY > picCanvas.ClientRectangle.Height) ||
(BulletX > picCanvas.ClientRectangle.Width))
{
// Stop running.
tmrMoveShot.Enabled = false;
// Re-enable UI elements.
btnShoot.Enabled = true;
txtDegrees.Enabled = true;
txtSpeed.Enabled = true;
Cursor = Cursors.Default;
}
}
}
Some improvements that you could make to the cannon game include hit detection to know when the ball hits the target, drawing a nice irregular surface between the cannon and the target, placing the target at different altitudes,allowing the target to shoot back, etc. Perhaps I'll get around to some of them one day.
Download the example to experiment with it and to see additional details.
|