commit 5c8296fbe31c8d0d88252b9d7b22c42fcec6e683 Author: Marcel Date: Sat Apr 6 19:19:26 2019 +0200 Initial commit diff --git a/assets/game_over.wav b/assets/game_over.wav new file mode 100644 index 0000000..57956ec Binary files /dev/null and b/assets/game_over.wav differ diff --git a/assets/hit_ball.wav b/assets/hit_ball.wav new file mode 100644 index 0000000..c05d2a5 Binary files /dev/null and b/assets/hit_ball.wav differ diff --git a/assets/hit_block.wav b/assets/hit_block.wav new file mode 100644 index 0000000..0b1036e Binary files /dev/null and b/assets/hit_block.wav differ diff --git a/assets/hit_coin.wav b/assets/hit_coin.wav new file mode 100644 index 0000000..763ef14 Binary files /dev/null and b/assets/hit_coin.wav differ diff --git a/assets/round_success.wav b/assets/round_success.wav new file mode 100644 index 0000000..262b970 Binary files /dev/null and b/assets/round_success.wav differ diff --git a/ball.js b/ball.js new file mode 100644 index 0000000..0d14a0d --- /dev/null +++ b/ball.js @@ -0,0 +1,107 @@ +class Ball { + constructor(game, cx, cy, v, dir) { + this.game = game; + this.id = this.game.balls.length; + this.cx = cx; + this.cy = cy; + this.vx = v * Math.cos(dir); + this.vy = v * Math.sin(dir); + this.collide = true; + } + + update(stepCount = 10) { + if (!this.game.isMoving) + return; + + for (let i = 0; i < stepCount; i++) { + this.cx += this.vx / stepCount; + this.cy -= this.vy / stepCount; + + if (this.cx - this.game.ballRadius <= 0 || this.cx + this.game.ballRadius >= this.game.canvas.width) { + this.vx *= -1; + } + + if (this.cy - this.game.ballRadius <= 0) { + this.vy *= -1; + } + + if (this.cy >= this.game.originY && this.vy < 0) { + this.vy = 0; + this.cy = this.game.originY; + + if (this.game.newOriginX === null || this.cx === this.game.newOriginX) { + this.game.newOriginX = this.cx; + this.vy = 0; + this.remove(); + } else { + this.vx = (this.game.newOriginX - this.cx) / 60 * 2; + setTimeout(() => { + this.vx = 0; + this.remove(); + }, 500); + } + } + + if (this.collide) { + this.game.rects.forEach(rect => { + this.checkHitRect(rect); + }); + + this.game.powerUps.forEach(powerUp => { + this.checkHitPowerUp(powerUp); + }); + } + } + } + + remove() { + this.game.balls.splice(this.game.balls.findIndex(ball => ball === this), 1); + + if (this.game.isMoving && this.game.balls.length === 0) { + this.game.roundOver(); + } + } + + checkHitRect(rect) { + const closestX = Math.clamp(this.cx, rect.x, rect.x + this.game.rectSize); + const closestY = Math.clamp(this.cy, rect.y, rect.y + this.game.rectSize); + + const distX = this.cx - closestX; + const distY = this.cy - closestY; + + const distSquared = (distX ** 2) + (distY ** 2); + + if (distSquared < this.game.ballRadius ** 2) { + const collBottom = rect.y + this.game.rectSize - (this.cy - this.game.ballRadius); + const collTop = (this.cy + this.game.ballRadius) - rect.y; + const collLeft = (this.cx + this.game.ballRadius) - rect.x; + const collRight = rect.x + this.game.rectSize - (this.cx - this.game.ballRadius); + + if ((collTop <= collLeft && collTop <= collRight) || (collBottom <= collLeft && collBottom <= collRight)) { + this.vy *= -1; + } else { + this.vx *= -1; + } + + rect.hit(this); + } + } + + checkHitPowerUp(powerUp) { + const distX = this.cx - powerUp.cx; + const distY = this.cy - powerUp.cy; + + const distSquared = (distX ** 2) + (distY ** 2); + + if (distSquared < (this.game.ballRadius + this.game.powerUpRadius) ** 2) { + powerUp.hit(); + } + } + + draw() { + this.game.ctx.fillStyle = '#fff'; + this.game.ctx.beginPath(); + this.game.ctx.arc(this.cx, this.cy, this.game.ballRadius, 0, 2 * Math.PI); + this.game.ctx.fill(); + } +} diff --git a/game.js b/game.js new file mode 100644 index 0000000..b7b951e --- /dev/null +++ b/game.js @@ -0,0 +1,298 @@ +class Game { + constructor(container) { + this.elements = { + highscore: container.querySelector('#highscore'), + score: container.querySelector('#score'), + coinCount: container.querySelector('#coin-count'), + pauseBtn: container.querySelector('.play-btn'), + pauseMenu: container.querySelector('#pause-menu'), + gameOverMenu: container.querySelector('#game-over-menu'), + }; + + this.canvas = container.querySelector('#canvas'); + this.ctx = this.canvas.getContext('2d'); + this.width = this.canvas.width; + this.height = this.canvas.height; + + this.colCount = 7; + this.rowCount = 7; + this.rectSize = this.width / this.colCount; + + this.originX = this.width / 2; + this.originY = (this.rowCount + 2) * this.rectSize; + this.newOriginX = null; + + this.ballRadius = 12; + this.ballVelocity = 15; + + this.powerUpRadius = 15; + + this.rects = []; + this.powerUps = []; + this.balls = []; + + this.roundNumber = 0; + this.ballCount = 1; + + this.remainingBalls = this.ballCount; + this.newBalls = 0; + this.isMoving = false; + this.isPaused = false; + this.isGameOver = false; + + this.coinAngle = 0; + this.mousePos = null; + + this.addEventListeners(); + } + + addEventListeners() { + this.canvas.addEventListener('mousedown', (e) => { + this.mousePos = cbRelMousePos(e); + }); + + this.canvas.addEventListener('mousemove', (e) => { + if (this.mousePos) { + this.mousePos = cbRelMousePos(e); + } + }); + + this.canvas.addEventListener('mouseup', (e) => { + this.mousePos = cbRelMousePos(e); + this.shoot().then(() => { + this.mousePos = null; + }); + }); + + this.elements.pauseBtn.addEventListener('click', () => { + if (this.isPaused) { + this.play(); + } else { + this.pause(); + } + }); + } + + removeEventListeners() { + + } + + draw() { + this.ctx.clearRect(0, 0, this.width, this.height); + this.drawBase(); + + this.rects.forEach(rect => rect.draw()); + this.powerUps.forEach(powerUp => powerUp.draw()); + this.balls.forEach(ball => ball.draw()); + + this.drawOrigin(); + + if (!this.isPaused) { + requestAnimationFrame(() => this.draw()); + } + } + + drawBase() { + const y = this.originY + this.ballRadius; + const height = this.height - this.originY - this.ballRadius; + + this.ctx.fillStyle = 'rgba(0,0,0,.15)'; + this.ctx.fillRect(0, y, this.width, height); + } + + drawOrigin() { + if (this.originX !== null) { + if (!this.isMoving && this.mousePos !== null) { + this.drawShootingLine(); + } + + this.ctx.fillStyle = '#fff'; + this.ctx.fillCircle(this.originX, this.originY, this.ballRadius); + + if (this.remainingBalls > 0) { + this.ctx.fillText(this.remainingBalls + 'x', this.originX, this.originY + 50); + } + } + + if (this.newOriginX !== null) { + this.ctx.fillStyle = '#fff'; + this.ctx.fillCircle(this.newOriginX, this.originY, this.ballRadius); + } + } + + drawShootingLine() { + const dir = this.calcMouseAngle(); + let curX = this.originX; + let curY = this.originY; + + for (let i = 0; i < 10; i++) { + const alpha = 1 - (i / 20); + this.ctx.fillStyle = `rgba(255,255,255,${alpha})`; + this.ctx.beginPath(); + this.ctx.arc(curX, curY, this.ballRadius, 0, 2 * Math.PI); + this.ctx.fill(); + + curX += Math.cos(dir) * this.ballRadius * 4; + curY -= Math.sin(dir) * this.ballRadius * 4; + } + } + + calcMouseAngle() { + const dx = this.mousePos.x - this.originX, + dy = this.originY - this.mousePos.y; + + let dir = Math.atan(dy / dx); + if (dx < 0) + dir += Math.PI; + + dir = Math.min(Math.max(dir, .05), Math.PI - 0.05); + return dir; + } + + async shoot() { + if (this.isMoving || this.mousePos >= this.originY) { + return; + } + + this.isMoving = true; + + const dir = this.calcMouseAngle(); + const ballCount = this.ballCount; + for (let i = 0; i < ballCount; i++) { + const ball = new Ball(this, this.originX, this.originY, this.ballVelocity, dir); + this.balls.push(ball); + this.remainingBalls--; + if (this.remainingBalls === 0) { + this.originX = null; + } + await sleep(100); + } + + } + + roundOver() { + this.ballCount += this.newBalls; + this.newBalls = 0; + this.remainingBalls = this.ballCount; + this.originX = this.newOriginX; + this.newOriginX = null; + + this.increaseRoundNumber(); + this.addNewObjects(); + this.shiftObjectsDown(); + + sounds.roundOver.play(); + + setTimeout(() => { + this.isMoving = false; + }, 500); + } + + gameOver() { + this.isGameOver = true; + coinCount += Math.floor((this.roundNumber - this.rowCount) / 10); + + this.elements.gameOverMenu.classList.add('shown'); + + onGameOver(); + } + + addNewObjects() { + const spawnBall = Math.random() < .5, + ballPos = spawnBall ? Math.round(Math.random() * (this.colCount - 1)) : null, + spawnCoin = Math.random() < .33, + coinPos = spawnCoin ? Math.round(Math.random() * (this.colCount - 1)) : null; + + for (let i = 0; i < this.colCount; i++) { + if (spawnBall && ballPos === i) { + this.powerUps.push(new PowerUpBall(this, (i + .5) * this.rectSize, .5 * this.rectSize)); + continue; + } + + if (spawnCoin && coinPos === i) { + this.powerUps.push(new PowerUpCoin(this, (i + .5) * this.rectSize, .5 * this.rectSize)); + continue; + } + + if (Math.random() < .6) { + const number = Math.random() < .6 ? this.roundNumber : 2 * this.roundNumber; + this.rects.push(new Rect(this, i * this.rectSize, 0, number)); + } + } + } + + shiftObjectsDown() { + this.rects.forEach(rect => { + moveObject(rect, 0, this.rectSize, 500); + + if (rect.y + 2 * this.rectSize >= this.originY) { + this.gameOver(); + } + }); + + this.powerUps.forEach(powerUp => { + moveObject(powerUp, 0, this.rectSize, 500); + + if (powerUp.cy + this.rectSize >= this.originY) { + setTimeout(() => { + powerUp.remove(); + }); + } + }) + } + + increaseRoundNumber() { + this.roundNumber++; + + this.elements.score.innerText = this.roundNumber; + } + + increaseCoinCount() { + coinCount++; + + this.elements.coinCount.innerText = coinCount; + } + + update() { + this.balls.forEach(ball => { + ball.update(10); + }); + + this.coinAngle += .05; + if (this.coinAngle >= 2 * Math.PI) { + this.coinAngle -= 2 * Math.PI; + } + + if (!this.isPaused && !this.isGameOver) { + requestAnimationFrame(() => this.update()); + } + } + + start() { + this.increaseRoundNumber(); + this.addNewObjects(); + this.shiftObjectsDown(); + this.play() + } + + play() { + this.isPaused = false; + this.update(); + this.draw(); + + this.elements.pauseBtn.classList.add('paused'); + this.elements.pauseMenu.classList.remove('shown'); + } + + pause() { + this.isPaused = true; + + this.elements.pauseBtn.classList.remove('paused'); + this.elements.pauseMenu.classList.add('shown'); + } + + destroy() { + this.removeEventListeners(); + } + +} diff --git a/helper-functions.js b/helper-functions.js new file mode 100644 index 0000000..0246501 --- /dev/null +++ b/helper-functions.js @@ -0,0 +1,120 @@ +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +function moveObject(object, amountX, amountY, time) { + let counter = 0; + const step = time / 1000 * 60; + const stepX = amountX / step; + const stepY = amountY / step; + + let goalX; + let goalY; + if (object.hasOwnProperty('cx') && object.hasOwnProperty('cy')) { + goalX = object.cx + amountX; + goalY = object.cy + amountY; + } else { + goalX = object.x + amountX; + goalY = object.y + amountY; + } + + const interval = setInterval(() => { + if (object.hasOwnProperty('cx') && object.hasOwnProperty('cy')) { + object.cx += stepX; + object.cy += stepY; + } else { + object.x += stepX; + object.y += stepY; + } + counter++; + + if (counter >= step) { + clearInterval(interval); + + if (object.hasOwnProperty('cx') && object.hasOwnProperty('cy')) { + object.cx = goalX; + object.cy = goalY; + } else { + object.x = goalX; + object.y = goalY; + } + } + }, 1000 / 60); +} + +function calculateColor(number) { + let minHue; + let maxHue; + let percentage; + if (number < 10) { + minHue = 25; + maxHue = 35; + percentage = number / 10; + } else if (number < 20) { + minHue = 60; + maxHue = 80; + percentage = (number - 10) / 10; + } else if (number < 50) { + minHue = 310; + maxHue = 340; + percentage = (number - 20) / 30; + } else if (number < 100) { + minHue = 35; + maxHue = 65; + percentage = (number - 50) / 50; + } else if (number < 200) { + minHue = 260; + maxHue = 280; + percentage = (number - 100) / 100; + } + const hue = (percentage * (maxHue - minHue)) + minHue; + return `hsl(${hue}, 100%, 50%)`; +} + +function cbElementPos(event) { + event = event || window.event; + let obj = event.target || event.srcElement, + x = 0, + y = 0; + while (obj.offsetParent) { + x += obj.offsetLeft; + y += obj.offsetTop; + obj = obj.offsetParent; + } + return {x, y}; +} + +function cbMousePos(event) { + event = event || window.event; + return { + x: event.pageX || event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft, + y: event.pageY || event.clientY + document.body.scrollTop + document.documentElement.scrollTop, + } +} + +function cbRelMousePos(event) { + const elem = cbElementPos(event), + mouse = cbMousePos(event); + return { + x: (mouse.x - elem.x), + y: (mouse.y - elem.y), + }; +} + +window.requestAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.msRequestAnimationFrame || + window.mozRequestAnimationFrame || + function (callback) { + return setTimeout(callback, 1000 / 60); + }; + +Math.clamp = function (a, b, c) { + return Math.max(b, Math.min(c, a)); +}; + +CanvasRenderingContext2D.prototype.fillCircle = function (cx, cy, radius) { + this.beginPath(); + this.arc(cx, cy, radius, 0, 2 * Math.PI); + this.fill(); +}; diff --git a/index.html b/index.html new file mode 100644 index 0000000..578386d --- /dev/null +++ b/index.html @@ -0,0 +1,63 @@ + + + + + Ballz.js + + + + +
+
+ + +
+ Best: +

0

+
+ +
+

0

+
+ +
+ Coins: +

0

+
+ +
+
+ +
+ + +
+

Pausing

+ +
+ + + + +
+
+ +
+ +
+
+
+ + + + + + + + + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..2169ceb --- /dev/null +++ b/main.js @@ -0,0 +1,17 @@ +const sounds = { + hitBlock: new Audio('assets/hit_block.wav'), + hitCoin: new Audio('assets/hit_coin.wav'), + hitBall: new Audio('assets/hit_ball.wav'), + roundOver: new Audio('assets/round_success.wav'), + gameOver: new Audio('assets/game_over.wav') +}; + +let coinCount = 0; + +const container = document.getElementsByClassName('container')[0]; +const game = new Game(container); +game.start(); + +function onGameOver() { + +} diff --git a/powerup-ball.js b/powerup-ball.js new file mode 100644 index 0000000..3025c28 --- /dev/null +++ b/powerup-ball.js @@ -0,0 +1,36 @@ +class PowerUpBall { + constructor(game, cx, cy) { + this.game = game; + this.cx = cx; + this.cy = cy; + this.anim = 0.1; + this.animChange = 0.025; + } + + draw() { + this.game.ctx.fillStyle = '#fff'; + this.game.ctx.strokeStyle = '#fff'; + this.game.ctx.beginPath(); + this.game.ctx.arc(this.cx, this.cy, this.game.powerUpRadius * .6, 0, 2 * Math.PI); + this.game.ctx.fill(); + + this.game.ctx.beginPath(); + this.game.ctx.arc(this.cx, this.cy, this.game.powerUpRadius * (.6 + this.anim), 0, 2 * Math.PI); + this.game.ctx.stroke(); + + this.anim = this.anim + this.animChange; + if (this.anim >= .4 || this.anim <= 0.1) { + this.animChange *= -1; + } + } + + hit() { + sounds.hitBall.play(); + this.game.newBalls++; + this.remove(); + } + + remove() { + this.game.powerUps.splice(this.game.powerUps.findIndex(powerUp => powerUp === this), 1); + } +} diff --git a/powerup-coin.js b/powerup-coin.js new file mode 100644 index 0000000..e8623b3 --- /dev/null +++ b/powerup-coin.js @@ -0,0 +1,24 @@ +class PowerUpCoin { + constructor(game, cx, cy) { + this.game = game; + this.cx = cx; + this.cy = cy; + } + + draw() { + this.game.ctx.fillStyle = '#fa0'; + this.game.ctx.beginPath(); + this.game.ctx.ellipse(this.cx, this.cy, Math.abs(this.game.powerUpRadius * Math.cos(this.game.coinAngle)), this.game.powerUpRadius, 0, 0, 2 * Math.PI); + this.game.ctx.fill(); + } + + hit() { + sounds.hitCoin.play(); + this.game.increaseCoinCount(); + this.remove(); + } + + remove() { + this.game.powerUps.splice(this.game.powerUps.findIndex(powerUp => powerUp === this), 1); + } +} diff --git a/rect.js b/rect.js new file mode 100644 index 0000000..394b076 --- /dev/null +++ b/rect.js @@ -0,0 +1,37 @@ +class Rect { + constructor(game, x, y, number) { + this.game = game; + this.x = x; + this.y = y; + this.number = number; + this.color = calculateColor(number); + this.lastHits = {}; + } + + draw() { + this.game.ctx.fillStyle = this.color; + this.game.ctx.fillRect(this.x + 2, this.y + 2, this.game.rectSize - 4, this.game.rectSize - 4); + + this.game.ctx.fillStyle = '#000'; + this.game.ctx.font = '20px Roboto, Arial, sans-serif'; + this.game.ctx.textAlign = 'center'; + this.game.ctx.textBaseline = 'middle'; + this.game.ctx.fillText(this.number, this.x + this.game.rectSize / 2, this.y + this.game.rectSize / 2); + } + + hit(ball) { + if (this.lastHits[ball.id] && Date.now() - this.lastHits[ball.id] < 100) { + return; + } + + this.number--; + this.color = calculateColor(this.number); + sounds.hitBlock.play(); + + if (this.number <= 0) { + this.game.rects.splice(this.game.rects.findIndex(rect => rect === this), 1); + } + + this.lastHits[ball.id] = Date.now(); + } +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..dab9793 --- /dev/null +++ b/style.css @@ -0,0 +1,171 @@ +body { + width: 100%; + height: 100%; + margin: 0; + background: #333; +} + +* { + color: #fff; + font-family: Roboto, Arial, sans-serif; + font-weight: 500; +} + +.btn { + padding: 10px 20px; + border: none; + border-radius: 10px; + background: none; + color: #fff; + box-shadow: none; + font-size: 16px; + cursor: pointer; +} + +.btn.primary { + background: #2272ff; +} + +.btn.danger { + background: #F72754; +} + +.container { + display: block; + width: 100%; + max-width: 500px; + margin: 10px auto; +} + +.details { + display: grid; + grid-template-columns: 1fr 2fr 2fr 1fr 1fr; +} + +.details h1, .details h3, .details .details-title { + display: block; + width: 100%; + margin: 0; + text-align: center; +} + +.menu-btn-container { + display: block; +} + +.menu-btn-container .play-btn { + position: relative; + display: block; + overflow: hidden; + width: 20px; + height: 20px; + margin: 10px auto; + cursor: pointer; +} + +.menu-btn-container .play-btn::before, .menu-btn-container .play-btn::after { + position: absolute; + top: 0; + z-index: -1; + width: 51%; + height: 100%; + background: #ffffff; + content: ""; + transition: .3s; + will-change: transform; +} + +.menu-btn-container .play-btn::before { + left: 0; +} + +.menu-btn-container .play-btn::after { + right: 0; +} + +.menu-btn-container .play-btn.paused::before { + -moz-transform: translateX(-25%); + -ms-transform: translateX(-25%); + -o-transform: translateX(-25%); + -webkit-transform: translateX(-25%); + transform: translateX(-25%); +} + +.menu-btn-container .play-btn.paused::after { + transform: translateX(25%) +} + +.menu-btn-container .play-btn.paused .pause-btn::before, .menu-btn-container .play-btn.paused .pause-btn::after { + transform: rotate(0); +} + +.menu-btn-container .play-btn .pause-btn::before, .menu-btn-container .play-btn .pause-btn::after { + position: absolute; + width: 150%; + height: 100%; + background: #333; + outline: 1px solid transparent; + content: ""; + transition: .3s; +} + +.menu-btn-container .play-btn .pause-btn::before { + top: -100%; + transform: rotate(26.5deg); + transform-origin: 0 100%; +} + +.menu-btn-container .play-btn .pause-btn::after { + top: 100%; + transform: rotate(-26.5deg); + transform-origin: 0 0; +} + +.game-container { + position: relative; +} + +#canvas { + display: block; + margin: 20px auto; + border-radius: 10px; + background: rgba(255, 255, 255, .2); +} + +.game-container .game-overlay { + position: absolute; + top: 0; + left: 0; + transform: translateY(100%); + width: 100%; + height: 100%; + border-radius: 10px; + background: #555; + opacity: 0; + pointer-events: none; + transition: .3s; +} + +.game-container .game-overlay.shown { + transform: none; + opacity: 1; + pointer-events: all; +} + +.game-container .game-overlay .game-overlay-title { + margin: 45% 25px 50px; + font-size: 25px; + text-align: center; +} + +.game-container .game-overlay .game-overlay-btns { + display: block; + width: 50%; + margin: 25px auto; +} + +.game-container .game-overlay .game-overlay-btns .btn { + display: block; + width: 100%; + margin: 16px 0 0; +} diff --git a/temp.js b/temp.js new file mode 100644 index 0000000..e321116 --- /dev/null +++ b/temp.js @@ -0,0 +1,205 @@ +// const canvas = document.getElementById('canvas'); +// const ctx = canvas.getContext('2d'); +// +// let width = 500, +// height = 800; +// +// const rectSize = width / 7, +// ballRadius = 12, +// powerUpRadius = 15, +// ballVelocity = 15, +// frameRate = 60; +// +// const sounds = { +// }; +// +// let roundNumber = 0; +// +// const rects = []; +// const powerUps = []; +// let balls = []; +// let ballCount = 1; +// let remainingBalls; +// let newBalls = 0; +// let originX; +// let newOriginX = 250; +// const originY = 9 * rectSize; +// let isMoving = false; +// +// let coinAngle = 0; +// +// let mousePos = null; +// +// function drawShootingLine() { +// const dx = mousePos.x - originX; +// const dy = originY - mousePos.y; +// let angle = Math.atan(dy / dx); +// let curX = originX; +// let curY = originY; +// +// if (dx < 0) +// angle += Math.PI; +// +// angle = Math.min(Math.max(angle, 0.05), Math.PI - 0.05); +// +// ctx.fillStyle = 'rgba(255, 255, 255, .5)'; +// for (let i = 0; i < 10; i++) { +// ctx.beginPath(); +// ctx.arc(curX, curY, ballRadius, 0, 2 * Math.PI); +// ctx.fill(); +// +// curX += ballRadius * 4 * Math.cos(angle); +// curY -= ballRadius * 4 * Math.sin(angle); +// } +// } +// +// function update() { +// balls.forEach(ball => { +// ball.update(10); +// }); +// +// coinAngle += .05; +// +// requestAnimationFrame(update); +// } +// +// function draw() { +// ctx.clearRect(0, 0, canvas.width, canvas.height); +// +// ctx.fillStyle = 'rgba(0,0,0,.15)'; +// ctx.fillRect(0, originY + ballRadius, width, height - originX - ballRadius); +// +// rects.forEach(rect => rect.draw()); +// +// powerUps.forEach(powerUp => powerUp.draw()); +// +// if (originX !== null) { +// if (!isMoving && mousePos !== null) { +// drawShootingLine(); +// } +// +// ctx.fillStyle = '#fff'; +// ctx.beginPath(); +// ctx.arc(originX, originY, ballRadius, 0, 2 * Math.PI); +// ctx.fill(); +// +// if (remainingBalls > 0) { +// ctx.fillText(`${remainingBalls}x`, originX, originY + 50); +// } else { +// originX = null; +// } +// } +// if(newOriginX !== null) { +// ctx.fillStyle = '#fff'; +// ctx.beginPath(); +// ctx.arc(newOriginX, originY, ballRadius, 0, 2 * Math.PI); +// ctx.fill(); +// } +// +// balls.forEach(ball => { +// ball.draw(); +// }); +// +// requestAnimationFrame(draw); +// } +// +// onRoundEnd(); +// update(); +// draw(); +// +// canvas.addEventListener('mousemove', (e) => { +// mousePos = cbRelMousePos(e); +// }); +// +// canvas.addEventListener('click', (e) => { +// mousePos = cbRelMousePos(e); +// shoot(); +// }); +// +// async function shoot() { +// if (isMoving) +// return; +// +// if (mousePos.y >= originY) +// return; +// +// isMoving = true; +// const dx = mousePos.x - originX, +// dy = originY - mousePos.y; +// let dir = Math.atan(dy / dx); +// if (dx < 0) { +// dir += Math.PI; +// } +// +// dir = Math.min(Math.max(dir, 0.05), Math.PI - 0.05); +// +// for (let i = 0; i < ballCount; i++) { +// balls.push(new Ball(originX, originY, ballVelocity, dir)); +// remainingBalls--; +// await sleep(100); +// } +// } +// +// function onRoundEnd() { +// isMoving = false; +// roundNumber++; +// ballCount += newBalls; +// newBalls = 0; +// remainingBalls = ballCount; +// originX = newOriginX; +// newOriginX = null; +// +// rects.forEach(rect => { +// moveObject(rect, 0, rectSize, 500); +// +// if(rect.y + 2 * rectSize >= originY) { +// gameOver(); +// } +// }); +// +// powerUps.forEach(powerUp => { +// moveObject(powerUp, 0, rectSize, 500); +// +// if(powerUp.cy + rectSize >= originY) { +// setTimeout(() => { +// powerUp.remove(); +// }, 500); +// } +// }); +// +// const cols = width / rectSize; +// const spawnBall = Math.random() < .5; +// const ballPos = spawnBall ? Math.round(Math.random() * (width / rectSize - 1)) : null; +// const spawnCoin = Math.random() < .33; +// const coinPos = spawnCoin ? Math.round(Math.random() * (width / rectSize - 1)) : null; +// for (let i = 0; i < cols; i++) { +// if (spawnBall && i === ballPos) { +// const powerUp = new PowerUpBall((i + .5) * rectSize, .5 * rectSize); +// powerUps.push(powerUp); +// +// moveObject(powerUp, 0, rectSize, 500); +// } else if (spawnCoin && i === coinPos) { +// const powerUp = new PowerUpCoin((i + .5) * rectSize, .5 * rectSize); +// powerUps.push(powerUp); +// +// moveObject(powerUp, 0, rectSize, 500); +// } else { +// if (Math.random() < .6) { +// const number = Math.random() < .5 ? roundNumber : 2 * roundNumber; +// const rect = new Rect(i * rectSize, 0, number); +// rects.push(rect); +// +// moveObject(rect, 0, rectSize, 500); +// } +// } +// } +// +// sounds.roundOver.play(); +// } +// +// +// function gameOver() { +// } +// +// +