
开篇寄语
前些天,伯衡君写了一篇简单教程如何用javascript入门游戏编程,具体可以参看下方的前情提要,本篇文章是前一篇的延续,继续深化与提高在游戏编程中Javascript语法和逻辑的使用,这次则需要聊的深入一些了,以一个打砖块的小游戏为例,为将来使用一些公开的游戏JS引擎打下基础,是以为记。
前情提要
游戏目标
- 场景搭建构成:分数,砖块,圆球和移动板
- 移动和球发生联系,当球碰到板子反弹,球碰到一块砖块,那一砖块会消失,同时,分数也会增加,球碰到墙壁,球会反弹
胜负条件
- 当砖块都被打掉后,意味着游戏胜利,给予提示,退出游戏
- 当球碰到底部变宽,也就是说这个球没有被移动板接到,意味着游戏失败,给予提示,退出游戏
科学方法
- 将复杂的事情,拆解成一步步简单的可以解决的事项,简单事项的一步步解决,复杂的事项也就迎刃而解了
内容详情
首先是建立场景,新建一个html文件,放置在编辑器里面编写,伯衡君一般选用VScode进行编辑,文中涉及到canvas相关知识,如果不明白的可以先补充一些这方面的知识,做一个全屏的舞台,放置下面的代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Breakout</title>
<style>
* {
padding: 0;
margin: 0;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
</script>
</body>
</html>
在场景中新建一个半径为10的小圆球,并设置该球的坐标:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Breakout</title>
<style>
* {
padding: 0;
margin: 0;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
var ballRadius = 10;
var x = canvas.width / 2;
var y = canvas.height - 100;
//Draw A Ball
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
}
</script>
</body>
</html>
生成效果,如下图所示:

之后,需要让小球动起来,碰到墙壁的条件是圆球的坐标加上移动值加上小球的尺寸大于画布的横坐标或者纵坐标的最大值,或者是坐标加上移动值小于球的半径,并设置持续函数,增加代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Breakout</title>
<style>
* {
padding: 0;
margin: 0;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ballRadius = 10;
var x = canvas.width / 2;
var y = canvas.height - 100;
var dx = 3;
var dy = -3;
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
dy = -dy;
}
x += dx;
y += dy;
}
setInterval(draw, 10);
</script>
</body>
</html>
运行后,就可以看到满屏碰撞的圆球了。
圆球设置已经OK,接下来是绘制一个移动板,用来接发球,设置经纬度,长度,宽度,按下键盘左右键才能移动这样的移动的条件,增加代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Breakout</title>
<style>
* {
padding: 0;
margin: 0;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ballRadius = 10;
var x = canvas.width / 2;
var y = canvas.height - 100;
var dx = 2;
var dy = -2;
var paddleHeight = 10;
var paddleWidth = 75;
var paddleX = (canvas.width - paddleWidth) / 2;
var rightPressed = false;
var leftPressed = false;
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) {
if (e.key == "Right" || e.key == "ArrowRight") {
rightPressed = true;
} else if (e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = true;
}
}
function keyUpHandler(e) {
if (e.key == "Right" || e.key == "ArrowRight") {
rightPressed = false;
} else if (e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = false;
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height - paddleHeight - 20, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawPaddle();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
dy = -dy;
}
if (rightPressed) {
paddleX += 7;
if (paddleX + paddleWidth > canvas.width) {
paddleX = canvas.width - paddleWidth;
}
} else if (leftPressed) {
paddleX -= 7;
if (paddleX < 0) {
paddleX = 0;
}
}
x += dx;
y += dy;
}
setInterval(draw, 10);
</script>
</body>
</html>
如下图所示:

接下来设置是一个游戏条件,当球碰触到底线时,游戏宣告结束,增加代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Breakout</title>
<style>
* {
padding: 0;
margin: 0;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ballRadius = 10;
var x = canvas.width / 2;
var y = canvas.height - 100;
var dx = 2;
var dy = -2;
var paddleHeight = 10;
var paddleWidth = 75;
var paddleX = (canvas.width - paddleWidth) / 2;
var rightPressed = false;
var leftPressed = false;
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) {
if (e.key == "Right" || e.key == "ArrowRight") {
rightPressed = true;
} else if (e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = true;
}
}
function keyUpHandler(e) {
if (e.key == "Right" || e.key == "ArrowRight") {
rightPressed = false;
} else if (e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = false;
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height - paddleHeight - 20, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawPaddle();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy < ballRadius) {
dy = -dy;
} else if (y + dy + 20 > canvas.height - ballRadius) {
if (x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
} else {
if (canvas.height - ballRadius - y == 0) {
alert("GAME OVER");
document.location.reload();
clearInterval(interval);
} // Needed for Chrome to end game
}
}
if (rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 7;
} else if (leftPressed && paddleX > 0) {
paddleX -= 7;
}
x += dx;
y += dy;
}
var interval = setInterval(draw, 10);
</script>
</body>
</html>
生成效果如下图所示:

接下来是建立转块阵了,主要是使用for loop这种循环形式,建立4行9列共36个的砖块,生成代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Breakout</title>
<style>
* {
padding: 0;
margin: 0;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ballRadius = 10;
var x = canvas.width / 2;
var y = canvas.height - 100;
var dx = 2;
var dy = -2;
var paddleHeight = 10;
var paddleWidth = 75;
var paddleX = (canvas.width - paddleWidth) / 2;
var rightPressed = false;
var leftPressed = false;
var brickRowCount = 9;
var brickColumnCount = 4;
var brickWidth = 70;
var brickHeight = 30;
var brickPadding = 10;
var brickOffsetTop = 30;
var brickOffsetLeft = window.innerWidth / 4;
var bricks = [];
for (var c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (var r = 0; r < brickRowCount; r++) {
bricks[c][r] = {
x: 0,
y: 0,
status: 1
};
}
}
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
function keyDownHandler(e) {
if (e.key == "Right" || e.key == "ArrowRight") {
rightPressed = true;
} else if (e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = true;
}
}
function keyUpHandler(e) {
if (e.key == "Right" || e.key == "ArrowRight") {
rightPressed = false;
} else if (e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = false;
}
}
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height - paddleHeight - 20, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
//Draw Bricks
function drawBricks() {
for (var c = 0; c < brickColumnCount; c++) {
for (var r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status == 1) {
var brickX = (r * (brickWidth + brickPadding)) + brickOffsetLeft;
var brickY = (c * (brickHeight + brickPadding)) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawPaddle();
drawBricks();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy < ballRadius) {
dy = -dy;
} else if (y + dy + 20 > canvas.height - ballRadius) {
if (x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
} else {
if (canvas.height - ballRadius - y == 0) {
alert("GAME OVER");
document.location.reload();
clearInterval(interval);
} // Needed for Chrome to end game
}
}
if (rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 7;
} else if (leftPressed && paddleX > 0) {
paddleX -= 7;
}
x += dx;
y += dy;
}
var interval = setInterval(draw, 10);
</script>
</body>
</html>
生成效果如下图所示:

接下来是让球敲打砖块,当碰到砖块,该砖块会消失,并增加记分板和鼠标控制,以及砖块都消失后的胜利条件,增加代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Breakout</title>
<style>
* {
padding: 0;
margin: 0;
}
canvas {
background: #eee;
display: block;
margin: 0 auto;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext("2d");
var ballRadius = 10;
var x = canvas.width / 2;
var y = canvas.height - 30;
var dx = 2;
var dy = -2;
var paddleHeight = 10;
var paddleWidth = 75;
var paddleX = (canvas.width - paddleWidth) / 2;
var rightPressed = false;
var leftPressed = false;
var brickRowCount = 9;
var brickColumnCount = 4;
var brickWidth = 70;
var brickHeight = 30;
var brickPadding = 10;
var brickOffsetTop = 30;
var brickOffsetLeft = window.innerWidth / 4;
var score = 0;
var bricks = [];
for (var c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (var r = 0; r < brickRowCount; r++) {
bricks[c][r] = {
x: 0,
y: 0,
status: 1
};
}
}
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("mousemove", mouseMoveHandler, false);
function keyDownHandler(e) {
if (e.key == "Right" || e.key == "ArrowRight") {
rightPressed = true;
} else if (e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = true;
}
}
function keyUpHandler(e) {
if (e.key == "Right" || e.key == "ArrowRight") {
rightPressed = false;
} else if (e.key == "Left" || e.key == "ArrowLeft") {
leftPressed = false;
}
}
function mouseMoveHandler(e) {
var relativeX = e.clientX - canvas.offsetLeft;
if (relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth / 2;
}
}
function collisionDetection() {
for (var c = 0; c < brickColumnCount; c++) {
for (var r = 0; r < brickRowCount; r++) {
var b = bricks[c][r];
if (b.status == 1) {
if (x > b.x && x < b.x + brickWidth && y > b.y && y < b.y + brickHeight) {
dy = -dy;
b.status = 0;
score++;
if (score == brickRowCount * brickColumnCount) {
alert("YOU WIN, CONGRATS!");
document.location.reload();
clearInterval(interval); // Needed for Chrome to end game
}
}
}
}
}
}
//Draw A Ball
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
//Draw A Paddle
function drawPaddle() {
ctx.beginPath();
ctx.rect(paddleX, canvas.height - paddleHeight - 20, paddleWidth, paddleHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
//Draw Bricks
function drawBricks() {
for (var c = 0; c < brickColumnCount; c++) {
for (var r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status == 1) {
var brickX = (r * (brickWidth + brickPadding)) + brickOffsetLeft;
var brickY = (c * (brickHeight + brickPadding)) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
}
function drawScore() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Score: " + score, 8, 20);
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks();
drawBall();
drawPaddle();
drawScore();
collisionDetection();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy < ballRadius) {
dy = -dy;
} else if (y + dy + 20 > canvas.height - ballRadius) {
if (x > paddleX && x < paddleX + paddleWidth) {
dy = -dy;
} else {
if (canvas.height - ballRadius - y == 0) {
alert("GAME OVER");
document.location.reload();
clearInterval(interval);
} // Needed for Chrome to end game
}
}
if (rightPressed && paddleX < canvas.width - paddleWidth) {
paddleX += 7;
} else if (leftPressed && paddleX > 0) {
paddleX -= 7;
}
x += dx;
y += dy;
}
var interval = setInterval(draw, 10);
</script>
</body>
</html>
一个简单的打砖块游戏就生成了,效果如下图所示:

温馨提示
关键在于方法的掌握,即将复杂的事情简单化,拆解步骤,然后执行。
- 我的微信
- 微信扫一扫加好友
-
- 我的微信公众号
- 扫描关注公众号
-





