In this one video we will make an entire game and you’ll actually understand it through diagrams and more. We’ll create Pong using JavaScript and you’ll learn about Receiving User Input, Handling Collisions, Moving AI, Creating User Interfaces, Playing Sound and Much more. You can play the game here. The sound file is here.
All of the heavily commented code follows below. If you want me to make a specific game please tell me.
If you enjoy free tutorials like this consider donating $1 on Patreon. Starting next month, I will be uploading additional content every month only for the Patreons that help me keep my channel alive and free.
Code From the Video
// Reference to the canvas element
let canvas;
// Context provides functions used for drawing and
// working with Canvas
let ctx;
// Used to monitor whether paddles and ball are
// moving and in what direction
let DIRECTION = {
STOPPED: 0,
UP: 1,
DOWN: 2,
LEFT: 3,
RIGHT: 4
};
// The paddle object used for both the player and AI
class Paddle{
constructor(side) {
// Define size of paddle
this.width = 15;
this.height = 65;
// Put paddle on right or left for player and AI
this.x = side === 'left' ? 150 : canvas.width - 150;
// Center the paddle
this.y = canvas.height / 2;
// Will hold the increasing score
this.score = 0;
// Defines movement direction of paddles
this.move = DIRECTION.STOPPED;
// Defines how quickly paddles can be moved
this.speed = 11;
}
}
// The ball object
class Ball{
constructor(newSpeed) {
// Size of ball
this.width = 15;
this.height = 15;
// Position in center
this.x = canvas.width / 2;
this.y = canvas.height / 2;
// Ball moves in x & y direction so monitor both
this.moveX = DIRECTION.STOPPED;
this.moveY = DIRECTION.STOPPED;
// Speed ball can move
this.speed = newSpeed;
}
}
let player;
let aiPlayer;
let ball;
// Monitors whether ball is currently in play
let running = false;
let gameOver = false;
// Will be used to add a delay before play resumes
let delayAmount;
// Should ball target player or AI
let targetForBall;
// Used to play sounds when paddle hits a ball
let beepSound;
// Call for our function to execute when page is loaded
document.addEventListener('DOMContentLoaded', SetupCanvas);
function SetupCanvas(){
// Get reference to canvas element
canvas = document.getElementById('my-canvas');
// Get methods for manipulating the canvas
ctx = canvas.getContext('2d');
// Define canvas size
canvas.width = 1400;
canvas.height = 1000;
// Create player and AI paddles
player = new Paddle('left');
aiPlayer = new Paddle('right');
// Create ball and define its speed
ball = new Ball(7);
// Make AI paddle slightly slower then the ball
// and the player
aiPlayer.speed = 6.5;
// Set target for ball to the player
targetForBall = player;
// Set delay between scores
delayAmount = (new Date()).getTime();
// Allow for playing sound
beepSound = document.getElementById('beepSound');
beepSound.src = 'beep.wav';
// Handle keyboard input
document.addEventListener('keydown', MovePlayerPaddle);
document.addEventListener('keyup', StopPlayerPaddle);
// Draw the board for the 1st time
Draw();
}
function Draw(){
// Clear the canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// Draw Canvas background
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw Paddles
ctx.fillStyle = 'white';
ctx.fillRect(player.x, player.y, player.width, player.height);
ctx.fillRect(aiPlayer.x, aiPlayer.y, aiPlayer.width, aiPlayer.height);
// Draw Ball
ctx.fillRect(ball.x, ball.y, ball.width, ball.height);
// Set font for scores
ctx.font = '80px Arial';
ctx.textAlign = 'center';
// Draw scores
ctx.fillText(player.score.toString(), (canvas.width/2) - 300, 100);
ctx.fillText(aiPlayer.score.toString(), (canvas.width/2) + 300, 100);
// Declare a winner
if(player.score === 2){
ctx.fillText("Player Wins", canvas.width/2, 300);
gameOver = true;
}
if(aiPlayer.score === 2){
ctx.fillText("AI Wins", canvas.width/2, 300);
gameOver = true;
}
}
// All the logic is here including collision detection,
// player / AI paddle movement, keeping assets on the board
function Update(){
if(!gameOver){
// Handle if ball scores on the player
if(ball.x <= 0){
ResetBall(aiPlayer, player);
}
// Handle if ball scores on AI
if(ball.x >= canvas.width - ball.width){
ResetBall(player, aiPlayer);
}
// Handle if ball hits the top or bottom
if(ball.y <= 0){
ball.moveY = DIRECTION.DOWN;
}
if(ball.y >= canvas.height - ball.height){
ball.moveY = DIRECTION.UP;
}
// Move player paddle if they are pressing down
// buttons
if(player.move === DIRECTION.DOWN){
player.y += player.speed;
} else if(player.move === DIRECTION.UP){
player.y -= player.speed;
}
// If player tries to move off the board prevent that
if(player.y < 0){
player.y = 0;
} else if(player.y >= (canvas.height - player.height)){
player.y = canvas.height - player.height;
}
// Add a delay and only if a current target is set move
// the ball
if(AddADelay() && targetForBall){
// Move ball toward he target
ball.moveX = targetForBall === player ? DIRECTION.LEFT : DIRECTION.RIGHT;
// Move either up or down to randomize ball movement
ball.moveY = [DIRECTION.UP, DIRECTION.DOWN][Math.round(Math.random())];
// Start ball from the middle
ball.y = canvas.height / 2;
// Nullify target because target changes repeatedly
// as the ball bounces
targetForBall = null;
}
// Move ball based on moveY & moveX values
if (ball.moveY === DIRECTION.UP){
ball.y -= ball.speed;
} else if (ball.moveY === DIRECTION.DOWN){
ball.y += ball.speed;
}
if (ball.moveX === DIRECTION.LEFT){
ball.x -= ball.speed;
} else if (ball.moveX === DIRECTION.RIGHT){
ball.x += ball.speed;
}
// Handle AI up & down movement
if (aiPlayer.y > ball.y - (aiPlayer.height / 2)) {
if (ball.moveX === DIRECTION.RIGHT){
aiPlayer.y -= aiPlayer.speed;
} else {
aiPlayer.y -= aiPlayer.speed;
}
}
if (aiPlayer.y < ball.y - (aiPlayer.height / 2)) {
if (ball.moveX === DIRECTION.RIGHT){
aiPlayer.y += aiPlayer.speed;
} else {
aiPlayer.y += aiPlayer.speed;
}
}
// If AI tries to move off the board handle that
if(aiPlayer.y < 0){
aiPlayer.y = 0;
} else if(aiPlayer.y >= (canvas.height - aiPlayer.height)){
aiPlayer.y = canvas.height - aiPlayer.height;
}
// Handle ball collision with the players paddle
// x represents the upper left hand position of
// the object. So we have to subtract the ball width
// to check if the balls x is <= paddle x.
// The same must be true for the player x to have
// a collision
if (ball.x - ball.width <= player.x && ball.x >= player.x - player.width) {
// If also this is true for y in the upper left
// hand corner we have a collison
if (ball.y <= player.y + player.height && ball.y + ball.height >= player.y) {
ball.x = (player.x + ball.width);
// Move ball toward right of screen
ball.moveX = DIRECTION.RIGHT;
beepSound.play();
}
}
// Handle ball collison with AI paddle
if (ball.x - ball.width <= aiPlayer.x && ball.x >= aiPlayer.x - aiPlayer.width) {
if (ball.y <= aiPlayer.y + aiPlayer.height && ball.y + ball.height >= aiPlayer.y) {
ball.x = (aiPlayer.x - ball.width);
ball.moveX = DIRECTION.LEFT;
beepSound.play();
}
}
}
}
// If we are not in play mode start the game running and loop
// through updates and draws till the end of the game
function MovePlayerPaddle(key){
if(running === false){
running = true;
window.requestAnimationFrame(GameLoop);
}
// Handle up arrow and w input
if(key.keyCode === 38 || key.keyCode === 87) player.move = DIRECTION.UP;
// Handle down arrow and s input
if(key.keyCode === 40 || key.keyCode === 83) player.move = DIRECTION.DOWN;
}
function StopPlayerPaddle(evt){
player.move = DIRECTION.STOPPED;
}
// Loops constantly updating position of assets
// while drawing them
function GameLoop(){
Update();
Draw();
// Keep looping
if(!gameOver) requestAnimationFrame(GameLoop);
}
function ResetBall(whoScored, whoLost){
// Increment the player that scored
whoScored.score++;
// Reset the ball and increase speed
let newBallSpeed = ball.speed + .2;
ball = new Ball(newBallSpeed);
// Set new target for the ball
targetForBall = whoLost;
// Reset the the delay start time
delayAmount = (new Date()).getTime();
}
// Used to add a second delay after scoring
function AddADelay(){
return ((new Date()).getTime() - delayAmount >= 1000);
}
JSTut10.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- Set the page width to the device width -->
<meta name="viewport" content="width = device-width, initial-scale = 1">
<title>JavaScript Pong</title>
<link rel="stylesheet" type="text/css" href="mainstyle.css">
<script src="pong.js"></script>
</head>
<body>
<!-- What we will be drawing our game on -->
<canvas id='my-canvas'></canvas>
<!-- Embedded beep sound in the web page which
I'll target in the JS -->
<audio id="beepSound" style="display: none;"></audio>
</body>
</html>
mainstyle.css
body {
text-align: center;
background: #fff;
}
/* # is used to target ids in HTML */
#my-canvas {
background: #fff;
margin-left: 10px;
margin-top: 10px;
}