Basic HTML Breakout Game
2 years ago in HTML
<!DOCTYPE html>
<html>
<head>
<title>Basic Breakout HTML Game</title>
<meta charset="UTF-8">
<style>
html, body {
height: 100%;
margin: 0;
}
body {
background: black;
display: flex;
align-items: center;
justify-content: center;
}
</style>
</head>
<body>
<canvas width="400" height="500" id="game"></canvas>
<script>
const canvas = document.getElementById('game');
const context = canvas.getContext('2d');
// each row is 14 bricks long. the level consists of 6 blank rows then 8 rows
// of 4 colors: red, orange, green, and yellow
const level1 = [
[],
[],
[],
[],
[],
[],
['R','R','R','R','R','R','R','R','R','R','R','R','R','R'],
['R','R','R','R','R','R','R','R','R','R','R','R','R','R'],
['O','O','O','O','O','O','O','O','O','O','O','O','O','O'],
['O','O','O','O','O','O','O','O','O','O','O','O','O','O'],
['G','G','G','G','G','G','G','G','G','G','G','G','G','G'],
['G','G','G','G','G','G','G','G','G','G','G','G','G','G'],
['Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y'],
['Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y']
];
// create a mapping between color short code (R, O, G, Y) and color name
const colorMap = {
'R': 'red',
'O': 'orange',
'G': 'green',
'Y': 'yellow'
};
// use a 2px gap between each brick
const brickGap = 2;
const brickWidth = 25;
const brickHeight = 12;
// the wall width takes up the remaining space of the canvas width. with 14 bricks
// and 13 2px gaps between them, thats: 400 - (14 * 25 + 2 * 13) = 24px. so each
// wall will be 12px
const wallSize = 12;
const bricks = [];
// create the level by looping over each row and column in the level1 array
// and creating an object with the bricks position (x, y) and color
for (let row = 0; row < level1.length; row++) {
for (let col = 0; col < level1[row].length; col++) {
const colorCode = level1[row][col];
bricks.push({
x: wallSize + (brickWidth + brickGap) * col,
y: wallSize + (brickHeight + brickGap) * row,
color: colorMap[colorCode],
width: brickWidth,
height: brickHeight
});
}
}
const paddle = {
// place the paddle horizontally in the middle of the screen
x: canvas.width / 2 - brickWidth / 2,
y: 440,
width: brickWidth,
height: brickHeight,
// paddle x velocity
dx: 0
};
const ball = {
x: 130,
y: 260,
width: 5,
height: 5,
// how fast the ball should go in either the x or y direction
speed: 2,
// ball velocity
dx: 0,
dy: 0
};
// check for collision between two objects using axis-aligned bounding box (AABB)
// @see https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
function collides(obj1, obj2) {
return obj1.x < obj2.x + obj2.width &&
obj1.x + obj1.width > obj2.x &&
obj1.y < obj2.y + obj2.height &&
obj1.y + obj1.height > obj2.y;
}
// game loop
function loop() {
requestAnimationFrame(loop);
context.clearRect(0,0,canvas.width,canvas.height);
// move paddle by it's velocity
paddle.x += paddle.dx;
// prevent paddle from going through walls
if (paddle.x < wallSize) {
paddle.x = wallSize
}
else if (paddle.x + brickWidth > canvas.width - wallSize) {
paddle.x = canvas.width - wallSize - brickWidth;
}
// move ball by it's velocity
ball.x += ball.dx;
ball.y += ball.dy;
// prevent ball from going through walls by changing its velocity
// left & right walls
if (ball.x < wallSize) {
ball.x = wallSize;
ball.dx *= -1;
}
else if (ball.x + ball.width > canvas.width - wallSize) {
ball.x = canvas.width - wallSize - ball.width;
ball.dx *= -1;
}
// top wall
if (ball.y < wallSize) {
ball.y = wallSize;
ball.dy *= -1;
}
// reset ball if it goes below the screen
if (ball.y > canvas.height) {
ball.x = 130;
ball.y = 260;
ball.dx = 0;
ball.dy = 0;
}
// check to see if ball collides with paddle. if they do change y velocity
if (collides(ball, paddle)) {
ball.dy *= -1;
// move ball above the paddle otherwise the collision will happen again
// in the next frame
ball.y = paddle.y - ball.height;
}
// check to see if ball collides with a brick. if it does, remove the brick
// and change the ball velocity based on the side the brick was hit on
for (let i = 0; i < bricks.length; i++) {
const brick = bricks[i];
if (collides(ball, brick)) {
// remove brick from the bricks array
bricks.splice(i, 1);
// ball is above or below the brick, change y velocity
// account for the balls speed since it will be inside the brick when it
// collides
if (ball.y + ball.height - ball.speed <= brick.y ||
ball.y >= brick.y + brick.height - ball.speed) {
ball.dy *= -1;
}
// ball is on either side of the brick, change x velocity
else {
ball.dx *= -1;
}
break;
}
}
// draw walls
context.fillStyle = 'lightgrey';
context.fillRect(0, 0, canvas.width, wallSize);
context.fillRect(0, 0, wallSize, canvas.height);
context.fillRect(canvas.width - wallSize, 0, wallSize, canvas.height);
// draw ball if it's moving
if (ball.dx || ball.dy) {
context.fillRect(ball.x, ball.y, ball.width, ball.height);
}
// draw bricks
bricks.forEach(function(brick) {
context.fillStyle = brick.color;
context.fillRect(brick.x, brick.y, brick.width, brick.height);
});
// draw paddle
context.fillStyle = 'cyan';
context.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
}
// listen to keyboard events to move the paddle
document.addEventListener('keydown', function(e) {
// left arrow key
if (e.which === 37) {
paddle.dx = -3;
}
// right arrow key
else if (e.which === 39) {
paddle.dx = 3;
}
// space key
// if they ball is not moving, we can launch the ball using the space key. ball
// will move towards the bottom right to start
if (ball.dx === 0 && ball.dy === 0 && e.which === 32) {
ball.dx = ball.speed;
ball.dy = ball.speed;
}
});
// listen to keyboard events to stop the paddle if key is released
document.addEventListener('keyup', function(e) {
if (e.which === 37 || e.which === 39) {
paddle.dx = 0;
}
});
// start the game
requestAnimationFrame(loop);
</script>
</body>
</html>