Click here to Skip to main content
15,884,177 members
Articles / Programming Languages / XML

Small WinForm Pong Game - C#

Rate me:
Please Sign up or sign in to vote.
2.86/5 (7 votes)
8 Nov 2019CPOL5 min read 25.6K   1.1K   12   11
Simple WinForm Pong game written in C#

Introduction

I'll present you the way in which I wrote a small Pong game in C# WinForm.

Image 1

Using the Code

Let's declare global variables first. We have const integer values called limit_Pad presenting the bottom limit of the paddle, limit_Ball - the limit of the ball before bouncing up. Then, we have regular integer values computer_won and player_won that count victories of players, speed_Top and speed_Left setting a direction and movement of the ball, and boolean values up and down to decide where the paddles will go, and a game variable that prevents player from moving the paddle before the game is started. On the bottom is one Random() class held in r instance:

C++
const int limit_Pad = 170;
const int limit_Ball = 245;

int computer_won = 0;
int player_won = 0;

int speed_Top;
int speed_Left;

bool up = false;
bool down = false;
bool game = false;

Random r = new Random(); 

The first thing to do was to create a pattern of moving paddles. For that purpose, I needed 2 events - KeyDown which detects whether the button W is pressed for moving the paddle up or S button for moving the paddle down by activating a timer which moves the paddle up or down by 3 units. The KeyUp event detects when the button is released and stops the timer controlling the paddle:

C#
private void Pressed(object sender, KeyEventArgs e)
{
 if (game)
 {
  if (e.KeyCode == Keys.Up || e.KeyCode == Keys.W)
  {
   up = true;
  }
  else if (e.KeyCode == Keys.Down || e.KeyCode == Keys.S)
  {
   down = true; 
  }
  timer1.Start(); 
 }
}
private void Released(object sender, KeyEventArgs e)
{
 if (e.KeyCode == Keys.W || e.KeyCode == Keys.W)
 {
  up = false; 
 }
 else if (e.KeyCode == Keys.Down || e.KeyCode == Keys.S)
 {
  down = false; 
 }
 timer1.Stop();
}
private void MovePaddle(object sender, EventArgs e)
{
 if (up && Player.Location.Y > 0)
 {
  Player.Top -= 3; 
 }
 else if (down && Player.Location.Y < limit_Pad)
 {
  Player.Top += 3;
 }
}

The PC paddle movement is simple. It's run by a timer called Computer(), and it contains two parts; the first one which limits the paddle movement by the top and bottom, and the second that follows the ball movement regardless of whether the ball is below half paddle height or above:

C#
private void Computer(object sender, EventArgs e)
{
 if (PC.Location.Y <= 0)
 {
  PC.Location = new Point(PC.Location.X, 0); 
 }
 else if (PC.Location.Y >= limit_Pad)
 {
  PC.Location = new Point(PC.Location.X, limit_Pad);
 }

 if (Ball.Location.Y < PC.Top + (PC.Height / 2))
 {
  PC.Top -= 3;
 }
 else if (Ball.Location.Y > PC.Top + (PC.Height / 2))
 {
  PC.Top += 3;
 }
}

Now let's move for the ball movement part. First thing to do is to set the ball direction values. Upon start, the ball moves towards Player paddle by the Left property coordinate only. For that purpose, we create two procedures: one sets the values, and the other one makes the ball move:

C#
private void StartValues()
{
 speed_Top = 0;
 speed_Left = -5; 
}
private void BallMoves()
{
 Ball.Top += speed_Top;
 Ball.Left += speed_Left; 
}

The next thing to do is to limit the ball borders by vertical and horizontal line. Vertically, when the ball hits the ceiling, it bounces back in the opposite direction. So what we have to do is multiply the speed_Top variable by -1 and it will change its course regardless of hitting the upper or lower bound:

C#
private void HitBorder()
{
 if (Ball.Location.Y <= 0 || Ball.Location.Y >= limit_Ball)
 {
  speed_Top *= -1; 
 }
}

Horizontally, there's a bit more work to do. The main procedure called BallLeftField() will contain multiple smaller procedures. PlayerWon() and ComputerWon() will increment their global int variables and write them to the Label controls:

C#
private void PlayerWon()
{
 player_won++;
 label1.Text = player_won.ToString();
}
private void ComputerWon()
{
 computer_won++;
 label3.Text = computer_won.ToString(); 
}

Side that wins 10 rounds first is the winner, so when the game is over, we have to set the variables and controls at their starting positions. For that purpose, I used EndGame() procedure. It also sets the bool value game to false preventing the players paddle to move while the game is inactive, as well as setting the computer_won and player_won values to 0:

C#
private void EndGame()
{
 Player.Location = new Point(0, 75);
 PC.Location = new Point(454, 75); 
 player_won = 0;
 computer_won = 0;
 label1.Text = player_won.ToString();
 label3.Text = computer_won.ToString();
 timer1.Stop();
 timer2.Stop();
 timer3.Stop();
 button1.Visible = true; 
 game = false;
}

And when the ball left field by horizontal line, we have to set its position back to center and which side took a round, the opposite side gets a serve:

C#
private void NewPoint(int n)
{ 
 const int x = 227,y = 120;
 Ball.Location = new Point(x, y);
 speed_Top = 0;
 speed_Left = n;
}

The BallLeftField() procedure:

C#
private void BallLeftField() // Slightly different from the project code:
{
 if (player_won == 10 || computer_won == 10)
 {
  EndGame();
 }

 if (Ball.Location.X < 0)
 {
  NewPoint(5);
  ComputerWon(); 
 }
 else if (Ball.Location.X > this.ClientSize.Width)
 {
  NewPoint(-5);
  PlayerWon(); 
 }
}

The most important part of the ball movement was to determine where the ball collided with paddle. In order to do that, I split paddle in 5 parts from up to down:

  • Upper - Ball collision position is greater or equal than pad top minus ball height and lower or equal than pad top plus ball height
  • High - Ball colliding position is greater than pad top + ball height and lesser or equal than pad top + 2 times ball height
  • Middle - Ball colliding position is greater than pad top + 2 times ball height and lesser or equal than pad top + 3 times pad top
  • Low - Ball position is greater than pad top + 3 times ball height and lesser or equal than 4 times pad top + ball height
  • Bot - Ball position greater than pad top + 4 times ball height and lesser and equal than pad bottom plus ball height

For that purpose, I created 5 bool functions:

C#
private bool Upper(PictureBox Pad)
{
 return Ball.Location.Y >= Pad.Top - Ball.Height && Ball.Location.Y <= Pad.Top + Ball.Height;
}
private bool High(PictureBox Pad)
{
 return Ball.Location.Y > Pad.Top + Ball.Height && Ball.Location.Y <= Pad.Top + 2 * Ball.Height;
}
private bool Middle(PictureBox Pad)
{
 return Ball.Location.Y > Pad.Top + 2 * Ball.Height && Ball.Location.Y <= Pad.Top + 3 * Ball.Height;
}
private bool Low(PictureBox Pad)
{
 return Ball.Location.Y > Pad.Top + 3 * Ball.Height && Ball.Location.Y <= Pad.Top + 4 * Ball.Height;
}
private bool Bot(PictureBox Pad)
{
 return Ball.Location.Y > Pad.Top + 4 * Ball.Height && Ball.Location.Y <= Pad.Bottom + Ball.Height;
}

What we must know is which exact coordinates to give the ball depending on which paddle - Players or PCs it collided with. First, I'll write a function that returns negative value of randomly given number:

C#
private int Negative(int i,int n)
{
 int myval = r.Next(i, n) * -1;
 return myval; 
}

Then depending on the ball position (either greater than half of a form width or greater), the paddle will launch the ball towards the opposite side giving its speed_Left variable positive or negative value:

C#
private int AdjustCoordinates(int i,int n)
{
 int res = 0; 

 if (Ball.Location.X < this.Width / 2)
 {
  res = r.Next(i, n);
 }
 else if (Ball.Location.X > this.Width / 2)
 {
  res = Negative(i, n);
 }
 return res;
}

And there is the common Collision() procedure for both paddles. It takes the bool outcomes of switch statements which check at what part the paddle ball collided with giving the ball proper coordinates for moving the opposite side:

C#
private void Collision(PictureBox Paddle) 
{
 switch (true)
 {
  case true when Upper(Paddle):
      speed_Top = Negative(4, 6);
      speed_Left = AdjustCoordinates(5, 6);
      break;
  case true when High(Paddle):
      speed_Top = Negative(2, 3);
      speed_Left = AdjustCoordinates(6, 7);
      break;
  case true when Middle(Paddle):
      speed_Top = 0;
      speed_Left = AdjustCoordinates(5, 5);
      break;
  case true when Low(Paddle):
      speed_Top = r.Next(2, 3);
      speed_Left = AdjustCoordinates(6, 7);
      break;
  case true when Bot(Paddle):
      speed_Top = r.Next(4, 6);
      speed_Left = AdjustCoordinates(5, 6);
      break;                   
  }
  Edge(); 
}

I forgot to mention the Edge() procedure. As I was testing the game, I noticed that while hitting far sides of the paddles would bounce the ball back in the field even if it looked like the ball would leave the form, it even looked as the horizontal borders bounced the ball back in field which defies the logic of the game. So I wrote a procedure that launches the ball out the bounds if it collided with a small part of a paddle leaning to the forms horizontal edges:

C#
private void Edge()
{
 if (Ball.Location.X < this.Width / 2)
 {
  if (Ball.Location.X < 0 + Ball.Height / 3)
  {
   speed_Left *= -1; 
  }
 }
 else if (Ball.Location.X > this.Width / 2)
 {
  if (Ball.Location.X > PC.Location.X + (Ball.Width /3))
  {
   speed_Left *= -1;
  }
 }
}

When all the functions and procedures are done, we can move to the main timer which follows the entire ball movement. First, it checks if the ball collided with a Player setting the ball coordinates and launching it towards the enemy, then doing the same for the PC part. After that, it checks if the ball collided with upper\lower bounds changing its Top direction, or if the ball left field setting the ball to the center of the form and firing it:

C#
private void MoveBall(object sender, EventArgs e)
{
 if (Ball.Bounds.IntersectsWith(Player.Bounds))
 {
  Collision(Player);
 }
 else if (Ball.Bounds.IntersectsWith(PC.Bounds))
 {
  Collision(PC);
 }
 HitBorder();
 BallLeftField();
 BallMoves();
}

And the final part, the "Start Game" button. Upon clicking, it sets the starting ball direction values, then sets bool value game to true enabling the Player to move the paddle, enabling all 3 timers making the ball move as well as the Enemy(PC).

C#
private void button1_Click(object sender, EventArgs e)
{        
 StartValues(); 
 game = true;
 button1.Visible = false;
 timer1.Start();
 timer2.Start();
 timer3.Start(); 
}

And that's it! It's nothing special, but I hope it might give some idea to beginners.

History

  • 4th November, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Employed (other) G4S
Serbia Serbia
My name is Aleksandar,I'm 27. I'm currently working in G4S secure solutions. I went to college,studied IT but quit after short time. Still my love for programming remained. Now I'm writing small-trivial projects in C# and posting them as a Tips.

Comments and Discussions

 
QuestionPong C# Pin
Member 1567102012-Jun-22 9:01
Member 1567102012-Jun-22 9:01 
QuestionWorks and great explanations. Pin
whinton32410-Nov-19 2:18
whinton32410-Nov-19 2:18 
QuestionHow do you use SQLite database? Pin
Southmountain9-Nov-19 6:41
Southmountain9-Nov-19 6:41 
AnswerRe: How do you use SQLite database? Pin
NickNs19919-Nov-19 8:12
NickNs19919-Nov-19 8:12 
I'm not sure what are you talking about?
NewsFunny hobby project, but... Pin
Emil Steen6-Nov-19 3:01
Emil Steen6-Nov-19 3:01 
QuestionKeys Pin
avisal5-Nov-19 6:50
professionalavisal5-Nov-19 6:50 
AnswerRe: Keys Pin
NickNs19915-Nov-19 12:46
NickNs19915-Nov-19 12:46 
QuestionScreenshot please Pin
.dan.g.4-Nov-19 23:16
professional.dan.g.4-Nov-19 23:16 
AnswerRe: Screenshot please Pin
NickNs19915-Nov-19 2:05
NickNs19915-Nov-19 2:05 
GeneralRe: Screenshot please Pin
.dan.g.6-Nov-19 13:18
professional.dan.g.6-Nov-19 13:18 
QuestionPlease wait for download link Pin
NickNs19914-Nov-19 7:12
NickNs19914-Nov-19 7:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.