Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Bricks vs Balls - Ultimate Brick Breaker</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| background: linear-gradient(135deg, #1e3c72, #2a5298); | |
| font-family: 'Arial', sans-serif; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| overflow: hidden; | |
| } | |
| .game-container { | |
| position: relative; | |
| background: rgba(0, 0, 0, 0.8); | |
| border-radius: 20px; | |
| padding: 20px; | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); | |
| backdrop-filter: blur(10px); | |
| } | |
| canvas { | |
| border: 3px solid #00ffff; | |
| border-radius: 15px; | |
| background: linear-gradient(180deg, #000428, #004e92); | |
| box-shadow: inset 0 0 50px rgba(0, 255, 255, 0.1); | |
| } | |
| .ui { | |
| position: absolute; | |
| top: 10px; | |
| left: 20px; | |
| color: #00ffff; | |
| font-size: 18px; | |
| font-weight: bold; | |
| text-shadow: 0 0 10px rgba(0, 255, 255, 0.5); | |
| } | |
| .controls { | |
| position: absolute; | |
| bottom: -50px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| color: #fff; | |
| text-align: center; | |
| font-size: 14px; | |
| opacity: 0.8; | |
| } | |
| .game-over { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| background: rgba(0, 0, 0, 0.9); | |
| color: #00ffff; | |
| padding: 30px; | |
| border-radius: 15px; | |
| text-align: center; | |
| border: 2px solid #00ffff; | |
| box-shadow: 0 0 30px rgba(0, 255, 255, 0.3); | |
| display: none; | |
| } | |
| .restart-btn { | |
| background: linear-gradient(45deg, #00ffff, #0080ff); | |
| border: none; | |
| color: #000; | |
| padding: 12px 25px; | |
| font-size: 16px; | |
| font-weight: bold; | |
| border-radius: 25px; | |
| cursor: pointer; | |
| margin-top: 15px; | |
| transition: all 0.3s ease; | |
| } | |
| .restart-btn:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 5px 15px rgba(0, 255, 255, 0.4); | |
| } | |
| .power-indicator { | |
| position: absolute; | |
| bottom: 20px; | |
| left: 20px; | |
| color: #ffff00; | |
| font-size: 14px; | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="game-container"> | |
| <div class="ui"> | |
| <div>Score: <span id="score">0</span></div> | |
| <div>Level: <span id="level">1</span></div> | |
| <div>Balls: <span id="balls">1</span></div> | |
| </div> | |
| <canvas id="gameCanvas" width="800" height="600"></canvas> | |
| <div class="power-indicator" id="powerIndicator"> | |
| Power: <span id="powerValue">0</span>% | |
| </div> | |
| <div class="controls"> | |
| Mouse: Aim & Shoot | Space: Quick Restart | Click: Fire Balls | |
| </div> | |
| <div class="game-over" id="gameOver"> | |
| <h2>Game Over!</h2> | |
| <p>Final Score: <span id="finalScore">0</span></p> | |
| <p>Level Reached: <span id="finalLevel">1</span></p> | |
| <button class="restart-btn" onclick="restartGame()">Play Again</button> | |
| </div> | |
| </div> | |
| <script> | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const scoreEl = document.getElementById('score'); | |
| const levelEl = document.getElementById('level'); | |
| const ballsEl = document.getElementById('balls'); | |
| const gameOverEl = document.getElementById('gameOver'); | |
| const powerIndicator = document.getElementById('powerIndicator'); | |
| const powerValue = document.getElementById('powerValue'); | |
| let gameState = { | |
| score: 0, | |
| level: 1, | |
| ballCount: 1, | |
| isAiming: false, | |
| isShooting: false, | |
| gameOver: false, | |
| mouseX: 0, | |
| mouseY: 0, | |
| launchPower: 0 | |
| }; | |
| let balls = []; | |
| let bricks = []; | |
| let powerUps = []; | |
| let particles = []; | |
| const BALL_RADIUS = 8; | |
| const BALL_SPEED = 8; | |
| const BRICK_WIDTH = 60; | |
| const BRICK_HEIGHT = 30; | |
| const BRICK_ROWS = 6; | |
| const BRICK_COLS = 12; | |
| class Ball { | |
| constructor(x, y, vx = 0, vy = 0) { | |
| this.x = x; | |
| this.y = y; | |
| this.vx = vx; | |
| this.vy = vy; | |
| this.radius = BALL_RADIUS; | |
| this.trail = []; | |
| this.active = true; | |
| } | |
| update() { | |
| if (!this.active) return; | |
| this.trail.push({x: this.x, y: this.y}); | |
| if (this.trail.length > 10) this.trail.shift(); | |
| this.x += this.vx; | |
| this.y += this.vy; | |
| // Wall collisions | |
| if (this.x <= this.radius || this.x >= canvas.width - this.radius) { | |
| this.vx = -this.vx; | |
| this.x = Math.max(this.radius, Math.min(canvas.width - this.radius, this.x)); | |
| createImpactParticles(this.x, this.y, '#00ffff'); | |
| } | |
| if (this.y <= this.radius) { | |
| this.vy = -this.vy; | |
| this.y = this.radius; | |
| createImpactParticles(this.x, this.y, '#00ffff'); | |
| } | |
| // Bottom boundary - ball returns to launch position | |
| if (this.y > canvas.height) { | |
| this.active = false; | |
| } | |
| // Brick collisions | |
| for (let i = bricks.length - 1; i >= 0; i--) { | |
| const brick = bricks[i]; | |
| if (this.collidesWith(brick)) { | |
| this.handleBrickCollision(brick); | |
| gameState.score += brick.hits * 10; | |
| brick.hits--; | |
| createImpactParticles(this.x, this.y, brick.color); | |
| if (brick.hits <= 0) { | |
| // Chance for power-up | |
| if (Math.random() < 0.1) { | |
| powerUps.push(new PowerUp(brick.x + brick.width/2, brick.y + brick.height/2)); | |
| } | |
| bricks.splice(i, 1); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| collidesWith(brick) { | |
| const closestX = Math.max(brick.x, Math.min(this.x, brick.x + brick.width)); | |
| const closestY = Math.max(brick.y, Math.min(this.y, brick.y + brick.height)); | |
| const distance = Math.sqrt((this.x - closestX) ** 2 + (this.y - closestY) ** 2); | |
| return distance < this.radius; | |
| } | |
| handleBrickCollision(brick) { | |
| const brickCenterX = brick.x + brick.width / 2; | |
| const brickCenterY = brick.y + brick.height / 2; | |
| const dx = this.x - brickCenterX; | |
| const dy = this.y - brickCenterY; | |
| if (Math.abs(dx) > Math.abs(dy)) { | |
| this.vx = -this.vx; | |
| } else { | |
| this.vy = -this.vy; | |
| } | |
| } | |
| draw() { | |
| if (!this.active) return; | |
| // Draw trail | |
| ctx.globalAlpha = 0.6; | |
| for (let i = 0; i < this.trail.length; i++) { | |
| const alpha = i / this.trail.length; | |
| const size = this.radius * alpha; | |
| ctx.beginPath(); | |
| ctx.arc(this.trail[i].x, this.trail[i].y, size, 0, Math.PI * 2); | |
| ctx.fillStyle = `rgba(0, 255, 255, ${alpha * 0.3})`; | |
| ctx.fill(); | |
| } | |
| ctx.globalAlpha = 1; | |
| // Draw ball with glow effect | |
| ctx.shadowColor = '#00ffff'; | |
| ctx.shadowBlur = 15; | |
| ctx.beginPath(); | |
| ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); | |
| const gradient = ctx.createRadialGradient(this.x-3, this.y-3, 0, this.x, this.y, this.radius); | |
| gradient.addColorStop(0, '#ffffff'); | |
| gradient.addColorStop(0.3, '#00ffff'); | |
| gradient.addColorStop(1, '#0080ff'); | |
| ctx.fillStyle = gradient; | |
| ctx.fill(); | |
| ctx.shadowBlur = 0; | |
| } | |
| } | |
| class Brick { | |
| constructor(x, y, hits) { | |
| this.x = x; | |
| this.y = y; | |
| this.width = BRICK_WIDTH; | |
| this.height = BRICK_HEIGHT; | |
| this.hits = hits; | |
| this.maxHits = hits; | |
| this.color = this.getColorByHits(hits); | |
| this.pulsePhase = Math.random() * Math.PI * 2; | |
| } | |
| getColorByHits(hits) { | |
| const colors = ['#ff4444', '#ff8844', '#ffaa44', '#ffff44', '#88ff44', '#44ff88']; | |
| return colors[Math.min(hits - 1, colors.length - 1)]; | |
| } | |
| update() { | |
| this.pulsePhase += 0.05; | |
| this.color = this.getColorByHits(this.hits); | |
| } | |
| draw() { | |
| const pulse = Math.sin(this.pulsePhase) * 0.1 + 0.9; | |
| ctx.shadowColor = this.color; | |
| ctx.shadowBlur = 10 * pulse; | |
| // Main brick | |
| ctx.fillStyle = this.color; | |
| ctx.fillRect(this.x + 2, this.y + 2, this.width - 4, this.height - 4); | |
| // Highlight | |
| const gradient = ctx.createLinearGradient(this.x, this.y, this.x, this.y + this.height); | |
| gradient.addColorStop(0, 'rgba(255, 255, 255, 0.3)'); | |
| gradient.addColorStop(1, 'rgba(255, 255, 255, 0)'); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(this.x + 2, this.y + 2, this.width - 4, this.height/2 - 2); | |
| // Hit counter | |
| ctx.shadowBlur = 0; | |
| ctx.fillStyle = '#000'; | |
| ctx.font = 'bold 14px Arial'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText(this.hits, this.x + this.width/2, this.y + this.height/2 + 5); | |
| } | |
| } | |
| class PowerUp { | |
| constructor(x, y) { | |
| this.x = x; | |
| this.y = y; | |
| this.radius = 15; | |
| this.vy = 2; | |
| this.type = Math.random() < 0.5 ? 'multi' : 'power'; | |
| this.rotation = 0; | |
| } | |
| update() { | |
| this.y += this.vy; | |
| this.rotation += 0.1; | |
| if (this.y > canvas.height) { | |
| return false; | |
| } | |
| // Check collision with launch area | |
| if (this.y > canvas.height - 50 && Math.abs(this.x - canvas.width/2) < 50) { | |
| this.activate(); | |
| return false; | |
| } | |
| return true; | |
| } | |
| activate() { | |
| if (this.type === 'multi') { | |
| gameState.ballCount++; | |
| } else { | |
| // Power boost - temporary effect | |
| gameState.score += 50; | |
| } | |
| createImpactParticles(this.x, this.y, '#ffff00'); | |
| } | |
| draw() { | |
| ctx.save(); | |
| ctx.translate(this.x, this.y); | |
| ctx.rotate(this.rotation); | |
| ctx.shadowColor = '#ffff00'; | |
| ctx.shadowBlur = 15; | |
| ctx.fillStyle = this.type === 'multi' ? '#ffff00' : '#ff8800'; | |
| ctx.beginPath(); | |
| ctx.arc(0, 0, this.radius, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.fillStyle = '#000'; | |
| ctx.font = 'bold 12px Arial'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText(this.type === 'multi' ? '+' : '⚡', 0, 4); | |
| ctx.restore(); | |
| } | |
| } | |
| class Particle { | |
| constructor(x, y, color) { | |
| this.x = x; | |
| this.y = y; | |
| this.vx = (Math.random() - 0.5) * 8; | |
| this.vy = (Math.random() - 0.5) * 8; | |
| this.life = 1; | |
| this.decay = 0.02; | |
| this.color = color; | |
| this.size = Math.random() * 4 + 2; | |
| } | |
| update() { | |
| this.x += this.vx; | |
| this.y += this.vy; | |
| this.life -= this.decay; | |
| this.size *= 0.98; | |
| return this.life > 0; | |
| } | |
| draw() { | |
| ctx.globalAlpha = this.life; | |
| ctx.fillStyle = this.color; | |
| ctx.beginPath(); | |
| ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.globalAlpha = 1; | |
| } | |
| } | |
| function createImpactParticles(x, y, color) { | |
| for (let i = 0; i < 8; i++) { | |
| particles.push(new Particle(x, y, color)); | |
| } | |
| } | |
| function initLevel() { | |
| bricks = []; | |
| const padding = 20; | |
| const totalWidth = BRICK_COLS * BRICK_WIDTH + (BRICK_COLS - 1) * 5; | |
| const startX = (canvas.width - totalWidth) / 2; | |
| for (let row = 0; row < BRICK_ROWS; row++) { | |
| for (let col = 0; col < BRICK_COLS; col++) { | |
| const x = startX + col * (BRICK_WIDTH + 5); | |
| const y = padding + row * (BRICK_HEIGHT + 5); | |
| const hits = Math.min(gameState.level + row, 6); | |
| bricks.push(new Brick(x, y, hits)); | |
| } | |
| } | |
| } | |
| function launchBalls() { | |
| if (gameState.isShooting || balls.some(b => b.active)) return; | |
| const launchX = canvas.width / 2; | |
| const launchY = canvas.height - 30; | |
| const angle = Math.atan2(gameState.mouseY - launchY, gameState.mouseX - launchX); | |
| const speed = BALL_SPEED; | |
| gameState.isShooting = true; | |
| for (let i = 0; i < gameState.ballCount; i++) { | |
| setTimeout(() => { | |
| const vx = Math.cos(angle) * speed; | |
| const vy = Math.sin(angle) * speed; | |
| balls.push(new Ball(launchX, launchY, vx, vy)); | |
| }, i * 100); | |
| } | |
| } | |
| function updateGame() { | |
| if (gameState.gameOver) return; | |
| // Update balls | |
| balls = balls.filter(ball => { | |
| ball.update(); | |
| return ball.active; | |
| }); | |
| // Update bricks | |
| bricks.forEach(brick => brick.update()); | |
| // Update power-ups | |
| powerUps = powerUps.filter(powerUp => powerUp.update()); | |
| // Update particles | |
| particles = particles.filter(particle => particle.update()); | |
| // Check if all balls are done and ready for next shot | |
| if (gameState.isShooting && balls.length === 0) { | |
| gameState.isShooting = false; | |
| } | |
| // Check level completion | |
| if (bricks.length === 0) { | |
| gameState.level++; | |
| gameState.ballCount++; | |
| initLevel(); | |
| } | |
| // Check game over (simplified) | |
| if (gameState.score < 0) { | |
| gameState.gameOver = true; | |
| showGameOver(); | |
| } | |
| updateUI(); | |
| } | |
| function drawGame() { | |
| // Clear canvas with gradient | |
| const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height); | |
| gradient.addColorStop(0, '#000428'); | |
| gradient.addColorStop(1, '#004e92'); | |
| ctx.fillStyle = gradient; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Draw aiming line | |
| if (gameState.isAiming && !gameState.isShooting) { | |
| drawAimingLine(); | |
| } | |
| // Draw game objects | |
| bricks.forEach(brick => brick.draw()); | |
| balls.forEach(ball => ball.draw()); | |
| powerUps.forEach(powerUp => powerUp.draw()); | |
| particles.forEach(particle => particle.draw()); | |
| // Draw launch position | |
| if (!gameState.isShooting && balls.length === 0) { | |
| ctx.shadowColor = '#00ffff'; | |
| ctx.shadowBlur = 10; | |
| ctx.fillStyle = '#00ffff'; | |
| ctx.beginPath(); | |
| ctx.arc(canvas.width / 2, canvas.height - 30, 10, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.shadowBlur = 0; | |
| } | |
| } | |
| function drawAimingLine() { | |
| const launchX = canvas.width / 2; | |
| const launchY = canvas.height - 30; | |
| ctx.setLineDash([5, 5]); | |
| ctx.strokeStyle = '#00ffff'; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| ctx.moveTo(launchX, launchY); | |
| ctx.lineTo(gameState.mouseX, gameState.mouseY); | |
| ctx.stroke(); | |
| ctx.setLineDash([]); | |
| // Show power indicator | |
| const distance = Math.sqrt((gameState.mouseX - launchX) ** 2 + (gameState.mouseY - launchY) ** 2); | |
| const power = Math.min(distance / 200, 1) * 100; | |
| powerValue.textContent = Math.round(power); | |
| powerIndicator.style.opacity = '1'; | |
| } | |
| function updateUI() { | |
| scoreEl.textContent = gameState.score; | |
| levelEl.textContent = gameState.level; | |
| ballsEl.textContent = gameState.ballCount; | |
| } | |
| function showGameOver() { | |
| document.getElementById('finalScore').textContent = gameState.score; | |
| document.getElementById('finalLevel').textContent = gameState.level; | |
| gameOverEl.style.display = 'block'; | |
| } | |
| function restartGame() { | |
| gameState = { | |
| score: 0, | |
| level: 1, | |
| ballCount: 1, | |
| isAiming: false, | |
| isShooting: false, | |
| gameOver: false, | |
| mouseX: 0, | |
| mouseY: 0, | |
| launchPower: 0 | |
| }; | |
| balls = []; | |
| bricks = []; | |
| powerUps = []; | |
| particles = []; | |
| gameOverEl.style.display = 'none'; | |
| powerIndicator.style.opacity = '0'; | |
| initLevel(); | |
| updateUI(); | |
| } | |
| // Event listeners | |
| canvas.addEventListener('mousemove', (e) => { | |
| const rect = canvas.getBoundingClientRect(); | |
| gameState.mouseX = e.clientX - rect.left; | |
| gameState.mouseY = e.clientY - rect.top; | |
| gameState.isAiming = !gameState.isShooting && balls.length === 0; | |
| }); | |
| canvas.addEventListener('mouseleave', () => { | |
| gameState.isAiming = false; | |
| powerIndicator.style.opacity = '0'; | |
| }); | |
| canvas.addEventListener('click', () => { | |
| if (!gameState.gameOver && !gameState.isShooting && balls.length === 0) { | |
| launchBalls(); | |
| powerIndicator.style.opacity = '0'; | |
| } | |
| }); | |
| document.addEventListener('keydown', (e) => { | |
| if (e.code === 'Space') { | |
| e.preventDefault(); | |
| if (gameState.gameOver) { | |
| restartGame(); | |
| } else if (!gameState.isShooting && balls.length === 0) { | |
| launchBalls(); | |
| } | |
| } | |
| }); | |
| // Game loop | |
| function gameLoop() { | |
| updateGame(); | |
| drawGame(); | |
| requestAnimationFrame(gameLoop); | |
| } | |
| // Initialize game | |
| initLevel(); | |
| updateUI(); | |
| gameLoop(); | |
| </script> | |
| </body> | |
| </html> |