C# Tutorials – Create a simple Pong Game in Windows Forms and Visual Studio

Hi, in this tutorial we will build a simple pong game using C# programming language. We will be building this project using Windows Forms Application in Visual Studio. This game is very simple you, we are using 3 picture boxes in the project, 1 for the player, 1 for the computer and 1 for the ball. You can download the images used in this tutorial below the video tutorial section. The source code section will provide you will the full source code used in this project. We recommend you try and recreate the project yourself and if you are having issues you can double check with the source code.

Lesson Objectives:

  • Create a pong game using Windows Forms and C#
  • Assign Key up and down movements to the player
  • Assign random movements to the ball x and y values
  • Assign movements and random speed for the computer movement
  • Keep track of the score and update score for player and computer
  • Import images and assign those images to the ball, the player and the computer picture box
  • Show custom end message when the player or the computer wins the game

Video Tutorial –

 

Download the Pong Game Assets

 

Source Code Explanations –

Variables 

int ballXspeed = 4;
int ballYspeed = 4;
int speed = 2;
Random rand = new Random();
bool goDown, goUp;
int computer_speed_change = 50;
int playerScore = 0;
int computerScore = 0;
int playerSpeed = 8;
int[] i = { 5, 6, 8, 9 };
int[] j = { 10, 9, 8, 11, 12 };

ballXspeed and ballYspeed determine the ball’s horizontal and vertical speeds respectively, affecting its movement across the screen.

speed influences the speed of the player’s paddle movements, impacting how quickly the player can react to the ball.

rand generates random numbers, adding unpredictability to the game, such as determining the ball’s initial direction or triggering random events.

goDown and goUp track paddle movement, responding to player input for upward or downward movement.

computer_speed_change adjusts the computer-controlled paddle’s movement speed or behavior, making the AI more dynamic.

playerScore and computerScore keep track of player and computer scores respectively, updating when the ball passes a paddle.

playerSpeed controls the player’s paddle movement speed based on input.

i may randomize the opponent paddle’s movement, storing possible y-coordinates for its position.

j might randomize ball speed, containing various values for different post-collision speeds.

Together, these variables govern key aspects of Pong gameplay, from ball and paddle movement to scoring and AI behavior, while also introducing elements of randomness for dynamic gameplay.

Key Down Event

private void KeyIsDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Down)
    {
        goDown = true;
    }
    if (e.KeyCode == Keys.Up)
    {
        goUp = true;
    }
}

This function, named KeyIsDown, is an event handler that gets triggered when the player presses a key on the keyboard. It takes two parameters: sender, which represents the object that triggered the event (in this case, likely a keyboard input), and e, which provides information about the event, such as which key was pressed.

Inside the function, it checks if the KeyCode property of the KeyEventArgs object e is equal to Keys.Down or Keys.Up, which represent the down arrow key and the up arrow key respectively. If the pressed key is the down arrow key (Keys.Down), it sets the boolean variable goDown to true, indicating that the paddle should move downwards. Similarly, if the pressed key is the up arrow key (Keys.Up), it sets the boolean variable goUp to true, indicating that the paddle should move upwards.

In summary, this function allows the game to respond to keyboard input from the player, enabling them to control the movement of their paddle in the game by pressing the down or up arrow keys.

Key Up Event

private void KeyIsUp(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Down)
    {
        goDown = false;
    }
    if (e.KeyCode == Keys.Up)
    {
        goUp = false;
    }
}

This function, named KeyIsUp, is an event handler that is triggered when the player releases a key on the keyboard. It takes two parameters: sender, which represents the object that triggered the event (likely a keyboard input), and e, which provides information about the event, such as which key was released.

Within the function, it checks if the KeyCode property of the KeyEventArgs object e matches specific keys. If the released key is the down arrow key (Keys.Down), it sets the boolean variable goDown to false, indicating that the player has released the key for moving the paddle downwards. Similarly, if the released key is the up arrow key (Keys.Up), it sets the boolean variable goUp to false, indicating that the player has released the key for moving the paddle upwards.

Check Collision Function

private void CheckCollision(PictureBox PicOne, PictureBox PicTwo, int offset)
{
    if (PicOne.Bounds.IntersectsWith(PicTwo.Bounds))
    {
        PicOne.Left = offset;

        int x = j[rand.Next(j.Length)];
        int y = j[rand.Next(j.Length)];

        if (ballXspeed < 0)
        {
            ballXspeed = x;
        }
        else
        {
            ballXspeed = -x;
        }

        if (ballYspeed < 0)
        {
            ballYspeed = -y;
        }
        else
        {
            ballYspeed = y;
        }
    }
}

This function, named CheckCollision, is responsible for detecting collisions between two PictureBoxes (PicOne and PicTwo) and handling the collision accordingly. Additionally, it takes an offset parameter, which determines where the ball should be placed after the collision occurs.

Inside the function, it first checks if the boundaries of PicOne intersect with the boundaries of PicTwo, indicating a collision between the two PictureBoxes. If a collision is detected, the function proceeds to handle it.

It starts by setting the Left property of PicOne to the specified offset, effectively repositioning PicOne. This likely ensures that after the collision, the ball (represented by PicOne) is placed at a specific location on the screen.

Next, the function generates random values for x and y using the rand.Next(j.Length) method. These values are chosen from the j array, which presumably contains various speed options for the ball.

Then, based on the direction of the ball’s movement (determined by ballXspeed and ballYspeed), the function updates these speed variables (ballXspeed and ballYspeed) accordingly. If the ball is moving leftward or upward (i.e., ballXspeed or ballYspeed is negative), the function sets ballXspeed or ballYspeed to a negative value of x or y, respectively, effectively changing the direction of the ball. Otherwise, if the ball is moving rightward or downward, it sets ballXspeed or ballYspeed to a positive value of x or y.

This function detects collisions between two PictureBoxes and handles them by repositioning one of them (PicOne), changing the direction of the ball’s movement (ballXspeed and ballYspeed), and setting it to a specific location (offset) after the collision.

Game Over Function

private void GameOver(string message)
{
    GameTimer.Stop();
    MessageBox.Show(message, "Moo Says: ");
    computerScore = 0;
    playerScore = 0;
    ballXspeed = ballYspeed = 4;
    computer_speed_change = 50;
    GameTimer.Start();
}

This function, named GameOver, is responsible for displaying a custom message when the game has ended. It takes a parameter message, which represents the message to be shown to the player upon game over.

Upon invocation, the function executes several steps:

  1. GameTimer.Stop(): This stops the game timer, effectively pausing the game. This ensures that no further game updates or actions occur while the game over message is displayed.
  2. MessageBox.Show(message, "Moo Says: "): This displays a message box to the player, showing the custom message passed as the message parameter. The message box title is set to “Moo Says: “.
  3. computerScore = 0 and playerScore = 0: These lines reset the scores for both the player and the computer to zero, effectively restarting the game.
  4. ballXspeed = ballYspeed = 4: These lines reset the horizontal and vertical speeds of the ball to 4, likely resetting the ball to its initial speed.
  5. computer_speed_change = 50: This line likely resets the parameter controlling the computer’s speed or behavior to its initial value.
  6. GameTimer.Start(): Finally, this restarts the game timer, allowing the game to resume after the game over message has been displayed and the necessary reset actions have been performed.

This function handles the necessary steps to end the game, including pausing the game timer, displaying a custom game over message, resetting scores and game parameters, and restarting the game timer to allow the player to start a new game.

The Game Timer Event

private void GameTimerEvent(object sender, EventArgs e)
       {
           ball.Top -= ballYspeed;
           ball.Left -= ballXspeed;

           this.Text = "Player Score: " + playerScore + " -- Computer Score: " + computerScore;

           if (ball.Top < 0 || ball.Bottom > this.ClientSize.Height)
           {
               ballYspeed = -ballYspeed;
           }
           if (ball.Left < -2)
           {
               ball.Left = 300;
               ballXspeed = -ballXspeed;
               computerScore++;
           }
           if (ball.Right > this.ClientSize.Width + 2)
           {
               ball.Left = 300;
               ballXspeed = -ballXspeed;
               playerScore++;
           }
           if (computer.Top <= 1)
           {
               computer.Top = 0;
           }
           else if (computer.Bottom >= this.ClientSize.Height)
           {
               computer.Top = this.ClientSize.Height - computer.Height;
           }

           if (ball.Top < computer.Top + (computer.Height / 2) && ball.Left > 300)
           {
               computer.Top -= speed;
           }
           if (ball.Top > computer.Top + (computer.Height / 2) && ball.Left > 300)
           {
               computer.Top += speed;
           }

           computer_speed_change -= 1;

           if (computer_speed_change < 0)
           {
               speed = i[rand.Next(i.Length)];
               computer_speed_change = 50;
           }

           if (goDown && player.Top + player.Height < this.ClientSize.Height)
           {
               player.Top += playerSpeed;
           }

           if (goUp && player.Top > 0)
           {
               player.Top -= playerSpeed;
           }

           CheckCollision(ball, player, player.Right + 5);
           CheckCollision(ball, computer, computer.Left - 35);

           if (computerScore > 5)
           {
               GameOver("Sorry you lost the game");
           }
           else if (playerScore > 5)
           {
               GameOver("You Won this game");
           }

       }

The GameTimerEvent function is the heart of the game’s logic, controlling various aspects of gameplay:

Firstly, it manages the movement of the ball across the screen by adjusting its position based on its current horizontal (ballXspeed) and vertical (ballYspeed) speeds.

Next, it updates the window title to display the current scores of both the player and the computer, providing players with real-time feedback on their progress.

The function also handles collisions between the ball and the boundaries of the game window. When the ball hits the top or bottom boundaries, its vertical movement direction is reversed to simulate bouncing off the walls.

Additionally, when the ball crosses either the left or right boundaries, it indicates a point scored. The function updates the scores accordingly, resets the ball’s position to the center of the screen, and reverses its horizontal movement direction to simulate a bounce off the side walls.

To ensure fair gameplay, the function prevents the computer-controlled paddle (computer) from moving outside the vertical boundaries of the game window.

Moreover, it adjusts the position of the computer-controlled paddle based on the ball’s position. If the ball is above the paddle’s center and on the computer’s side of the screen, the paddle moves up to intercept the ball. Conversely, if the ball is below the paddle’s center, the paddle moves down to intercept it.

At regular intervals, determined by the computer_speed_change variable, the speed of the computer-controlled paddle is adjusted randomly. This introduces variability to the computer’s movement, enhancing the game’s challenge and unpredictability.

Player input is also handled, allowing players to control their paddle’s movement using the keyboard. When the corresponding keys are pressed (goUp or goDown), the player-controlled paddle (player) moves up or down, respectively.

Collision detection between the ball and both the player’s and computer’s paddles is crucial for fair gameplay. When a collision occurs, the CheckCollision function is called to handle the collision, ensuring that the game mechanics function correctly.

Finally, the function checks if either the player or the computer has reached a score greater than 5. If so, it ends the game by calling the GameOver function with an appropriate message, displaying a message box to inform the player of the game’s outcome.

The GameTimerEvent function orchestrates the dynamic gameplay of the Pong game, managing movement, collisions, scoring, and game progression seamlessly.




Comment on this tutorial and let us know how you got on -