Seventh JavaScript Project: Creating the Pong Game

Seventh JavaScript Project: Creating the Pong Game

Welcome back to our JavaScript project series! Today, we are diving into the creation of a classic arcade game - PONG. Originally developed in 1972, this game is simple yet excellent for testing and refining our skills in HTML, CSS, and, most importantly, JavaScript.

What Will We Learn

  1. Working with Canvas: We'll explore how to create a game canvas using HTML5 Canvas. Canvas provides us with a surface on which we can draw and update game graphics.
  2. Keyboard Controls: We'll learn how to capture keyboard events in JavaScript to control paddle movement.
  3. Game Logic: We'll implement the game logic that governs paddle movement, ball movement, and scorekeeping.
  4. Against the Computer or Against a Player: We'll add the option to play against the computer or against another player on the same device.
  5. Difficulty Levels: We'll introduce difficulty levels for players to choose between easy, medium, and hard.

Getting Started

  1. Open your favorite text editor.
  2. Create a new directory for your project and place three files inside: index.html, style.css, and script.js.
  3. Copy and paste the code from the sections below.

Introduction to the Pong Game

Pong is a game where two players control paddles on either side of a rectangular canvas. The ball moves back and forth, and the goal is for players to bounce the ball back towards their opponent. Each player earns points when they overcome their opponent. The objective is to reach a specific score first.

Code in Action

Without further ado, let's dive into the code and start building our version of the Pong game in JavaScript. Below, you'll find the complete code for HTML, CSS, and JavaScript.

HTML (index.html)

First, we will create our classic HTML structure:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>PONG Game</title>
</head>

<body>
    <!-- Game Menu -->
    <div id="menu">
        <label for="difficulty">Choose difficulty:</label>
        <select id="difficulty" name="difficulty">
            <option value="easy">Easy</option>
            <option value="medium">Medium</option>
            <option value="hard">Hard</option>
        </select>

        <label for="mode">Choose mode:</label>
        <select id="mode" name="mode">
            <option value="single">Computer</option>
            <option value="multi">Player</option>
        </select>

        <button onclick="startGame()">Start Game</button>
    </div>

    <!-- Game Canvas -->
    <canvas id="pongCanvas" width="600" height="400" style="display:none;"></canvas>

    <!-- JavaScript File -->
    <script src="script.js"></script>
</body>

</html>        

Description:

  1. Difficulty and Mode <select> Elements: Allow the user to choose the game difficulty and mode.
  2. Start Button (<button>): Initiates the startGame() function upon clicking.
  3. <canvas> Element: Used for rendering game elements.

CSS (style.css)

We will continue with the styles

body {
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background-color: #222;
}

canvas {
    border: 2px solid #fff;
}

#menu {
    text-align: center;
    color: #fff;
    margin-bottom: 20px;
}

label, select, button {
    margin: 5px;
}        

This CSS file defines basic styles for the body, canvas, and game menu elements to ensure a visually appealing and centered layout. If you have any specific styling preferences or additional elements to describe, feel free to let me know!

JavaScript (script.js)

First we write the constants

Constants:

canvas and ctx:

// Get the reference to the game canvas and its 2D context
const canvas = document.getElementById('pongCanvas');
const ctx = canvas.getContext('2d');        

The canvas constant represents the HTML canvas element where the game is rendered, and ctx is its 2D rendering context.


paddleSpeed, ballSpeed, and winningScore:

// Set the speed of paddles, ball, and winning score
const paddleSpeed = 5;
const ballSpeed = 3;
const winningScore = 5;        

These constants define the speed of the paddles (paddleSpeed), the speed of the ball (ballSpeed), and the score needed to win the game (winningScore).


Variables:

Paddles and Ball:

// Vertical position of the left and right paddles
let leftPaddleY = 160;
let rightPaddleY = 160;

// Speed of movement for the left and right paddles
let leftPaddleVelocity = 0;
let rightPaddleVelocity = 0;

// Position of the ball on the canvas
let ballX = 300;
let ballY = 200;

// Speed of movement for the ball on the x and y axes
let ballXSpeed = ballSpeed;
let ballYSpeed = ballSpeed;        

These variables define the position and movement speed of paddles and the ball on the canvas. leftPaddleY and rightPaddleY represent the vertical positions of the left and right paddles. leftPaddleVelocity and rightPaddleVelocity store the movement speed of the paddles. ballX and ballY specify the position of the ball, and ballXSpeed and ballYSpeed represent its movement speed on the x and y axes.


Scores and Difficulty:

// Scores of the left and right players
let leftPlayerScore = 0;
let rightPlayerScore = 0;

// Difficulty of artificial intelligence
let aiDifficulty = 0.1;

// Indicates whether the second player is controlled by the computer
let player2IsComputer = false;        

Variables leftPlayerScore and rightPlayerScore store the current scores of the left and right players. aiDifficulty sets the difficulty of artificial intelligence in single-player mode. player2IsComputer indicates whether the second player (right player) is controlled by the computer.


Others:

let gameStarted = false;        

The variable gameStarted indicates whether the game is running or in menu mode.


Event Listeners:

// Event listener for keydown events
document.addEventListener('keydown', (event) => {
    if (gameStarted) {
        if (event.key === 'ArrowUp') {
            // Move the right paddle up
            rightPaddleVelocity = -paddleSpeed;
        } else if (event.key === 'ArrowDown') {
            // Move the right paddle down
            rightPaddleVelocity = paddleSpeed;
        } else if (event.key === 'w') {
            // Move the left paddle up
            leftPaddleVelocity = -paddleSpeed;
        } else if (event.key === 's') {
            // Move the left paddle down
            leftPaddleVelocity = paddleSpeed;
        }
    }
});

// Event listener for keyup events
document.addEventListener('keyup', (event) => {
    if (gameStarted) {
        if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
            // Stop vertical movement of the right paddle
            rightPaddleVelocity = 0;
        } else if (event.key === 'w' || event.key === 's') {
            // Stop vertical movement of the left paddle
            leftPaddleVelocity = 0;
        }
    }
});        

These event listeners are responsible for handling keyboard events. When a key is pressed (keydown), the code checks if the game is started (gameStarted) and then adjusts the velocity of the paddles accordingly. The keys 'ArrowUp' and 'ArrowDown' control the right paddle, while 'w' and 's' control the left paddle.

When a key is released (keyup), the corresponding paddle's velocity is set to 0, stopping its movement.

These event listeners contribute to the interactivity of the game by allowing players to control the paddles using the arrow keys and 'w' and 's' keys.


Function startGame():

function startGame() {
    // Get elements for difficulty and game mode
    const difficultySelect = document.getElementById('difficulty');
    const modeSelect = document.getElementById('mode');

    // Set the difficulty based on the selection
    switch (difficultySelect.value) {
        case 'easy':
            aiDifficulty = 0.1;
            break;
        case 'medium':
            aiDifficulty = 0.5;
            break;
        case 'hard':
            aiDifficulty = 0.9;
            break;
        default:
            aiDifficulty = 0.1;
    }

    // Determine if the second player is controlled by the computer
    player2IsComputer = modeSelect.value === 'single';

    // Start the game
    gameStarted = true;

    // Hide the menu and display the canvas
    document.getElementById('menu').style.display = 'none';
    document.getElementById('pongCanvas').style.display = 'block';

    // Start the game loop
    gameLoop();
}        

The startGame() function performs the following steps:

  1. Get elements: Retrieves references to HTML elements for difficulty selection and game mode.
  2. Set difficulty: Based on the selected difficulty in the difficulty selection (difficultySelect), it sets the value of aiDifficulty. If no difficulty is selected, it defaults to a difficulty of 0.1.
  3. Determine game mode: Based on the selected mode in the mode selection (modeSelect), it determines whether the second player is controlled by the computer (player2IsComputer).
  4. Start the game: Sets the gameStarted flag to true.
  5. Display the game canvas: Hides the menu and displays the game canvas.
  6. Start the game loop: Calls the gameLoop() function to initiate the main game loop.

This function serves as the initialization of the game and transition from the menu to the game canvas.


Function update():

function update() {
    // Update paddle positions
    leftPaddleY += leftPaddleVelocity;
    rightPaddleY += rightPaddleVelocity;

    // Update ball position
    ballX += ballXSpeed;
    ballY += ballYSpeed;

    // Reflect the ball if it hits the top or bottom of the canvas
    if (ballY < 0 || ballY > canvas.height) {
        ballYSpeed = -ballYSpeed;
    }

    // Check if the ball passed the left paddle
    if (ballX < 0) {
        if (ballY > leftPaddleY && ballY < leftPaddleY + 80) {
            // Reflect the ball if it hits the left paddle
            ballXSpeed = -ballXSpeed;
        } else {
            // Reset the ball position, increase the right player's score, and check for a win
            ballX = canvas.width / 2;
            ballY = canvas.height / 2;
            rightPlayerScore++;
            checkWin();
        }
    }

    // Check if the ball passed the right paddle
    if (ballX > canvas.width) {
        if (ballY > rightPaddleY && ballY < rightPaddleY + 80) {
            // Reflect the ball if it hits the right paddle
            ballXSpeed = -ballXSpeed;
        } else {
            // Reset the ball position, increase the left player's score, and check for a win
            ballX = canvas.width / 2;
            ballY = canvas.height / 2;
            leftPlayerScore++;
            checkWin();
        }
    }

    // Ensure paddles stay within the canvas boundaries
    if (leftPaddleY < 0) {
        leftPaddleY = 0;
    } else if (leftPaddleY > canvas.height - 80) {
        leftPaddleY = canvas.height - 80;
    }

    if (rightPaddleY < 0) {
        rightPaddleY = 0;
    } else if (rightPaddleY > canvas.height - 80) {
        rightPaddleY = canvas.height - 80;
    }

    // If playing against the computer, update the AI for the right paddle
    if (player2IsComputer) {
        // Right paddle AI
        if (ballXSpeed > 0 && ballX > canvas.width / 2) {
            if (Math.random() < aiDifficulty) {
                if (ballY > rightPaddleY + 40) {
                    rightPaddleVelocity = paddleSpeed;
                } else if (ballY < rightPaddleY + 40) {
                    rightPaddleVelocity = -paddleSpeed;
                } else {
                    rightPaddleVelocity = 0;
                }
            }
        } else {
            rightPaddleVelocity = 0;
        }
    }

    // Draw the updated game state
    draw();
}        

The update() function handles the movement and interaction of paddles and the ball during each frame of the game. It also checks for collisions, updates scores, and ensures that paddles stay within the canvas boundaries. Additionally, if playing against the computer, it updates the AI for the right paddle based on the ball's position.


Function checkWin():

function checkWin() {
    // Check if players have reached the winning score
    if (leftPlayerScore === winningScore || rightPlayerScore === winningScore) {
        // Display the winning player using an alert
        alert(leftPlayerScore === winningScore ? "Left player wins!" : "Right player wins!");

        // Reset the game to its default state
        leftPlayerScore = 0;
        rightPlayerScore = 0;
        gameStarted = false;

        // Display the menu and hide the canvas
        document.getElementById('menu').style.display = 'block';
        document.getElementById('pongCanvas').style.display = 'none';
    }
}        

Description of the checkWin() function:

  1. Winning Check: The function checks whether one of the players has reached the winning score (winningScore).
  2. Display Winner: If the winning score is achieved, an alert is displayed with information about the winning player.
  3. Game Reset: It sets the scores of both players to zero, marks that the game is not active (gameStarted = false), and displays the menu while hiding the canvas.

This function ensures that the game is reset upon reaching the winning score, and players are informed about the winner. Its invocation is placed in the update() and startGame() functions at the appropriate locations where win checking is needed.


Function draw():

function draw() {
    // Clear the canvas
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // Draw paddles
    ctx.fillStyle = '#fff';
    ctx.fillRect(0, leftPaddleY, 10, 80); // Left paddle
    ctx.fillRect(canvas.width - 10, rightPaddleY, 10, 80); // Right paddle

    // Draw ball
    ctx.beginPath();
    ctx.arc(ballX, ballY, 10, 0, Math.PI * 2);
    ctx.fillStyle = '#fff';
    ctx.fill();
    ctx.closePath();

    // Draw score
    ctx.font = '30px Arial';
    ctx.fillText(leftPlayerScore + ' - ' + rightPlayerScore, canvas.width / 2 - 30, 30);
}        

Description of the draw() function:

  1. Clear Canvas: The function starts by clearing the entire canvas to prepare for the updated drawings.
  2. Draw Paddles: It draws the left and right paddles using the fillRect method. The left paddle is drawn at position (0, leftPaddleY) with a size of 10x80, and the right paddle is drawn at (canvas.width - 10, rightPaddleY) with the same size.
  3. Draw Ball: The function uses the arc method to draw the ball at its current position (ballX, ballY) with a radius of 10 and a full circle (Math.PI * 2). The ball is filled with the color white.
  4. Draw Score: It displays the current score at the top center of the canvas using the fillText method. The font is set to '30px Arial', and the score is positioned based on the canvas width and the individual scores.

This function is called within the update function to redraw the game elements and scores after each update. It ensures that the visual representation of the game is continuously updated on the canvas.


Function gameLoop():

function gameLoop() {
    if (gameStarted) {
        // Game update
        update();

        // Recursive call to gameLoop for the next animation frame
        requestAnimationFrame(gameLoop);
    }
}        

Description of the gameLoop() function:

  1. Game Started Condition: The function checks if the game is active using the gameStarted condition. If true, it proceeds to update the game.
  2. Game Update: Calls the update function, which updates the game state, including paddle movement, ball movement, and checks for winning conditions.
  3. Recursive Animation: Uses the requestAnimationFrame method, ensuring a recursive call to the gameLoop function. This creates a continuous cycle of game update and rendering.

This function establishes an ongoing cycle for updating and rendering the game. It starts when the game is initiated and continues until the game is ended.


Conclusion:

In this article, we've created a simple yet enjoyable Pong game using HTML, CSS, and JavaScript. Through our code, players can choose the game difficulty, play mode, and initiate an endless cycle of game updates and rendering. Players can either compete against a computer opponent with varying difficulty levels or play against each other on a single device.

We've learned to work with HTML to structure the page, CSS for a basic visual design, and, most importantly, JavaScript for implementing the game logic. Through various functions, event listeners, and the gameLoop cycle, we've ensured smooth gameplay and continuous updating of the game state.

This project not only provides entertainment but also serves as an excellent foundation for further expansion and improvement. You can add more features, enhance the design, or even implement a multiplayer networked game.

We hope you enjoyed the process of creating the Pong game and that this article has provided valuable insights for further exploration into the world of game development using web technologies. Happy coding!

You can find the complete code here GitHub


要查看或添加评论,请登录

?tefan Tusjak的更多文章

社区洞察

其他会员也浏览了