|
<!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> |
Nice!