Ping Pong

We have everything we need, we've seen the code, the circuit is built. Lets make a game! We'll start by breaking the game down into steps. This will make it easier to develop, and is called divide and conquer, a really common strategy for solving hard problems with code.



Divide and Conquer

So, how many steps are there in making ping pong? It can be supprisingly complicated if you don't think about it a little first! We need a score keeper, two bats, collision detection, input read code, and to display all this on the screen. All fast enough so it doesn't run at a snails pace! Harder than you thought huh? Never fear, we will build things up step by step. Below is a list of all the stages we will go through in building the game.

  1. Keeping Score
  2. Player Bats
  3. Moving The Ball And Collisions
  4. Winning!

Don't worry if you get stuck. The workshop helpers will be happy to lend a hand. You can also take a quick peek at the finished code here if you are really stuck.

Keeping Score

Pong is a two player game, so we need to have two scores, and to be able to read inputs from both players individually. Below is some example code with small bits missing, can you fill them in so we can read the position of the bats properly and display each players score?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <TFT.h>  // Arduino LCD library
#include <SPI.h>

// pins to control the screen 
#define CS   7
#define DC   0
#define RST  1

// Pins for the player paddle positions
#define PIN_P1_BAT A2
#define PIN_P2_BAT A3

// create an instance of the library
TFT screen = TFT(CS, DC, RST);

// Player Scores
int score_p1;
int score_p2;
int position_p1;
int position_p2;

void drawPlayerScores()
{
    // This function will draw a very simple "X - Y" string where
    // X and Y are the scores for each player.

    // Set the text color to black.
    screen.stroke(0,0,0);

    // We will print five letters, so make our array five long.
    char toPrint[5];
    String S = "";
    S += String(score_p1);  // Add the player 1 score
    S += " - ";             // Separate the two scores
    // ......               // Add the player 2 score
    S.toCharArray(toPrint,5); // Convert the string to something we can show.
    // ......               // Display the text!
}

void getPlayerPositions(){
    // Read player 1 position
    // What do you think the map function does?
    position_p1 = map(analogRead(PIN_P1_BAT),0,1023-bat_h,0,screen_h);
    
    // Read player 2 position
    // ....
}

void setup() {

    // Set up the screen.
    screen.begin();
    screen.background(255,255,255);
    screen.fill(255,255,255);
    screen.stroke(0, 0, 0);
    screen.setTextSize(1);

    // Set up the player scores and bat positions.
    score_p1    = 0;
    score_p2    = 0;
    position_p1 = 0;
    position_p2 = 0;
}

void loop() {

    // Read the player positions.
    getPlayerPositions();

    // Draw the player scores.
    drawPlayerScores();

    delay(500);
}

Got it working? This code will form the skeleton of our game, so all of the other code will be added to what we have already written.

Player Bats

So, we can draw the player scores, and read where the bats should be. But we don't draw the bats! Let's add a function that uses the positions we read in the previous step to draw them in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <TFT.h>  // Arduino LCD library
#include <SPI.h>

// pins to control the screen 
#define CS   7
#define DC   0
#define RST  1

// Pins for the player paddle positions
#define PIN_P1_BAT A2
#define PIN_P2_BAT A3

// create an instance of the library
TFT screen = TFT(CS, DC, RST);

const int bat_w = 5;
const int bat_h = 10;

const int screen_w = 320;
const int screen_h = 240;

// Player Scores
int score_p1;
int score_p2;
int position_p1;
int position_p2;

void drawPlayerBats()
{
    // Draw player 1's bat. First, clear the old area the bat took up.
    screen.fill(255,255,255);
    screen.stroke(255,255,255);
    screen.rect(1,position_p1,bat_w,bat_h);

    // Clear player 2's bat. How do we know the position of their bat.
    // ......

    // Read the player positions. This changes position_p1 and position_p2
    getPlayerPositions();

    // Now we can draw the new bats
    // Draw player 1's bat first.
    screen.fill(0,255,0); // Make player 1's bat green.
    screen.rect(1,position_p1,bat_w,bat_h);

    // Now draw player 2's bat. Remember, it needs to be on the other side
    // of the screen.
    // .....
}

void drawPlayerScores()
{
    // This function will draw a very simple "X - Y" string where
    // X and Y are the scores for each player.

    // Set the text color to black.
    screen.stroke(0,0,0);

    // We will print five letters, so make our array five long.
    char toPrint[5];
    String S = "";
    S += String(score_p1);  // Add the player 1 score
    S += " - ";             // Separate the two scores
    // ......               // Add the player 2 score
    S.toCharArray(toPrint,5); // Convert the string to something we can show.
    // ......               // Display the text!
}

void getPlayerPositions(){
    // Read player 1 position
    position_p1 = analogRead(PIN_P1_BAT) / 4;
    
    // Read player 2 position
    // ....
}

void setup() {

    // Set up the screen.
    screen.begin();
    screen.background(255,255,255);
    screen.fill(255,255,255);
    screen.stroke(0, 0, 0);
    screen.setTextSize(1);

    // Set up the player scores and bat positions.
    score_p1    = 0;
    score_p2    = 0;
    position_p1 = 0;
    position_p2 = 0;
}

void loop() {

    // Move the "getPlayerPositions()" function call into the
    // new drawPlayerBats() function. Can you figure out why?

    // Draw the player scores.
    drawPlayerScores();

    // Draw the bats using the positions we read a moment ago.
    drawPlayerBats();

    delay(500);
}

Moving The Ball

Ping pong is no fun without the ball! We need to keep track of the position of the ball, it's speed, direction, whether it has collided with a wall, or a bat, and keep score. Phew, that's a lot! Remember though, we are dividing and conquering this problem, so we can add the code in small steps. In fact, we can even add it all in a single function! Have a look at lines 75 to 130 and try to work out which bits need adding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#include <TFT.h>  // Arduino LCD library
#include <SPI.h>

// pins to control the screen 
#define CS   7
#define DC   0
#define RST  1

// Pins for the player paddle positions
#define PIN_P1_BAT A2
#define PIN_P2_BAT A3

// create an instance of the library
TFT screen = TFT(CS, DC, RST);

const int bat_w = 5;
const int bat_h = 10;

const int ball_speed = 1;
const int ball_size  = 3;

const int screen_w = 320;
const int screen_h = 240;

// Player Scores
int score_p1;
int score_p2;
int position_p1;
int position_p2;

int ball_pos_x;
int ball_pos_y;
int ball_dir_x;
int ball_dir_y;

void updateBall();
{
    // First, rub out the previous ball we drew.
    screen.fill(255,255,255);
    screen.stroke(255,255,255);
    screen.circle(ball_pos_x, ball_pos_y, ball_size);

    // Now let's update where the ball should be.

    // Should it bounce of the walls?
    if(ball_pos_y > screen_h)
    {
        ball_dir_y = -ball_speed;
    } 
    else if (ball_pos_y < 0)
    {
        ball_dir_y  = ball_speed;
    }

    // Should it bounce off player 1's bat?
    if(ball_pos_x < bat_w &&
       ball_pos_y > position_p1 &&
       ball_pos_y < position_p1 + bat_h)
    {
        ball_dir_x = ball_speed;
    }
    // Should it bounce off player 2's bat?
    else if(ball_pos_x > screen_w - bat_w &&
       ball_pos_y > position_p2 &&
       ball_pos_y < position_p2 + bat_h)
    {
        ball_dir_x = -ball_speed;
    }
    // Has player one scored?
    else if (ball_pos_x > screen_w)
    {
        // increase player one's score
        score_p1 += 1;
        // reset the ball position to the middle of the screen.
        ball_pos_x = screen_w/2;
        ball_pos_y = screen_h/2;

        // Where should we put the ball after a point is scored?
    }
    // Has player two scored?
    else if(ball_pos_x < 0)
    {
        // .......
    }

    // Make the ball move in the right direction.
    ball_pos_x = ball_pos_x + ball_dir_x;
    ball_pos_y = ball_pos_y + ball_dir_y;

    // Now draw the new ball
    screen.fill  ( // ..... What color shall the ball be?
    screen.circle( // .....
}

void drawPlayerBats()
{
    // Draw player 1's bat. First, clear the old area the bat took up.
    screen.fill(255,255,255);
    screen.stroke(255,255,255);
    screen.rect(1,position_p1,bat_w,bat_h);

    // Clear player 2's bat. How do we know the position of their bat.
    // ......

    // Read the player positions. This changes position_p1 and position_p2
    getPlayerPositions();

    // Now we can draw the new bats
    // Draw player 1's bat first.
    screen.fill(0,255,0); // Make player 1's bat green.
    screen.rect(1,position_p1,bat_w,bat_h);

    // Now draw player 2's bat. Remember, it needs to be on the other side
    // of the screen.
    // .....
}

void drawPlayerScores()
{
    // This function will draw a very simple "X - Y" string where
    // X and Y are the scores for each player.

    // Set the text color to black.
    screen.stroke(0,0,0);

    // We will print five letters, so make our array five long.
    char toPrint[5];
    String S = "";
    S += String(score_p1);  // Add the player 1 score
    S += " - ";             // Separate the two scores
    // ......               // Add the player 2 score
    S.toCharArray(toPrint,5); // Convert the string to something we can show.
    // ......               // Display the text!
}

void getPlayerPositions(){
    // Read player 1 position
    // What do you think the map function does?
    position_p1 = map(analogRead(PIN_P1_BAT),0,1023-bat_h,0,screen_h);
    
    // Read player 2 position
    // ....
}

void setup() {

    // Set up the screen.
    screen.begin();
    screen.background(255,255,255);
    screen.fill(255,255,255);
    screen.stroke(0, 0, 0);
    screen.setTextSize(1);

    // Set up the player scores and bat positions.
    score_p1    = 0;
    score_p2    = 0;
    position_p1 = 0;
    position_p2 = 0;

    // Set up the ball position and direction of travel.
    ball_pos_x  = screen_w/2;
    ball_pos_y  = screen_h/2;
    ball_dir_x  = ball_speed;
    ball_dir_y  = ball_speed;
}

void loop() {

    // Move the "getPlayerPositions()" function call into the
    // new drawPlayerBats() function. Can you figure out why?

    // Draw the player scores.
    drawPlayerScores();

    // Draw the bats using the positions we read a moment ago.
    drawPlayerBats();

    // Draw and update the ball position.
    updateBall();
}

Now, we are nearly there! We have the ball, the bats and a score counter. Now we just need to add a couple of functions to make starting and finishing the game a bit easier.

Winning!

So, at the moment, we are just thrust straight into the game and don't get any chance to get ready! Let's add a little count down to the start. There is some code below that displays a little count down from three. Can you work out where to put it in your code?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void startCountdown()
{
    int countdown = 4;
    // Make the text size nice and big.
    screen.setTextSize(3);

    while(countdown > 0){
        countdown = countdown - 1;
        
        // Make a string to show the score.
        char toPrint[2];
        String(countdown).toCharArray(toPrint,2);

        // screen.stroke ....... text colour
        // screen.text   ....... what to show and where
        
        delay(1000);    // Wait for one thousand milliseconds / one second.

        // Rub out the old text. Didn't we do something like this before?
        // screen.stroke .......
        // screen.fill .......
        // screen.rect .......
    }
    // Put the text size back to normal.
    screen.setTextSize(1);
}

It might be nice to have this count down after each time someone scores. Can you make that happen by adding only two more lines of code?

So, we can start the game nicely now, and play as well. We just need some sort of victory condition. Let's add one last function that checks if someone has won (first to score three maybe?) and show a message when they do. Again, can you work out where to put this function, and call it in you code?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void checkVictory()
{
    if(score_p1 >= 3)
    {
        // Make the font size nice and big.
        screen.setTextSize(2);

        // Print the message.
        screen.stroke(0,0,0);
        screen.text("Player 1 Wins!", 5,10);

        // Wait for a few seconds.
        delay (5000);
    }
    else if(score_p2 >= 3)
    {
        // .......
    }

    // What shall we do now if someone has one? Can we call one function to
    // go back to the beginning again?
}

The Finished Product

Congratulations! You've built your own Ping Pong game!

Since you found that so easy, can you make it better? How about using the buttons some how? Perhaps you think the colours are boring, or maybe you want to make another game entirely. This tutorial has told you everything you need to make awesome arcade games using the Arduino, so get hacking!

Be sure to post a picture of your creations to our Facebook page as well!


< Buttons And Dials | Home >