HTML5 pool game using the canvas and my Cannon framework : https://github.com/y-lohse/Cannon
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Cannon.include('lib/math.js'); | |
Cannon.include('lib/display.js'); | |
Cannon.include('lib/misc.js'); | |
Cannon.include('lib/SAT.js'); | |
var canvas, cue; | |
var shooting = false, shootStart; | |
var rails = [], balls = [], pockets = []; | |
var WALL_SIZE = 10, BALL_SIZE = 10, POCKET_SIZE = BALL_SIZE*1.7; | |
Cannon.onReady = function(){ | |
Cannon.use('*'); | |
init(); | |
canvas = new Cannon.Canvas('canvas'); | |
var background = new Rectangle(0, 0, canvas.width, canvas.height); | |
canvas.addChild(background); | |
background.fillStyle = "#00cc00"; | |
cue = new Ball(canvas.width/2, canvas.height/2, BALL_SIZE); | |
canvas.addChild(cue); | |
cue.fillStyle = '#ffffff'; | |
balls.push(cue); | |
var rail = new Rail(0, 0, WALL_SIZE, canvas.height); | |
canvas.addChild(rail); | |
rail.fillStyle = '#cc3333'; | |
rails.push(rail); | |
var rail = new Rail(canvas.width-WALL_SIZE, 0, WALL_SIZE, canvas.height); | |
canvas.addChild(rail); | |
rail.fillStyle = '#cc3333'; | |
rails.push(rail); | |
var rail = new Rail(0, 0, canvas.width, WALL_SIZE); | |
canvas.addChild(rail); | |
rail.fillStyle = '#cc3333'; | |
rails.push(rail); | |
var rail = new Rail(0, canvas.height-WALL_SIZE, canvas.width, WALL_SIZE); | |
canvas.addChild(rail); | |
rail.fillStyle = '#cc3333'; | |
rails.push(rail); | |
var pocket = new Ball(10, 10, POCKET_SIZE); | |
canvas.addChild(pocket); | |
pocket.fillStyle = '#000000'; | |
pockets.push(pocket); | |
var pocket = new Ball(canvas.width/2, 5, POCKET_SIZE); | |
canvas.addChild(pocket); | |
pocket.fillStyle = '#000000'; | |
pockets.push(pocket); | |
var pocket = new Ball(canvas.width-10, 10, POCKET_SIZE); | |
canvas.addChild(pocket); | |
pocket.fillStyle = '#000000'; | |
pockets.push(pocket); | |
var pocket = new Ball(10, canvas.height-10, POCKET_SIZE); | |
canvas.addChild(pocket); | |
pocket.fillStyle = '#000000'; | |
pockets.push(pocket); | |
var pocket = new Ball(canvas.width/2, canvas.height-5, POCKET_SIZE); | |
canvas.addChild(pocket); | |
pocket.fillStyle = '#000000'; | |
pockets.push(pocket); | |
var pocket = new Ball(canvas.width-10, canvas.height-10, POCKET_SIZE); | |
canvas.addChild(pocket); | |
pocket.fillStyle = '#000000'; | |
pockets.push(pocket); | |
for (var i = 0; i < 10; i++){ | |
var ball = new Ball(Cannon.Utils.randomIn(POCKET_SIZE, canvas.width-POCKET_SIZE), Cannon.Utils.randomIn(POCKET_SIZE, canvas.height-POCKET_SIZE), BALL_SIZE); | |
canvas.addChild(ball); | |
ball.fillStyle = '#cc0000'; | |
balls.push(ball); | |
} | |
canvas.on('canvas:render', onRender); | |
cue.on('mousedown', function(event){ | |
shooting = true; | |
shootStart = new Point2D(canvas.mouseX, canvas.mouseY); | |
}); | |
canvas.on('canvas:mouseup', function(){ | |
if (shooting){ | |
var end = shootStart.substract(new Point2D(canvas.mouseX, canvas.mouseY)); | |
cue.direction = new Vector2D(Math.max(Math.min(end.x/5, 10), -10), Math.max(Math.min(end.y/5, 10), -10)); | |
} | |
}); | |
}; | |
function onRender(){ | |
for (var i = 0; i < rails.length; i++){ | |
var rail = rails[i]; | |
for (var j = 0; j < balls.length; j++){ | |
var ball = balls[j]; | |
ball.updateBounding(); | |
var col = CollisionSolver.solveCollsion(ball.boundingCircle, rail.boundingBox); | |
if (col.colliding){ | |
var parallel = ball.direction.clone(); | |
var perpendicular = ball.direction.clone(); | |
var parAxis = col.collisions[0].clone().normalize(); | |
var perAxis = col.collisions[0].clone().normalize(); | |
var parallelDP = parallel.dotProduct(parAxis); | |
parallelDP *= -1; | |
var perpendicularDP = perpendicular.dotProduct(perAxis.rightNormal()); | |
perAxis = perAxis.rightNormal(); | |
parAxis.multiply(parallelDP); | |
perAxis.multiply(perpendicularDP); | |
var escapeVector = parAxis.clone().add(perAxis); | |
ball.direction = escapeVector; | |
ball.x += col.collisions[0].x; | |
ball.y += col.collisions[0].y; | |
} | |
} | |
} | |
var removeMe = []; | |
for (var i = 0; i < balls.length; i++){ | |
var ball1 = balls[i]; | |
ball1.updateBounding(); | |
for (var j = i+1; j < balls.length; j++){ | |
var ball2 = balls[j]; | |
var col = CollisionSolver.solveCollsion(ball1.boundingCircle, ball2.boundingCircle); | |
if (col.colliding){ | |
var nx = (ball2.x - ball1.x)/(2*ball1.radius); | |
var ny = (ball2.y - ball1.y)/(2*ball1.radius); | |
var gx = -ny; | |
var gy = nx; | |
var v1n = nx*ball1.direction.x + ny*ball1.direction.y; | |
var v1g = gx*ball1.direction.x + gy*ball1.direction.y; | |
var v2n = nx*ball2.direction.x + ny*ball2.direction.y; | |
var v2g = gx*ball2.direction.x + gy*ball2.direction.y; | |
ball1.direction = new Vector2D(nx*v2n + gx*v1g, ny*v2n + gy*v1g); | |
ball2.direction = new Vector2D(nx*v1n + gx*v2g, ny*v1n + gy*v2g); | |
//out of collision | |
ball1.x += col.collisions[0].x; | |
ball1.y += col.collisions[0].y; | |
} | |
} | |
for (var j = 0; j < pockets.length; j++){ | |
var col = CollisionSolver.solveCollsion(ball1.boundingCircle, pockets[j].boundingCircle); | |
if (col.colliding && col.collisions[0].length() > 3){ | |
removeMe.push(ball1); | |
} | |
} | |
ball1.direction.multiply(.97); | |
ball1.x += ball1.direction.x; | |
ball1.y += ball1.direction.y; | |
} | |
for (var i = 0; i < removeMe.length; i++){ | |
if (removeMe[i] === cue){ | |
cue.direction = new Vector2D(0, 0); | |
cue.x = canvas.width/2; | |
cue.y = canvas.height/2; | |
} | |
else{ | |
canvas.removeChild(removeMe[i]); | |
balls = Cannon.Utils.arrayWithout(balls, removeMe[i]); | |
} | |
} | |
} | |
function init(){ | |
Ball = Cannon.Display.Circle.extend({ | |
__construct: function(x, y, radius){ | |
this._super(false); | |
this.x = (x || 0); | |
this.y = (y || 0); | |
this.radius = (radius || 50); | |
this.direction = new Cannon.Math.Vector2D(0, 0); | |
this.boundingCircle = new Cannon.SAT.BoundingCircle(this.x, this.y, this.radius); | |
}, | |
updateBounding: function(){ | |
this.boundingCircle.x = this.x; | |
this.boundingCircle.y = this.y; | |
this.boundingCircle.radius = this.radius; | |
}, | |
}); | |
Rail = Cannon.Display.Rectangle.extend({ | |
__construct: function(x, y, width, height){ | |
this._super(false); | |
this.x = (x || 0); | |
this.y = (y || 0); | |
this.width = (width || 50); | |
this.height = (height || 50); | |
this.center = [this.width/2, this.height/2]; | |
this.boundingBox = new Cannon.SAT.BoundingBox(this.x, this.y, this.width, this.height, this.rotation); | |
}, | |
}); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>Pool</title> | |
<meta name="Description" content="SAT powered pool game"/> | |
<style> | |
*{ | |
margin: 0; | |
padding: 0; | |
} | |
</style> | |
<script type="text/javascript" src="lib/cannon.js"></script> | |
<script type="text/javascript" src="game.js"></script> | |
</head> | |
<body> | |
<div id="canvas" style="width: 500px; height: 300px; border: 1px solid black;"></div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello Friend,Thank you for your codes and ideas.
I implemented your codes,but i cant get all balls randomly,i have get only one ball and it is also cue ball.
balance 9 balls i cant able to get.
please help me to solve my issues,and also cue functionality not working,cue pulling and releasing by mouse that is not working.
and your example the above functionality working fine.
i dont know whether i made mistakes or not.please help me to solve this issues
thank you