Last active
May 23, 2022 08:19
-
-
Save daveknights/9e73111c30d54443a2da2f2230191d12 to your computer and use it in GitHub Desktop.
Very basic attempt at a snooker style game using P5.
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Pot Black</title> | |
<script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.js"></script> | |
<style> | |
body { | |
font-family: Arial, Helvetica, sans-serif; | |
margin: 0; | |
text-align: center; | |
} | |
main { | |
align-items: center; | |
display: flex; | |
flex-direction: column; | |
} | |
p { | |
margin-top: 0; | |
} | |
#shot-feedback { | |
color: white; | |
font-size: 1.5rem; | |
margin-bottom: 0; | |
padding: 12px; | |
width: 200px; | |
} | |
.pot { | |
background: black; | |
} | |
.foul { | |
background: red; | |
} | |
</style> | |
</head> | |
<body> | |
<header> | |
<h1>Pot Black</h1> | |
</header> | |
<main> | |
<p>Aim & click</p> | |
<p id="shot-feedback"></p> | |
</main> | |
<script src="sketch.js"></script> | |
</body> | |
</html> |
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
const feedback = document.querySelector('#shot-feedback'); | |
let whiteBall, whiteX, whiteY, whiteTargetX, whiteTargetY, moveWhiteBall, | |
blackBall, blackX, blackY, blackTargetX, blackTargetY, moveBlackBall, | |
calculatePath, inTopPocket, inBtmPocket, bounceX, bounceTopY, bounceBtmY; | |
let i = 0, j=0; | |
function createEllipse(fillColour, shadowVal, rad) { | |
return { | |
show: function(x, y) { | |
drawingContext.shadowOffsetY = shadowVal; | |
drawingContext.shadowBlur = shadowVal*2; | |
drawingContext.shadowColor = '#020'; | |
noStroke(); | |
fill(fillColour); | |
ellipse(x, y, rad); | |
}, | |
r: rad | |
}; | |
} | |
function cushion([tl, tr, br, bl], shadow) { | |
drawingContext.shadowOffsetX = shadow.x; | |
drawingContext.shadowOffsetY = shadow.y; | |
drawingContext.shadowBlur = 8; | |
fill('#060'); | |
beginShape(); | |
vertex(tl.x, tl.y); | |
vertex(tr.x, tr.y); | |
vertex(br.x, br.y); | |
vertex(bl.x, bl.y); | |
endShape(CLOSE); | |
} | |
function guideLine(msY) { | |
const targetStart = createVector(whiteX, whiteY); | |
const visibleStart = createVector(whiteX, whiteY); | |
const targetEnd = createVector(width, msY); | |
const visibleEnd = createVector(width, msY); | |
const maxVisibleEnd = visibleEnd.sub(visibleStart).normalize().mult(320).add(visibleStart); | |
stroke('aqua'); | |
drawingContext.setLineDash([5, 15]); | |
guide = line(visibleStart.x, visibleStart.y, maxVisibleEnd.x, maxVisibleEnd.y); | |
const maxTargetEnd = targetEnd.sub(targetStart).normalize().mult(370).add(targetStart); | |
whiteTargetX = maxTargetEnd.x; | |
whiteTargetY = maxTargetEnd.y; | |
return guide; | |
} | |
function showFeedback(text, feedbackClass) { | |
feedback.textContent = text; | |
feedback.classList.add(feedbackClass); | |
} | |
function calculateBlackTarget([x, y]) { | |
const path = createVector(x, y, 0); | |
const endPoints = path.lerp(floor(blackX), floor(blackY), 0, 16.5); | |
blackTargetX = endPoints.x; | |
blackTargetY = endPoints.y | |
calculatePath = false; | |
moveBlackBall = true; | |
} | |
function resetTable(startPos, pockets, path, bounces, balls) { | |
(startPos === true) && (whiteX = 80, whiteY = height/2, blackX = width/2, blackY = height/2); | |
(pockets === true) && (inTopPocket = false, inBtmPocket = false); | |
(path === true) && (calculatePath = undefined); | |
(bounces === true) && (bounceX = false, bounceTopY = false, bounceBtmY = false) | |
balls.length && (moveWhiteBall = balls[0], moveBlackBall = balls[1]); | |
} | |
function setup() { | |
const canvas = createCanvas(760, 560); | |
canvas.position((innerWidth/2 - width/2), 190); | |
// Reset options: startPos, pockets, path, bounces, balls[moveWhite, moveBlack] | |
resetTable(true, true, false, false, [false, false]); | |
topPocket = createEllipse('#030', 0, 60); | |
btmPocket = createEllipse('#030', 0, 60); | |
whiteBall = createEllipse('white', 5, 35); | |
blackBall = createEllipse('black', 5, 35); | |
} | |
function draw() { | |
background('white'); | |
// table | |
drawingContext.shadowOffsetY = 0; | |
drawingContext.shadowBlur = 0; | |
fill('#60340e'); | |
rect(0, 0, width, height, 0, 35, 35 ,0); | |
// Top corner | |
fill('#d8af6a'); | |
drawingContext.shadowOffsetX = 0; | |
rect(width - 80, 0, 80, 80, 0, 30, 0, 30); | |
// Bottom corner | |
rect(width - 80, height - 80, 80, 80, 30, 0, 30, 0); | |
// Baize | |
fill('#060'); | |
rect(0, 40, 720, 480); | |
// Pockets | |
fill('#060'); | |
drawingContext.shadowOffsetX = 0; | |
topPocket.show(width - 54, 54); | |
btmPocket.show(width - 54, height - 54); | |
// Balls & guide | |
if ((!inTopPocket && !inBtmPocket) || blackX < width - 50) { | |
blackBall.show(blackX, blackY); | |
} | |
!moveWhiteBall && guideLine(mouseY); | |
whiteBall.show(whiteX, whiteY); | |
// Top cushion | |
cushion( | |
[ | |
{x: 0, y: 40}, {x: width - 80, y: 40}, | |
{x: width - 100, y: 60}, {x: 0, y: 60}], | |
{x: 0, y: 4, blur: 8} | |
); | |
// Btm cushion | |
cushion( | |
[ | |
{x: 0, y: height - 60}, {x: width - 100, y: height- 60}, | |
{x: width - 80, y: height - 40}, {x: 0, y: height - 40} | |
], | |
{x: 0, y: -4} | |
); | |
// End cushion | |
cushion( | |
[ | |
{x: width - 60, y: 100}, {x: width - 40, y: 80}, | |
{x: width - 40, y: height - 80}, {x: width - 60, y: height - 100} | |
], | |
{x: -4, y: 0} | |
); | |
// Ball movement logic | |
if (moveWhiteBall && floor(whiteX) !== floor(whiteTargetX)) { | |
whiteX = lerp(whiteX, whiteTargetX, 0.1); | |
whiteY = lerp(whiteY, whiteTargetY, 0.1); | |
if (floor(whiteX) === floor(whiteTargetX) && !moveBlackBall) { | |
showFeedback('Foul', 'foul'); | |
// Reset options: startPos, pockets, path, bounces, balls[moveWhite, moveBlack] | |
resetTable(true, false, false, false, [false, null]); | |
} | |
} | |
if (moveBlackBall && floor(blackX) !== floor(blackTargetX) && (!bounceX && !bounceTopY && !bounceBtmY)) { | |
blackX = lerp(blackX, blackTargetX, 0.05); | |
blackY = lerp(blackY, blackTargetY, 0.05); | |
} else if (floor(blackX) === floor(blackTargetX)) { | |
// Reset options: startPos, pockets, path, bounces, balls[moveWhite, moveBlack] | |
resetTable(true, true, true, false, [false, false]); | |
} | |
// Ball collision logic | |
const ballDistance = dist(whiteX, whiteY, blackX, blackY); | |
if ((ballDistance < (whiteBall.r/2) + (blackBall.r/2)) && calculatePath !== false) { | |
calculateBlackTarget([whiteX, whiteY]); | |
} | |
// cushion bounce logic | |
if ((blackX > width - 74 && (!inTopPocket && !inBtmPocket)) || bounceX) { | |
// const extraDist = blackTargetX - (width - 60); | |
const newBlackTargetX = (width - 60) - (blackTargetX - (width - 60)); | |
bounceX = true; | |
if (floor(blackX) !== floor(newBlackTargetX)) { | |
blackX = lerp(blackX, newBlackTargetX, 0.1); | |
} else { | |
// Reset options: startPos, pockets, path, bounces, balls[moveWhite, moveBlack] | |
resetTable(true, false, true, true, [false, false]); | |
} | |
} | |
if ((blackY < 75 && (!inTopPocket && !inBtmPocket)) || bounceTopY) { | |
const newBlackTargetY = blackTargetY < -90 ? blackTargetY * -1 : 90; | |
bounceTopY = true; | |
if (floor(blackY) !== floor(newBlackTargetY) -1) { | |
blackY = lerp(blackY, newBlackTargetY, 0.1); | |
} else { | |
// Reset options: startPos, pockets, path, bounces, balls[moveWhite, moveBlack] | |
resetTable(true, false, true, true, [false, false]); | |
} | |
} | |
if ((blackY > height - 75 && (!inTopPocket && !inBtmPocket)) || bounceBtmY) { | |
const newBlackTargetY = blackTargetY - blackX > 90 ? height - 180 : height - 90; | |
bounceBtmY = true; | |
if (floor(blackY) !== floor(newBlackTargetY)) { | |
blackY = lerp(blackY, newBlackTargetY, 0.1); | |
} else { | |
// Reset options: startPos, pockets, path, bounces, balls[moveWhite, moveBlack] | |
resetTable(true, false, true, true, [false, false]); | |
} | |
} | |
// Ball in pocket logic | |
const topPocketDistance = dist(blackX, blackY, width - 48, 48); | |
const btmPocketDistance = dist(blackX, blackY, width - 48, height - 48); | |
if (topPocketDistance < (topPocket.r/2) + (blackBall.r/2)) { | |
inTopPocket = true; | |
showFeedback('Nice pot', 'pot'); | |
} | |
if (btmPocketDistance < (btmPocket.r/2) + (blackBall.r/2)) { | |
inBtmPocket = true; | |
showFeedback('Nice pot', 'pot'); | |
} | |
} | |
function mousePressed() { | |
feedback.textContent = ''; | |
feedback.classList.remove('pot', 'foul'); | |
moveWhiteBall = true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment