<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>partical</title> <style> body { margin: 0; padding: 0; } canvas { background-color: black; border: 1px solid; } </style> </head> <body> <canvas></canvas> <script> var GAME_OVER = false; var PAUSE = false; var total = 0; var canvas = document.querySelector('canvas'); var ctx = canvas.getContext('2d'); canvas.width = 250;//window.innerWidth; canvas.height = 250;//window.innerHeight; var midX = canvas.width / 2; var midY = canvas.height / 2; function Vector(x, y){ this.x = x || 0; this.y = y || 0; } Vector.prototype = { add: function(vector){ this.x += vector.x; this.y += vector.y; }, getMagnitude: function(){ return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2)); }, getAngle: function(){ return Math.atan2(this.y, this.x); } }; Vector.fromAngle = function(angle, magnitude){ return new Vector(magnitude * Math.cos(angle), magnitude * Math.sin(angle)); }; function Particle(point, velocity, acceleration){ this.position = point || new Vector(0,0); this.velocity = velocity || new Vector(0,0); this.acceleration = acceleration || new Vector(0,0); } Particle.prototype = { move: function(){ this.velocity.add(this.acceleration); this.position.add(this.velocity); }, bounceX: function(){ this.velocity.x = -(this.velocity.x); }, bounceY: function(){ this.velocity.y = -(this.velocity.y); } }; function Obstacle(point, status, width, height){ this.position = point || new Vector(0,0); this.status = status || 'active'; this.width = width || 1; this.height = height || 1; this.drawColor = "#999"; } function generateObstacles(){ var arr = []; total = 0; for (var i = canvas.width * 0.2; i < canvas.width * 0.8; i+= 4) { for (var j = canvas.height * 0.2; j < canvas.height * 0.3; j+= 4){ ob = new Obstacle(new Vector(i, j), 'active', 3, 3); total += 1; arr.push(ob); } } return arr; } function plotObstacles(){ } function drawObstacles(){ for (var i = 0; i < obstacles.length ; i++) { if(obstacles[i].status == 'active'){ drawRect(obstacles[i]); } } } function Emitter(point, velocity, spread){ this.position = point; this.velocity = velocity; this.spread = spread || Math.PI / 32; this.drawColor = "#999"; } Emitter.prototype = { emitParticle: function(){ var angle = this.velocity.getAngle();// + this.spread - (Math.random() * this.spread * 2); var magnitude = this.velocity.getMagnitude(); var velocity = Vector.fromAngle(angle, magnitude); var position = new Vector(this.position.x, this.position.y); return new Particle(position, velocity); } }; function Board(point, width, height, velocity){ this.position = point || new Vector(0,0); this.width = width || 1; this.height = height || 1; this.acceleration = new Vector(0,0); this.velocity = velocity || new Vector(0,0); this.max_velocity = 1.6; } Board.prototype = { move: function(){ this.velocity.add(this.acceleration); if(this.velocity > this.max_velocity){ this.velocity = this.max_velocity; } this.position.add(this.velocity); }, is_stopped: function(){ return this.velocity.x == 0 && this.velocity.y == 0; } }; function plotParticle(boundX, boundY){ if(particle.position.y > boundY){ GAME_OVER = true; return; } if(particle.position.y < 0){ particle.bounceY(); } if(particle.position.x < 0 || particle.position.x > boundX){ particle.bounceX(); } if((boundY - particle.position.y) < 10){ if(particle.position.x > board.position.x && particle.position.x < board.position.x + board.width && particle.position.y > board.position.y){ if(board.velocity.x != 0){ particle.velocity.x = board.velocity.x; } particle.bounceY(); } } for (var i = 0; i < obstacles.length ; i++) { // 让 obstacle 近似圆,根据两物体中心点距离是否小于两物体半径和判断是否碰撞,很粗 if(obstacles[i].status == 'active' && Math.sqrt(Math.pow((particle.position.x-(obstacles[i].position.x + obstacles[i].width / 2)),2) + Math.pow((particle.position.y-(obstacles[i].position.y + obstacles[i].height / 2)),2)) <= (1.5 * 1.414 + 2)){ obstacles[i].status = 'inactive'; total -= 1; if(total <= 0) { GAME_OVER = true; } //从上下碰撞 if(particle.position.x >= obstacles[i].position.x && particle.position.x <= (obstacles[i].position.x + obstacles[i].width)){ particle.bounceY(); } //从左右碰撞 else if (particle.position.y >= obstacles[i].position.y && particle.position.y <= (obstacles[i].position.y + obstacles[i].height)) { particle.bounceX(); } } } particle.move(); } function plotBoard(boundX, boundY){ if(board.position.x < 0){ board.position.x = 0; board.acceleration = new Vector(0,0); board.velocity = new Vector(0,0); } if(board.position.x > boundX - board.width){ board.position.x = boundX - board.width; board.acceleration = new Vector(0,0); board.velocity = new Vector(0,0); } if(board.position.y < 0){ board.position.y = 0; board.acceleration = new Vector(0,0); board.velocity = new Vector(0,0); } if(board.position.y > boundY - board.height){ board.position.y = boundY - board.height; board.acceleration = new Vector(0,0); board.velocity = new Vector(0,0); } board.move(); } var particleSize = 2; function drawParticle(){ ctx.fillStyle = 'rgb(255,255,255)'; ctx.beginPath(); ctx.arc(particle.position.x, particle.position.y, particleSize, 0, Math.PI * 2); ctx.closePath(); ctx.fill(); } var objectSize = 2; function drawCircle(object){ ctx.fillStyle = object.drawColor; ctx.beginPath(); ctx.arc(object.position.x, object.position.y, object.size || objectSize, 0, Math.PI * 2); ctx.closePath(); ctx.fill(); } function drawBoard(){ ctx.fillStyle = 'rgb(0,255,0)'; ctx.fillRect(board.position.x, board.position.y, board.width, board.height); } function drawRect(object){ ctx.fillStyle = object.drawColor; ctx.fillRect(object.position.x, object.position.y, object.width, object.height); } function drawPause(){ if(PAUSE) { ctx.fillStyle = "#999"; ctx.fillText('pause', 100, 100); } } function boardListenEvent(){ document.addEventListener('keydown', function(e){ switch(e.keyCode){ case 65: // console.log('keydown left'); if(board.velocity.x > 0){ board.velocity.x = 0; } board.acceleration = new Vector(-0.2, board.acceleration.y); break; case 68: // console.log('keydown right'); if(board.velocity.x < 0){ board.velocity.x = 0; } board.acceleration = new Vector(0.2, board.acceleration.y); break; case 32: // console.log('keydown run/pause toggle'); PAUSE = !PAUSE; if(!PAUSE) { window.requestAnimationFrame(loop); } break; default: console.log('keydown skip code: ', e.keyCode); } },false); document.addEventListener('keyup', function(e){ switch(e.keyCode){ case 65: // console.log('keyup left'); board.acceleration = new Vector(0, 0); board.velocity = new Vector(0, 0); break; case 68: // console.log('keyup right'); board.acceleration = new Vector(0, 0); board.velocity = new Vector(0, 0); break; default: // console.log('keyup skip code: ', e.keyCode); } // console.log('now velocity:', board.velocity); }, false); } var board,emitter,particle,obstacles; function begin(){ board = new Board(new Vector(0,canvas.height), 40, 5); emitter = new Emitter(new Vector(midX - 50, midY), Vector.fromAngle(-1, 2), Math.PI / 4); particle = emitter.emitParticle(); obstacles = generateObstacles(); } function loop(){ clear(); update(); draw(); queue(); } function clear(){ ctx.clearRect(0,0, canvas.width, canvas.height); } function update(){ plotBoard(canvas.width, canvas.height); plotParticle(canvas.width, canvas.height); plotObstacles(); } function draw(){ drawParticle(); drawBoard(); drawObstacles(); drawPause(); } function queue(){ if(!over() && !PAUSE){ window.requestAnimationFrame(loop); } else if(over()) { var confirm = window.confirm('game over, play again?'); if(confirm) { GAME_OVER = false; begin(); loop(); } } } function over(){ return GAME_OVER == true; } begin(); boardListenEvent(); loop(); </script> </body> </html>