<!DOCTYPE html> <html onmousemove='mmove(event);' onmousedown='createball();' onmouseup='launch();'> <head> <meta charset='utf-8'> <title> Pong </title> <style> body { cursor: none; } #c { border: 1px solid black; } #frame { position: absolute; top: 20px; left: 20px; z-index: 10; } </style> </head> <body onload='start();'> <span id='frame'>0</span> <canvas id='c' width=1024 height=768></canvas> <script> var TOP = 0, BOTTOM = 1, LEFT = 2, RIGHT = 3; var _fn = 0, _frame = document.getElementById("frame"); var _width = 1024, _height=768, _pwidth=80, _pheight=10; var _ctx = document.getElementById("c").getContext("2d"); var _px = 0, _py = _height-30, _ball = null, _newball = false; var _tiles = []; // From: https://stackoverflow.com/questions/9043805/test-if-two-lines-intersect-javascript-function // returns true iff the line from (a,b)->(c,d) intersects with (p,q)->(r,s) function intersects(a,b,c,d,p,q,r,s) { var det, gamma, lambda; det = (c - a) * (s - q) - (r - p) * (d - b); if (det === 0) { return false; } else { lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / det; gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / det; return (0 < lambda && lambda < 1) && (0 < gamma && gamma < 1); } }; function intersectx(x1, y1, x2, y2, x3, y3, x4, y4) { var a_dx = x2 - x1; var a_dy = y2 - y1; var b_dx = x4 - x3; var b_dy = y4 - y3; var s = (-a_dy * (x1 - x3) + a_dx * (y1 - y3)) / (-b_dx * a_dy + a_dx * b_dy); var t = (+b_dx * (y1 - y3) - b_dy * (x1 - x3)) / (-b_dx * a_dy + a_dx * b_dy); return (s >= 0 && s <= 1 && t >= 0 && t <= 1); } function framecounter() { _frame.innerHTML = _fn; _fn = 0; } function start() { for(var i = 0; i < _width / 80; i++) { _tiles.push(new Tile(i*80, 50)); _tiles.push(new Tile(i*80, 70)); _tiles.push(new Tile(i*80, 90)); _tiles.push(new Tile(i*80, 110)); _tiles.push(new Tile(i*80, 130)); _tiles.push(new Tile(i*80, 300)); _tiles.push(new Tile(i*80, 320)); _tiles.push(new Tile(i*80, 340)); _tiles.push(new Tile(i*80, 360)); } setTimeout(update, 16); setInterval(framecounter, 1000); } function update() { _ctx.clearRect(0,0,_width, _height); _ctx.fillStyle = "red"; _ctx.beginPath(); _ctx.rect(_px, _py, _pwidth, _pheight); _ctx.closePath(); _ctx.fill(); if (_newball) { _ctx.fillStyle = "black"; _ctx.beginPath(); _ctx.arc(_px+(_pwidth/2)-5, _py-5, 5, 0, 360); _ctx.closePath(); _ctx.fill(); } if (_ball != null) { var ox=_ball.x, oy = _ball.y; _ball.update(); if (_ball != null) { for(var i = 0; i < _tiles.length;) { if (_tiles[i].hit(_ball.x, _ball.y, _ball.r)) { console.log("hit"); var side = _tiles[i].intersect(ox, oy, _ball.x, _ball.y); _ball.bounce(side, _tiles[i].x,_tiles[i].y); _tiles.splice(i, 1); i = 0; } i++; } _ball.draw(); } } for(var i = 0; i < _tiles.length; i++) { _tiles[i].draw(); } _fn++; setTimeout(update, 16); } function mmove(e) { _px = Math.max(0, Math.min(e.clientX-(_pwidth/2), _width-_pwidth)); } function createball() { if (_ball == null) _newball = true; } function R(a, b) { var range = Math.abs(a-b)+1; var min = Math.min(a,b); return Math.floor(Math.random() * range) + min; } // Define a new tile object: function Tile(x,y) { this.x = x; this.xr = x + 80; this.y = y; this.yb = y + 20; this.color = "rgb(" + R(0,255) + "," + R(0,255) + "," + R(0,255) + ")"; this.draw = function() { _ctx.fillStyle = this.color; _ctx.beginPath(); _ctx.rect(this.x, this.y, 80, 20); _ctx.closePath(); _ctx.fill(); } // Check if the ball would be inside of the box (i.e. has hit it): this.hit = function(x, y, r) { if (x >= this.x && x <= this.xr) if (y >= this.y && y <= this.yb) return true; return false; } // Determine which side of the box the path of the ball intersects: this.intersect = function(ox, oy, x, y) { if (intersects(ox,oy,x,y, this.x, this.yb, this.xr, this.yb)) return BOTTOM; if (intersects(ox,oy,x,y, this.x, this.y, this.xr, this.y)) return TOP; if (Math.abs((x+9) - this.x) < Math.abs(x - (this.xr))) return LEFT; return RIGHT; } } function Ball(x, y) { this.x = x; this.y = y; this.r = 5; var speed = (Math.random()*8)+8; var angle = -((Math.random()*(Math.PI/2)) + Math.PI/4); this.dx = speed * Math.cos(angle); this.dy = speed * Math.sin(angle); this.draw = function() { _ctx.fillStyle = "black"; _ctx.beginPath(); _ctx.arc(this.x,this.y,this.r,0,360); _ctx.closePath(); _ctx.fill(); }; // Determine how the ball would bounce off a tile: this.bounce = function(side, x, y) { // console.log("bounce %s", side == TOP? "TOP" : side == BOTTOM ? "BOTTOM" : side == LEFT? "LEFT" : "RIGHT"); switch(side) { case TOP: this.y = (y - ((this.y+5)-y))-5; break; case BOTTOM: y+=20; this.y = y + (y-this.y); break; case LEFT: this.x = (x - ((this.x+10)-x))-10; break; case RIGHT: x += 80; this.x = x + (x-this.x); break; } if (side == TOP || side == BOTTOM) this.dy = -this.dy; else this.dx = -this.dx; }; // Update the ball position: this.update = function() { var ox = this.x, oy = this.y; this.x += this.dx; if (this.x-this.r < 0) { this.x = this.r - (this.x-this.r); this.dx = -this.dx; } if (this.x+this.r > _width) { this.x = (_width-this.r) - ((this.x+this.r)-_width); this.dx = -this.dx; } this.y += this.dy; if (this.y < 0) { this.y = this.r - (this.y-this.r); this.dy = -this.dy; } if (intersectx(ox, oy, this.x, this.y+9, _px, _py, _px+_pwidth, _py)) { // if (this.y+10 >= _py && this.y < _py+10 && this.x+9 >= _px && this.x <= _px+_pwidth) { this.y = (_py-10) - (_py-(this.y+9)); this.dy = -this.dy; } // If we drop off the bottom, delete the ball: if (this.y > _height) { _ball = null; } } } // Launch a ball on mouse-up if we've previously pressed down the mouse button: function launch() { if (_ball == null && _newball == true) { _ball = new Ball(_px+(_pwidth/2)-10, _py-10); _newball = false; } } </script> </body> </html>