Click here to Skip to main content
15,891,657 members
Articles / Game Development
Tip/Trick

C# Simple Pingpong Game

Rate me:
Please Sign up or sign in to vote.
4.89/5 (25 votes)
27 Apr 2015CPOL4 min read 187.9K   13.6K   33   18
I was bored at work, so I made a classic game to entertain myself.

Introduction

This is the game we all know, the classic Ping pong game.

Image 1

This is a simple version of the game, but very customizable and fun.

The Creation Process

I started the program with just making the ball bounce back and fourth against the walls, left to right.

  • To do this, I first made what I call the WorldFrame. I added a Panel in the right size and location.
  • In this panel, I then created a picturebox representing the ball called "pb_Ball".
  • After this, I made a timer called "timer_Movement" and had its interval set to 1 ms.
    • The tick event of this timer is what makes the ball move, I use a Boolean to check whether to go left or right. If the ball hits either of the walls, it changes the Boolean and makes the ball go in the other direction.
    • Here's how I check if the ball hits the wall:
C#
public Boolean Collision_Left(PictureBox obj)
{
    if (obj.Location.X <= 0)
    {
        return true;
    }
    return false;
}

public Boolean Collision_Right(PictureBox obj)
{
    if (obj.Location.X + obj.Width >= WorldFrame.Width)
    {
        return true;
    }
    return false;
}

Within the tick event, where the check for which direction the ball is going, I use this to check if the ball hits the outer wall. If it's true, just change the Boolean value.

  • After this, I added 2 Pictureboxes representing the player, and the enemy
  • Made the player able to go up and down with 2 events, KeyPress Up/Down
C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)      
            {
                case Keys.W:
                case Keys.Up:
                    Player_Down = false;
                    Player_Up = true;
                    break;
                case Keys.S:
                case Keys.Down:
                    Player_Up = false;
                    Player_Down = true;
                    break;
            }
        }

        private void Form1_KeyUp(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.W:
                case Keys.Up:
                    Player_Up = false;
                    break;
                case Keys.S:
                case Keys.Down:
                    Player_Down = false;
                    break;
            }
        }

I use 2 Booleans to check if the player is going Up or Down, and then in the Movement timer, I check if any of the booleans is true. Then the player moves.

C#
if (Player_Up && !Collision_Up(pb_Player))
                {               
                    pb_Player.Top -= Speed_Player;    
                }
if (Player_Down && !Collision_Down(pb_Player))
                {               
                    pb_Player.Top += Speed_Player;
                }

The extra functions like Collision_Up can be found in the well documented source code.

  • When this was done, I made the same thing for the collision on the walls, but on the player instead.
    • This was a bit tricky, at the same time I did this I made it so it checks where it lands on the player and from there changes the balls "force" how fast it goes up/down
    • I did this by creating 5 small rectangles in front of the player, invisible and checks if the ball interacts with the player and these rectangles. If it does, then boom it goes away to the other direction.
    • Sample code (See full source files for everything):
    C#
    public Boolean Collision_Player(PictureBox tar)
            {
                if (tar.Bounds.IntersectsWith(pb_Player.Bounds))
                {
                    PictureBox temp1 = new PictureBox();
                    temp1.Bounds = pb_Player.Bounds;
                    temp1.SetBounds(temp1.Location.X + temp1.Width, temp1.Location.Y, 1, 10);
                    if (tar.Bounds.IntersectsWith(temp1.Bounds)) 
                    {
                        BallForce = 3;
                        return true;
                    }
                    temp1.SetBounds(temp1.Location.X, temp1.Location.Y + 5, 1, 10);
                    if (tar.Bounds.IntersectsWith(temp1.Bounds))
                    {
                        BallForce = 2;
                        return true;
                    }
                    temp1.SetBounds(temp1.Location.X, temp1.Location.Y + 10, 1, 10);
                    if (tar.Bounds.IntersectsWith(temp1.Bounds))
                    {
                        BallForce = 1;
                        return true;
                    }
    ...
  • With this, I created within the Movement timer event a check for if the Integer "BallForce" has any value below or above 0. If it's below 0, the ball goes down, above it goes up.
    • At the same time, as the ball goes left-right, it also goes up/down depending on the BallForce value.
    • Example, if I hit the ball at the top of the player. BallForce goes to 3 - Then the ball goes 1 pixel to the side and 3 pixels up every ms.
    C#
    if (BallForce > 0)
     {
         pb_Ball.Top -= BallForce;
     }
     if (BallForce < 0)
     {
         pb_Ball.Top -= BallForce;
     }
    
  • Along with this, I had to make sure that the ball didn't go flying up in space or down to hell. So with every movement up, it checks if the ball goes Above/Below the WorldFrame. And if it does, it reverses the BallForce.

    Inside timer_Movement tick event:

    C#
    if (pb_Ball.Location.Y <= 1)
    {
        BallForce = ReverseInt(BallForce, true, true);
    }
    if (pb_Ball.Location.Y + pb_Ball.Height >= WorldFrame.Height - 1)
    {
        BallForce = ReverseInt(BallForce, true, false);
    }
    
    C#
    public int ReverseInt(int x, Boolean Force = false, Boolean Negative = false)
            {
                if (Force) 
                {
                    if (Negative)  
                    {
                        if (x > 0) 
                        {
                            x = ~x + 1;
                        }         
                    }
                    else
                    {  
                        x = x - (x * 2);
                    }
                }
                else
                {
                    if (x > 0)
                    {
                        x = x - (x * 2);
                    }
                    else
                    {          
                        x = ~x + 1;
                    }
                }
                return x;
            }
  • How the Enemy moves is very simple
    • It checks whether the ball is above or below its central point. Then, it moves depending on its location.
    C#
    if (pb_Enemy.Location.Y + 28 < pb_Ball.Location.Y)
                    {   //Which is around 28 pixels below its Y coordinate
                        pb_Enemy.Top += Speed_Enemy;
                    }
                    else
                    {
                        pb_Enemy.Top -= Speed_Enemy;
                    }
  • I added this to a new timer called timer_Enemy, not sure why but it's there now.
  • I also added ex the same as the "if ball hits player on the top it bounces back" to the enemy with this.
  • After this, I added every customizable option, like change of colors/timers/speeds with a new form called SettingsForm.cs.
    • In this, I use a global setting in the file to change, and then in Form1, I just apply these every ms together with the movement. See source file for more information.

    There are score images also, not visible on the image above but they're there.

  • I made these by adding 5 pictureboxes on each side of the "Press space to start" label.
    • Then, I changed the original code that the ball bounces when it hits each side by changing the pictureboxes color to a black dot if the player/enemy messes up.
    • If 5 of these boxes turn black, the game is over.
    C#
    public void AddScore(PictureBox[] Arr)
    {
        for (int i = 0; i < Arr.Length; i++)
        {
            if (Arr[i].BackColor == ScoreColor)
            {   //And then changes it to black
                Arr[i].BackColor = Color.Black;
                break;
            }
        }
    
        if (Arr[4].BackColor == Color.Black)
        {
            GameOn = false;
            label_Start.Visible = true;
            RestoreScore();
            pb_Ball.Location = new Point(208, rng.Next(10, 190));
            pb_Player.Location = new Point(3, 67);
            pb_Enemy.Location = new Point(409, 67);
            Round = 0;
            label_Time.Visible = false;
        }
    }
    

This is the basic idea, basis of the code.

Points of Interest

I learned a lot, had fun creating this. Making it while at work so had ~10 minutes of time each day so it took me a few days to complete.

I learned a lot from this, I have made games before so some of the code is what I reuse/improve from my other games.

I'm not a pro, I just like programming and I like to share my idéas and my code with others to play with.

License

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


Written By
Help desk / Support
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionhow do I use settings? Pin
Ionuţ Alex7-May-16 4:46
Ionuţ Alex7-May-16 4:46 
QuestionThe code Pin
Member 122760712-Feb-16 0:10
Member 122760712-Feb-16 0:10 
GeneralFull screen Pin
Axel Steiner28-Aug-15 7:56
Axel Steiner28-Aug-15 7:56 
QuestionGood job, but only one advice Pin
Octanic10-May-15 7:04
Octanic10-May-15 7:04 
QuestionInvalid .zip archive? Pin
Ravi Bhavnani28-Apr-15 7:23
professionalRavi Bhavnani28-Apr-15 7:23 
AnswerRe: Invalid .zip archive? Pin
Hydeen28-Apr-15 21:32
Hydeen28-Apr-15 21:32 
GeneralRe: Invalid .zip archive? Pin
Ravi Bhavnani29-Apr-15 5:01
professionalRavi Bhavnani29-Apr-15 5:01 
QuestionCouple of suggestions Pin
Bjarne Nielsen28-Apr-15 2:24
Bjarne Nielsen28-Apr-15 2:24 
AnswerRe: Bad code Pin
_Vitor Garcia_28-Apr-15 3:03
_Vitor Garcia_28-Apr-15 3:03 
AnswerRe: Bad code Pin
Hydeen28-Apr-15 3:24
Hydeen28-Apr-15 3:24 
GeneralRe: Bad code Pin
Bjarne Nielsen28-Apr-15 4:21
Bjarne Nielsen28-Apr-15 4:21 
GeneralRe: Bad code Pin
George200628-Apr-15 10:24
George200628-Apr-15 10:24 
I normally avoid replying to trolls, but I am going to be sucked into this one. DFTT DFTT DFTT... too late

You called this "bad code", yet different naming conventions and personal preferences do not make "bad code". Is it slightly less readable/maintainable in the long run, yes. But is it really "bad code"?

Many shops still use variable names with "pb", "tbox", "lbl" etc prefacing variable names. And to be honest, I personally like it. But that is my preference. When I open source code in something that does have intellisense, having variable names with a type prefacing the variable makes that code MORE readable. Telling someone to use variable names that are more descriptive, then telling them to use variable names that are LESS descriptive, that is confusing advice. Especially when that is YOUR preference. Personally I would always recommend the MOST descriptive name, without making insanely long variable names. That said, I do agree with Bjarne that single letter variable names should usually be avoided in lieu of a more descriptive name.

As for taking the author to issue over having "pb" prefacing the variable name - simply because he might someday make the ball object be something other than a PictureBox.... The PictureBox variable name has "pb" before it because it *is* a pb. If he ever changes the PictureBox to be something else, that variable name is nothing that a global search/replace can't fix in 2 seconds. Again, this is not "bad code".

As for the AddScore() function, granted there are better/safer/cleaner ways of doing collision detection, but it works! And I would guess that the author learned through experience that there are complications with the way this is written. But if you as the seasoned developer are truly trying to help, *show* a better or more efficient way rather than being someone who lives to publicly poo on other's contributions. What about complimenting him on writing documented code? You ignored the fact that he is not a full-time developer, and went right for the negative. Kudos for comments in your code Author!

Calling this "bad code" over that one function is being a troll plain and simple. Nothing else you pointed out is other than your own personal preference, and definitely does not justify calling this "bad code" to humiliate the author for his contribution, which you did in the subject line. Then in your reply you wrote "it's not meant to be offensive in any way". How else is he supposed to take "bad code" as a subject line? A subject line of "Some suggestions" followed with actual suggestions, instead of complete criticism... that would be non-offensive. I guarantee that if you posted some of your own code, and someone dumped on it in public like you did here, you would take offense.

Others may have some of the same preferences you do, and they may even be in the majority, but your difference in preference from that of the author does not make his code "bad code".

George
GeneralRe: Bad code Pin
Jacob Himes28-Apr-15 12:32
professionalJacob Himes28-Apr-15 12:32 
AnswerRe: Bad code Pin
PeejayAdams28-Apr-15 3:40
PeejayAdams28-Apr-15 3:40 
GeneralRe: Bad code Pin
Octanic10-May-15 7:08
Octanic10-May-15 7:08 
AnswerRe: Couple of suggestions Pin
Bjarne Nielsen28-Apr-15 21:52
Bjarne Nielsen28-Apr-15 21:52 
QuestionBrilliant, why didn't I think of that ... Pin
peterkmx28-Apr-15 1:29
professionalpeterkmx28-Apr-15 1:29 
GeneralMy vote of 5 Pin
Sachin Mahandule27-Apr-15 22:16
professionalSachin Mahandule27-Apr-15 22:16 

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.