JavaScript - Game Part 4

In this lesson we will move the paddles with the keyboard and mouse.


Now that we have the ball moving, let's try moving the paddles as well. The opponent paddle on the right will move automatically without any input from the player. Let's create a function to move the opponent paddle. To do so, we need an opponentPaddleDY variable to store the amount by which the paddle will move each frame. Let's set it initially to 3.

    let ballTop;
    let ballBottom;
    let opponentPaddleDY = 3;
    

...

function moveOpponentPaddle() { }

...

function draw() { context.clearRect(0, 0, canvas.width, canvas.height); drawBall(); moveOpponentPaddle(); drawPaddle(playerPaddle); drawPaddle(opponentPaddle); handleCollision(); requestAnimationFrame(draw); ballX += ballDX; ballY += ballDY; }

First, we want the opponent paddle to change directions when it hits the edges. The top of the paddle is represented only by its y coordinate, and the bottom is represented by the y coordinate plus the height of the paddle. Thus, we want the function to look like this:

    function moveOpponentPaddle() {
        if (opponentPaddle.y < 0 || 
            opponentPaddle.y + PADDLE_HEIGHT > canvas.height) {
            opponentPaddleDY = -opponentPaddleDY;
        }
    
        opponentPaddle.y += opponentPaddleDY;
    }
            

This function means that if the paddle touches either the top or bottom edge, it will change direction. The function will then add opponentPaddleDY to the opponent paddle's y position.

Now let's try to move the player paddle as well. Let's start with the keyboard. In order to detect key presses, we need to add event listeners to the document. These statements take the form:

    document.addEventListener("eventType", function);
            

The first parameter determines the type of event to listen for, like a key press or a mouse movement, and the second parameter defines a function that runs automatically when the event is detected. For the second parameter, we could write a function name and define that function elsewhere in the program, or we could insert a function directly into the parameter space. We could even write an arrow function in that space. The last option is the least verbose, so we will do that one. For moving with the arrow keys, we need to listen for two events. Add these lines to your program near the top.

    document.addEventListener("keydown", e => {
        
    });
    
    document.addEventListener("keyup", e => {
        
    });
    
            

Each function takes in an event parameter which holds information about the event. In this case, we call the parameter e. The "keydown" event triggers when a key is pressed down, and the "keyup" event is triggered when a key is released, or when you stop pressing a key. In order to properly use the events, we need to declare some variables:

            const PADDLE_COLOR = "#e0e0e0";
            const PADDLE_WIDTH = 15;
            const PADDLE_HEIGHT = 120;
            const PLAYER_PADDLE_DY = 6;
            

...

let ballBottom; let opponentPaddleDY = 3; let upIsPressed = false; let downIsPressed = false;

The constant PLAYER_PADDLE_DY determines the rate at which the paddle will move, and the upIsPressed and downIsPressed variables store information on whether or not the up or down arrow keys are being pressed.

Now, to find out which key is being pressed, we can use the event's keycode. You can use a search engine to find out which keycode corresponds to which key. You will find that the up-arrow key has keycode 38, and the down-arrow key has keycode 40. We can get the keycode that the event parameter variable stores with dot notation like this: e.keycode. With this information, we can fill in the functions:

    document.addEventListener("keydown", e => {
        if (e.keyCode === 38) {
            upIsPressed = true;
        }
        else if (e.keyCode === 40) {
            downIsPressed = true;
        }
    });
    
    document.addEventListener("keyup", e => {
        if (e.keyCode === 38) {
            upIsPressed = false;
        }
        else if (e.keyCode === 40) {
            downIsPressed = false;
        }
    });
            

What these functions do is set the appropriate variable to true if that key is being pressed and set the appropriate variable to false when the key stops being pressed. Now we can animate the paddle movement in the draw() function:

     function draw() {
         context.clearRect(0, 0, canvas.width, canvas.height);
         drawBall();
         moveOpponentPaddle();
         drawPaddle(playerPaddle);
         drawPaddle(opponentPaddle);
         handleCollision();
         requestAnimationFrame(draw);
         ballX += ballDX;
         ballY += ballDY;
         if (upIsPressed) {
             playerPaddle.y -= PLAYER_PADDLE_DY;
         }
         else if (downIsPressed) {
             playerPaddle.y += PLAYER_PADDLE_DY;
         }
     }
            

This moves the paddle up (by lowering the y value) when the up key is being pressed and moves the paddle down (by increasing the y value) when the down key is being pressed. There is one problem with this, however, because the paddle can go offscreen. We can fix this by adding additional code that pushes the paddle back into bounds if it gets too far high or low:

     function draw() {
         context.clearRect(0, 0, canvas.width, canvas.height);
         drawBall();
         moveOpponentPaddle();
         drawPaddle(playerPaddle);
         drawPaddle(opponentPaddle);
         handleCollision();
         requestAnimationFrame(draw);
         ballX += ballDX;
         ballY += ballDY;
         if (upIsPressed) {
             playerPaddle.y -= PLAYER_PADDLE_DY;
             if (playerPaddle.y < 0) {
                 playerPaddle.y += PLAYER_PADDLE_DY;
             }
         }
         else if (downIsPressed) {
             playerPaddle.y += PLAYER_PADDLE_DY;
             if (playerPaddle.y > canvas.height - PADDLE_HEIGHT) {
                 playerPaddle.y -= PLAYER_PADDLE_DY;
             }
         }
     }
            

Now, you should be able to move the paddle with the arrow keys.

You can also implement mouse movement in a similar way:

    let downIsPressed = false;
    let mouseY;
    

...

document.addEventListener("mousemove", e => { mouseY = e.clientY - canvas.offsetTop; if (mouseY > 0 && mouseY < canvas.height) { playerPaddle.y = mouseY - PADDLE_HEIGHT / 2; } });

We need to calculate the mouse y position as the difference between the mouse y position in the browser e.clientY and the offset off the top of the canvas canvas.offsetTop. The function then sets the paddle position with the mouse position when the mouse moves, as long as the mouse is inside the canvas bounds. We can keep the paddle from going off the screen at all with some additional code:

    document.addEventListener("mousemove", e => {
        mouseY = e.clientY - canvas.offsetTop;
        if (mouseY > 0 && mouseY < canvas.height) {
            playerPaddle.y = mouseY - PADDLE_HEIGHT / 2;
            if (playerPaddle.y < 0) {
                playerPaddle.y = 0;
            }
            else if (playerPaddle.y > canvas.height - PADDLE_HEIGHT) {
                playerPaddle.y = canvas.height - PADDLE_HEIGHT;
            }
        }
    });
            

An optional extra step to make the opponent paddle chase the ball.

We can add some simple code to the moveOpponentPaddle() function to make the paddle try to chase the ball instead of just going back and forth. See if you can do it yourself before seeing my code. What we want the opponent paddle to do is change direction when the ball is above it and the paddle is moving downward or when the ball is below it and the paddle is moving upward. Try it yourself!





Here is my code:

    function moveOpponentPaddle() {
        if (opponentPaddle.y < 0 || 
            opponentPaddle.y + PADDLE_HEIGHT > canvas.height) {
            opponentPaddleDY = -opponentPaddleDY;
        }
        else if ((ballY < opponentPaddle.y && opponentPaddleDY > 0) ||
                (ballY > opponentPaddle.y + PADDLE_HEIGHT && opponentPaddleDY < 0)) {
            opponentPaddleDY = -opponentPaddleDY;
        }
    
        opponentPaddle.y += opponentPaddleDY;
    }
            

Source code

< Previous Next >