The Pong Tutorial With Flash

This tutorial shows you how to make a Pong game. Pong is a minimal example of what a game is. Pong is simple to make, but still contains the structure of a real game. Other and more complex games would still be using the same basic concepts that are needed to create a Pong game.

The main focus of this tutorial is on showing you how to create a game from the ground up by using simple programming constructs that can be used to structure the game into logical parts. The game logic is explained using C++ code fragments, but implementations can use any other language. An example implementation of the game is available that was made using Flash ActionScript 3 at the end of the tutorial.

An actual C++ implementation of the game using SDL 2.0 can be found in my tutorial Porting Pong From Flash To C++ and SDL 2.0.

The Pong Tutorial With Flash

The Pong Tutorial With Flash

Click here to go directly to the end of this article and play the game.

Pong

Below you can see an impression of the original Pong game. The image shows the basic elements that are in the game. There are two short white lines on either side of the screen that represent the paddles for the players. There is a white dot, the ball. There is a dotted line dividing the playing field. Finally, there are two numbers that represent the score of each player.

The left paddle is controlled by the player, while the paddle on the right is controlled by the computer. The ball bounces against the top and bottom of the playing field and against the paddles. When the ball collides with the left or right side of the playing field, the opposing player scores a point.

The Original Pong

The Original Pong

To create a Pong game of our own, we must implement the following items:

  • Real-time updating game world
  • Handle player input
  • Artificial intelligence
  • Ball and paddle collisions
  • Scoring system

Creating Structure

When you are playing a game, you are seeing the end result of a development process, the complete picture. Every element on the screen is tied together to form a uniform experience. We want to create a Pong game, so we know what the end result will be. To make is easier for us to implement the game, we have to break it down into smaller logical elements. This way, we divide one big problem into smaller sub-problems that are easier to solve. When we have identified the elements of the game, we can add interactions between them.

Looking at the image of the original Pong game, we can identify the following types of logical elements:

  • Ball
  • Paddles
  • Playing field
  • Scores

If we are programming a game in an object-oriented language, we can divide the logical elements of the game in different classes. Classes are the perfect tool to create structure within the game, as all of the properties and logic of one aspect of the game, such as the ball, can be encapsulated in a single self-contained unit. Below we define some classes with properties for the elements that we discussed earlier. When we are going to implement the game, we may not even use some of these classes, but structuring the game before implementing the code is a good thing. For this tutorial, I am using C++ to demonstrate some coding concepts. The actual game can be implemented in any other language, using the same concepts. My own implementation was done using Flash ActionScript 3 that can be downloaded at the end of this tutorial.

Ball

The ball has a position, dimensions and a direction of movement.

1
2
3
4
5
class Ball {
public:
    float x, y, width, height;
    float dirx, diry;
};

Paddles

Paddles have a position and dimensions. They can only move in two directions, upwards and downwards.

1
2
3
4
class Paddle {
public:
    float x, y, width, height;
};

Playing Field

The playing field cannot change its position. It does interact with the ball and the paddles.

1
2
3
4
class PlayingField{
public:
    float x, y, width, height;
};

Scores

Scores are part of the graphical user interface of the game. They are positioned somewhere on the screen, but they do not interact directly with any of the other elements of the game.

1
2
3
4
5
class Score {
public:
    float x, y;
    int score;
};

The Game Loop

Now that we have identified the logical units in the game and defined some classes, we can think about simulations and interactions. We must create a game world that is updated as fast as possible, which allows us to make a fast-paced action game that reacts to the player in near real-time.

Below we define a rough sketch of a game loop, the basis of a real-time updating game world with interactions, together with a line-by-line explanation.

1
2
3
4
5
6
7
while (running) {
    HandlePlayerInput();
    UpdateBall();
    CheckCollisions();
    AI();
    Render();
}

Line 1: The while loop represents our game loop. Everyone is familiar with the concept of frames per second in games. At every frame, updates are made to the game and an image is shown to the player. These updates are done within the game loop. Every line of code from line 1 to line 6 is included in the game loop and will run every frame. When the framerate of the game is fast enough, the game can react to the player in real-time, making the game interactive and fluent.

Line 2: At each frame, we have to know if the player has interacted with the game. If the player has moved the mouse for example, his paddle would have to be moved accordingly.

Line 3: The position of the ball needs to be updated, depending on its speed and direction.

Line 4: When the ball hits a paddle, or when the ball bumps against the top or bottom of the playing field, the direction of the ball needs to be adjusted.

Line 5: The artificial intelligence needs to update the position of its paddle.

Line 6: The image on the screen is updated.

Interactions

We have structured our game into classes which can be instantiated into objects and made a gameloop. We now have to add interactions in the gameloop between the objects of the game.

Moving the Ball

The ball has a position and a direction of movement. The length of the direction of movement vector, indicates how many pixels per second the ball has to move. At each frame, we have to update the position of the ball. We can do this by adding the direction vector multiplied by the time since the previous frame to the current position of the ball. The following function is added to the Ball class.

1
2
3
4
5
// Update the position of the ball, dt is in seconds
void Ball::Step(float dt) {
    x += dirx * dt;
    y += diry * dt;
}

Moving The Paddles

The player and the AI must be able to move their paddle. The paddle can move in only two directions, upwards and downwards. It cannot move past the playing field. In other words, the bounding box of the paddle needs to stay within the bounding box of the playing field.

We are going to create a function that sets the y-coordinate of a paddle. The function will take two arguments. The first argument determines which of the two paddles will be moved. The other argument is the new y-coordinate of the selected paddle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// There are two paddles and a playing field defined earlier
Paddle paddle[2];
PlayingField playingfield;
 
// (...)
 
// Set the y-coordinate of paddle i
void SetPaddleY(int i, float y) {
    // Make sure the paddle is within the bounding box of the playing field
    float newy;
    if (y < playingfield.y) {
        // Upper bound
        newy = playingfield.y;
    } else if (y + paddle[i].height > playingfield.y + playingfield.height) {
        // Lower bound
        newy = playingfield.y + playingfield.height - paddle[i].height;
    } else {
        newy = y;
    }
 
    // Set the new y-coordinate
    paddle[i].y = newy;
}

We want to have the paddle for the player follow the position of the mouse. The following function must be called when there is a Mouse Move event.

1
2
3
4
void MouseMoved(int mousex, int mousey) {
    // Align the center of the paddle with the mouse position
    SetPaddleY(0, mousey - paddle[0].height / 2);
}

Ball-Playing Field Collisions

When the ball collides with the top or bottom of the playing field, we want to change the direction of the ball. When the ball collides with the left or right side of the playing field, someone scored a point and the position and direction of the ball need to be reset appropriately.

We want to respond to a collision with the top or bottom of the playing field, by reflecting the direction of movement of the ball in the axis that is perpendicular to the side of the playing field. The following image explains our collisions response.

Ball-Field Collision Response

Ball-Field Collision Response

We want to keep the speed of the ball constant, therefore we are going to add a function to the Ball class that makes it easier to set the correct movement directions of the ball. The dirx and diry variables together form a vector. This vector needs to be normalized and multiplied with a defined speed to maintain a constant ball speed. We define the following function in the Ball class that sets the new direction variables and scales them to the correct speed.

1
2
3
4
5
6
7
8
9
// Define a ball speed in pixels per second
const float BALL_SPEED = 550;
 
void Ball::SetDirection(float newdirx, float newdiry) {
    // Normalize the direction vector and multiply with BALL_SPEED
    float length = sqrt(newdirx * newdirx + newdiry * newdiry);
    dirx = BALL_SPEED * (newdirx / length);
    diry = BALL_SPEED * (newdiry / length);
}

We create the following function that checks for ball-playing field collisions. It reflects the ball when it collides with the top or bottom of the playing field and it resets the ball and increases the score when the ball collides with the left or right side of the playing field.

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
// These are defined earlier
Paddle paddle[2];
PlayingField playingfield;
Ball ball;
Score score[2]
 
// (..)
 
void CheckPlayingFieldCollisions() {
    // Top and bottom collisions
    if (ball.y < playingfield.y) {
        // Top
        // Keep the ball within the playing field and reflect the y-direction
        ball.y = playingfield.y;
        ball.diry *= -1;
    } else if (ball.y + ball.height > playingfield.y + playingfield.height) {
        // Bottom
        // Keep the ball within the playing field and reflect the y-direction
        ball.y = playingfield.y + playingfield.height - ball.height;
        ball.diry *= -1;
    }
 
    // Left and right collisions
    if (ball.x <= playingfield.x) {
        // Left
        // Player 2 scores
        score[1].score += 1;
 
        // Reset the position and direction of the ball
        ball.x = paddle[1].x - 2.0f * ball.width;
        ball.y = playingfield.y + playingfield.height / 2.0f - ball.height / 2.0f;
        ball.SetDirection(-1, -1);
    } else if (ball.x + ball.width >= playingfield.x + playingfield.width) {
        // Right
        // Player 1 scores
        score[0].score += 1;
 
        // Reset the position and direction of the ball
        ball.x = paddle[0].x + paddle[0].width + ball.width;
        ball.y = playingfield.y + playingfield.height / 2.0f - ball.height / 2.0f;
        ball.SetDirection(1, -1);
    }
}

Ball-Paddle Collisions

Finally, we have to check for collisions between the ball and the paddles. When the ball collides with a paddle, we want the ball to be reflected in a direction based on where the ball hits the paddle. We don’t want to just flip the x-direction of the ball, we want to give the player more control over the reflection direction of the ball.

The image below shows our desired collision response. When the ball hits the paddle at one of the red dots, the ball will be reflected in the direction that is indicated by the outgoing arrow. The incoming angle of the ball is discarded in this process.

Ball-Paddle Collisions Response

Ball-Paddle Collisions Response

Handling this collision can be split into two parts. First, we create a function that checks if the ball is in a collision with the paddle. The second part handles the collision response, reflecting the ball in a certain way. Below we define a function to check if there is a collision between a specified paddle and the ball. The ball is represented as a bounding box, as well as the paddle.

1
2
3
4
5
6
7
8
bool PaddleCollision(int i) {
    // Check paddle bounding box against ball bounding box
    if (ball.x + ball.width > paddle[i].x && ball.x < paddle[i].x + paddle[i].width &&
        ball.y + ball.height > paddle[i].y && ball.y < paddle[i].y + paddle[i].height) {
        return true;
    }
    return false;
}

We define a reflection function that takes two parameters. The first one defines which paddle we are reflecting the ball off. The second parameter defines the y-coordinate of the hit point on the paddle. The function returns a reflected y-direction.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
float GetReflection(int i, float hity) {
    // Make sure the hity variable is within the height of the paddle
    if (hity < 0) {
        hity = 0;
    } else if (hity > paddle[i].height) {
        hity = paddle[i].height;
    }
 
    // Everything above the center of the paddle is reflected upward
    // while everything below the center is reflected downward
    hity -= paddle[i].height / 2.0f;
 
    // Scale the reflection, making it fall in the range -2.0f to 2.0f
    return 2.0f * (hity / (paddle[i].height / 2.0f));
}

We can now use these functions to check for ball-paddle collisions and give a correct response by defining a CheckPaddleCollisions function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void CheckPaddleCollisions() {
    // Get the center y-coordinate of the ball
    float ballcentery = ball.y + ball.height / 2.0f;
 
    // Left paddle collisions
    if (PaddleCollision(0)) {
        ball.x = paddle[0].x + paddle[0].width;
        ball.SetDirection(1, GetReflection(0, ballcentery - paddle[0].y));
    }
 
    // Right paddle collisions
    if (PaddleCollision(1)) {
        ball.x = paddle[1].x - ball.width;
        ball.SetDirection(-1, GetReflection(1, ballcentery - paddle[1].y));
    }
}

Artificial Intelligence

We can create a very basic artificial intelligence for our Pong game. The AI should give the player the opportunity to score points, so we have to make it imperfect. Lets define some behavior we want our AI to show:

  • Chase the ball when it is going towards the AI player.
  • Only chase the ball when it is on its side of the playing field.
  • Lag a bit behind the ball, to make it imperfect.
  • Move towards the center when the ball is moving away from the AI player.

Implementing this behavior is pretty straightforward and is explained in the code below:

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
// These are defined earlier
Paddle paddle[2];
PlayingField playingfield;
Ball ball;
Score score[2]
 
// (..)
 
// Define the AI paddle speed in pixels per second
const float AI_SPEED = 300;
 
function AI(float dt):void {
    // Calculate how much the paddle can move in this frame
    float dy = AI_SPEED * dt;
 
    // Imperfect ai
    if (ball.x > playingfield.width / 2 && ball.dirx > 0) {
        // Lag behind the ball, when ball is in sight
        if (paddle[1].y + paddle[1].height / 2 < ball.y + ball.height / 2 - dy) {
            SetPaddleY(1, paddle[1].y + dy);
        } else if (paddle[1].y + paddle[1].height / 2 > ball.y + ball.height / 2 + dy) {
            SetPaddleY(1, paddle[1].y - dy);
        }
    } else {
        // Move to the center, when ball is far away
        if (paddle[1].y + paddle[1].height / 2 < playingfield.height / 2 - dy) {
            SetPaddleY(1, paddle[1].y + dy);
        } else if (paddle[1].y + paddle[1].height / 2 > playingfield.height / 2 + dy) {
            SetPaddleY(1, paddle[1].y - dy);
        }
    }
}

Pong Example

I have made an implementation of the game using Flash ActionScript 3 with FlashDevelop. Below you can see how the game turned out and play it. If you want to see a C++ implementation, have a look at my tutorial Porting Pong From Flash To C++ and SDL 2.0. The project files and the full source code is available here.

Sorry, either Adobe flash is not installed or you do not have it enabled