饮水思源:https://www.bilibili.com/video/av12168808
添加砖块(先写一个砖块 再弄成n个)&通过暂停与减速来debug:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> canvas { border: 1px black solid; } </style> </head> <body> <canvas id="id-canvas" width="400" height="300"></canvas> <script> let makeGuaGame = function(fps) { let g = { actions: {}, keydowns: {}, }; g.canvas = document.querySelector("#id-canvas"); g.context = g.canvas.getContext('2d'); g.registerAction = function(key, callback) { g.actions[key] = callback; }; g.drawImage = function(guaImage) { this.context.drawImage(guaImage.img, guaImage.x, guaImage.y); } window.addEventListener("keydown", event => { g.keydowns[event.key] = true; }); window.addEventListener("keyup", event => { g.keydowns[event.key] = false; }); setInterval(function() { // events let keys = Object.keys(g.keydowns); for (let i = 0; i != keys.length; ++i) { let k = keys[i]; if (g.keydowns[k]) { g.actions[k](); } } // update g.update(); // clear g.context.clearRect(0, 0, g.canvas.width, g.canvas.height); // draw g.draw(); }, 1000/fps); return g; }; let imgFromPath = function(path) { let img = new Image(path); img.src = path; return img; }; let makePaddle = function() { let img = imgFromPath('img/paddle.png'); let o = { img: img, x: 200, y: 250, speed: 5, }; o.moveLeft = function() { this.x -= this.speed; if (this.x < 0) { this.x = 0; } }; o.moveRight = function() { this.x += this.speed; if (this.x > 400 - this.img.naturalWidth) { this.x = 400 - this.img.naturalWidth; } }; o.collide = function(guaImg) { if (Math.abs(o.x - guaImg.x) < o.img.naturalWidth/2 + guaImg.img.naturalWidth/2 && Math.abs(o.y - guaImg.y) < o.img.naturalHeight/2 + guaImg.img.naturalHeight/2 ) { return true; } return false; }; return o; }; let makeBall = function() { let img = imgFromPath('img/ball.png'); let o = { img: img, x: 200, y: 250, speedX: 7, speedY: -7, fired: false, }; o.fire = function() { this.fired = true; } o.move = function() { if (this.fired) { if (this.x > 400 || this.x < 0) { this.speedX *= -1; } else if (this.y > 300 || this.y < 0) { this.speedY *= -1; } this.x += this.speedX; this.y += this.speedY; } }; o.反弹 = function() { this.speedY *= -1; } return o; }; let makeBrick = function() { let img = imgFromPath('img/brick.png'); let o = { img: img, x: 50, y: 50, alive: true, }; o.kill = function() { this.alive = false; } o.collide = function(guaImg) { if (!this.alive) return false; if (Math.abs(o.x - guaImg.x) < o.img.naturalWidth/2 + guaImg.img.naturalWidth/2 && Math.abs(o.y - guaImg.y) < o.img.naturalHeight/2 + guaImg.img.naturalHeight/2 ) { return true; } return false; }; return o; }; let __main = function() { let game = makeGuaGame(fps=30); let paddle = makePaddle(); let ball = makeBall(); let bricks = []; for (let i = 0; i != 3; ++i) { let b = makeBrick(); b.x = i * 150; bricks.push(b); } game.registerAction('a', function() { paddle.moveLeft(); }); game.registerAction('d', function() { paddle.moveRight(); }); game.registerAction('f', function() { ball.fire(); }); let pause = false; window.addEventListener("keydown", event => { if (event.key == 'p') { pause = !pause; } }); game.update = function() { if (pause) { // 暂停的移动 return; } ball.move(); if (paddle.collide(ball)) { ball.反弹(); } for (let i = 0; i != bricks.length; ++i) { if (bricks[i].collide(ball)) { bricks[i].kill(); ball.反弹(); } } }; game.draw = function() { game.drawImage(paddle); game.drawImage(ball); for (let i = 0; i != bricks.length; ++i) { if (bricks[i].alive) { game.drawImage(bricks[i]); } } }; }; __main(); </script> </body> </html>
将各部分代码拆分为独立文件 &添加关卡以及debug模式:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> canvas { border: 1px black solid; } </style> <script src="js/utils.js"></script> <script src="js/guagame.js"></script> <script src="js/paddle.js"></script> <script src="js/ball.js"></script> <script src="js/brick.js"></script> <script src="js/levels.js"></script> </head> <body> <canvas id="id-canvas" width="400" height="300"></canvas> <script> let pause = false; let enableDebugMode = function(enable) { if (!enable) { return; } window.addEventListener("keydown", event => { if (event.key == 'p') { pause = !pause; } }); }; let __main = function() { enableDebugMode(false); let game = makeGuaGame(fps=40); let paddle = makePaddle(); let ball = makeBall(); let bricks = loadLevel(0); game.registerAction('a', function() { paddle.moveLeft(); }); game.registerAction('d', function() { paddle.moveRight(); }); game.registerAction('f', function() { ball.fire(); }); game.update = function() { if (pause) { // 暂停球的移动 return; } ball.move(); if (paddle.collide(ball)) { ball.反弹(); } for (let i = 0; i != bricks.length; ++i) { if (bricks[i].collide(ball)) { bricks[i].kill(); ball.反弹(); } } }; game.draw = function() { game.drawImage(paddle); game.drawImage(ball); for (let i = 0; i != bricks.length; ++i) { if (bricks[i].alive) { game.drawImage(bricks[i]); } } }; }; __main(); </script> </body> </html>
砖块生命值 &动态调fps :
let makeBrick = function(x, y, HP=1) { // x, y 是砖块的坐标 let img = imgFromPath('img/brick.png'); let o = { img: img, x: x, y: y, HP: HP, alive: true, }; o.kill = function() { this.alive = false; // 根据生命值改变图片?先初始化一个img数组 }; o.hit = function() { console.log(this.HP); this.HP--; if (this.HP == 0) { this.kill(); } }; o.collide = function(guaImg) { if(!this.alive) return false; if(Math.abs(o.x - guaImg.x) < o.img.naturalWidth / 2 + guaImg.img.naturalWidth / 2 && Math.abs(o.y - guaImg.y) < o.img.naturalHeight / 2 + guaImg.img.naturalHeight / 2 ) { return true; } return false; }; return o; };
let levels = [ [ [100, 50], ], [ [0, 50], [150, 50, 50], [300, 50], ], ]; let loadLevel = function(n) { // n表示第几关,该函数返回一个砖块数组 let level = levels[n]; let bricks = []; for (let i = 0; i != level.length; ++i) { let param = level[i]; let b = makeBrick(param[0], param[1], param[2] || 1); bricks.push(b); } return bricks; };
let makeGuaGame = function() { let g = { actions: {}, keydowns: {}, fps: 40, }; g.canvas = document.querySelector("#id-canvas"); g.context = g.canvas.getContext('2d'); g.registerAction = function(key, callback) { g.actions[key] = callback; }; g.drawImage = function(guaImage) { this.context.drawImage(guaImage.img, guaImage.x, guaImage.y); }; window.addEventListener("keydown", event => { g.keydowns[event.key] = true; }); window.addEventListener("keyup", event => { g.keydowns[event.key] = false; }); let repaintInfinitely = function() { // events let keys = Object.keys(g.keydowns); for(let i = 0; i != keys.length; ++i) { let k = keys[i]; if(g.keydowns[k] && g.actions[k] != null) { g.actions[k](); } } // update g.update(); // clear g.context.clearRect(0, 0, g.canvas.width, g.canvas.height); // draw g.draw(); setTimeout(function() { repaintInfinitely(); // 递归调用setTimeout }, 1000 / g.fps); }; g.begin = function() { // 在一切就绪的时候调用(update和draw定义后) repaintInfinitely(); }; return g; };
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style type="text/css"> canvas { border: 1px black solid; } </style> <script src="js/utils.js"></script> <script src="js/guagame.js"></script> <script src="js/paddle.js"></script> <script src="js/ball.js"></script> <script src="js/brick.js"></script> <script src="js/levels.js"></script> </head> <body> <canvas id="id-canvas" width="400" height="300"></canvas> <p>fps:<input id="id-input-speed" type="range" /></p> <script> let pause = false; let enableDebugMode = function(game, enable) { if (!enable) { return; } window.addEventListener("keydown", event => { if (event.key == 'p') { pause = !pause; } }); document.querySelector("#id-input-speed").addEventListener("input", event => { let newFps = Number(event.target.value); game.fps = newFps; }); }; let __main = function() { let game = makeGuaGame(); enableDebugMode(game, true); let paddle = makePaddle(); let ball = makeBall(); let bricks = loadLevel(1); game.registerAction('a', function() { paddle.moveLeft(); }); game.registerAction('d', function() { paddle.moveRight(); }); game.registerAction('f', function() { ball.fire(); }); game.update = function() { if (pause) { // 暂停球的移动 return; } ball.move(); if (paddle.collide(ball)) { ball.反弹(); } for (let i = 0; i != bricks.length; ++i) { if (bricks[i].collide(ball)) { bricks[i].hit(); ball.反弹(); } } }; game.draw = function() { game.drawImage(paddle); game.drawImage(ball); for (let i = 0; i != bricks.length; ++i) { if (bricks[i].alive) { game.drawImage(bricks[i]); } } }; game.begin(); }; __main(); </script> </body> </html>
截屏留念,这是一个血很厚的砖块。。