Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Space Invaders - Arcade Edition</title> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap'); | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| background: #000; | |
| color: #0f0; | |
| font-family: 'Orbitron', monospace; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| overflow: hidden; | |
| } | |
| .arcade-container { | |
| background: #111; | |
| border: 4px solid #333; | |
| border-radius: 10px; | |
| padding: 20px; | |
| box-shadow: 0 0 30px rgba(0, 255, 0, 0.3); | |
| } | |
| .game-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 15px; | |
| padding: 10px; | |
| background: #222; | |
| border: 2px solid #444; | |
| border-radius: 5px; | |
| } | |
| .score-display, .lives-display, .level-display { | |
| font-size: 18px; | |
| font-weight: 700; | |
| text-shadow: 0 0 10px currentColor; | |
| } | |
| .score-display { | |
| color: #0ff; | |
| } | |
| .lives-display { | |
| color: #f0f; | |
| } | |
| .level-display { | |
| color: #ff0; | |
| } | |
| #gameCanvas { | |
| border: 3px solid #0f0; | |
| background: #000; | |
| display: block; | |
| image-rendering: pixelated; | |
| image-rendering: -moz-crisp-edges; | |
| image-rendering: crisp-edges; | |
| } | |
| .controls { | |
| margin-top: 15px; | |
| text-align: center; | |
| font-size: 14px; | |
| color: #888; | |
| } | |
| .game-over { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| background: rgba(0, 0, 0, 0.9); | |
| border: 3px solid #f00; | |
| border-radius: 10px; | |
| padding: 30px; | |
| text-align: center; | |
| display: none; | |
| z-index: 1000; | |
| } | |
| .game-over h2 { | |
| color: #f00; | |
| font-size: 32px; | |
| margin-bottom: 20px; | |
| text-shadow: 0 0 20px currentColor; | |
| } | |
| .game-over p { | |
| color: #0f0; | |
| font-size: 20px; | |
| margin-bottom: 20px; | |
| } | |
| .restart-btn { | |
| background: #0f0; | |
| color: #000; | |
| border: none; | |
| padding: 15px 30px; | |
| font-family: 'Orbitron', monospace; | |
| font-size: 18px; | |
| font-weight: 700; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .restart-btn:hover { | |
| background: #0ff; | |
| box-shadow: 0 0 20px rgba(0, 255, 255, 0.5); | |
| } | |
| .start-screen { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| background: rgba(0, 0, 0, 0.95); | |
| border: 3px solid #0f0; | |
| border-radius: 10px; | |
| padding: 40px; | |
| text-align: center; | |
| z-index: 1000; | |
| } | |
| .start-screen h1 { | |
| color: #0f0; | |
| font-size: 36px; | |
| margin-bottom: 20px; | |
| text-shadow: 0 0 30px currentColor; | |
| } | |
| .start-screen p { | |
| color: #0ff; | |
| font-size: 16px; | |
| margin-bottom: 30px; | |
| line-height: 1.6; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="arcade-container"> | |
| <div class="game-header"> | |
| <div class="score-display">SCORE: <span id="score">0</span></div> | |
| <div class="level-display">LEVEL: <span id="level">1</span></div> | |
| <div class="lives-display">LIVES: <span id="lives">3</span></div> | |
| </div> | |
| <canvas id="gameCanvas" width="800" height="600"></canvas> | |
| <div class="controls"> | |
| Use ← → ARROWS to move • SPACEBAR to shoot • ENTER to start/pause | |
| </div> | |
| </div> | |
| <div class="start-screen" id="startScreen"> | |
| <h1>SPACE INVADERS</h1> | |
| <p> | |
| Defend Earth from alien invasion!<br> | |
| Destroy all invaders before they reach you.<br> | |
| Each level gets faster and more challenging. | |
| </p> | |
| <button class="restart-btn" onclick="startGame()">START GAME</button> | |
| </div> | |
| <div class="game-over" id="gameOverScreen"> | |
| <h2>GAME OVER</h2> | |
| <p>Final Score: <span id="finalScore">0</span></p> | |
| <button class="restart-btn" onclick="restartGame()">PLAY AGAIN</button> | |
| </div> | |
| <script> | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| // Game state | |
| let gameRunning = false; | |
| let gamePaused = false; | |
| let score = 0; | |
| let lives = 3; | |
| let level = 1; | |
| let invaderDirection = 1; | |
| let invaderSpeed = 0.5; | |
| let lastTime = 0; | |
| // Game objects | |
| const player = { | |
| x: canvas.width / 2 - 25, | |
| y: canvas.height - 60, | |
| width: 50, | |
| height: 30, | |
| speed: 5 | |
| }; | |
| const bullets = []; | |
| const invaderBullets = []; | |
| const invaders = []; | |
| const particles = []; | |
| // Input handling | |
| const keys = {}; | |
| document.addEventListener('keydown', (e) => { | |
| keys[e.code] = true; | |
| if (e.code === 'Enter') { | |
| if (!gameRunning) startGame(); | |
| else gamePaused = !gamePaused; | |
| } | |
| }); | |
| document.addEventListener('keyup', (e) => { | |
| keys[e.code] = false; | |
| }); | |
| // Initialize invaders | |
| function createInvaders() { | |
| invaders.length = 0; | |
| const rows = 5; | |
| const cols = 11; | |
| const invaderWidth = 40; | |
| const invaderHeight = 30; | |
| const spacing = 15; | |
| const startX = 50; | |
| const startY = 50; | |
| for (let row = 0; row < rows; row++) { | |
| for (let col = 0; col < cols; col++) { | |
| invaders.push({ | |
| x: startX + col * (invaderWidth + spacing), | |
| y: startY + row * (invaderHeight + spacing), | |
| width: invaderWidth, | |
| height: invaderHeight, | |
| type: row < 2 ? 'strong' : 'normal', | |
| points: row < 2 ? 20 : 10 | |
| }); | |
| } | |
| } | |
| } | |
| // Create particle effect | |
| function createParticles(x, y, color) { | |
| for (let i = 0; i < 8; i++) { | |
| particles.push({ | |
| x: x, | |
| y: y, | |
| vx: (Math.random() - 0.5) * 6, | |
| vy: (Math.random() - 0.5) * 6, | |
| life: 30, | |
| color: color | |
| }); | |
| } | |
| } | |
| // Draw player | |
| function drawPlayer() { | |
| ctx.fillStyle = '#0f0'; | |
| // Player ship design | |
| ctx.fillRect(player.x + 20, player.y, 10, 20); | |
| ctx.fillRect(player.x + 10, player.y + 10, 30, 15); | |
| ctx.fillRect(player.x, player.y + 20, 50, 10); | |
| // Add glow effect | |
| ctx.shadowColor = '#0f0'; | |
| ctx.shadowBlur = 10; | |
| ctx.fillRect(player.x + 20, player.y + 5, 10, 5); | |
| ctx.shadowBlur = 0; | |
| } | |
| // Draw invader | |
| function drawInvader(invader) { | |
| ctx.fillStyle = invader.type === 'strong' ? '#f0f' : '#0ff'; | |
| // Simple invader design | |
| const centerX = invader.x + invader.width / 2; | |
| const centerY = invader.y + invader.height / 2; | |
| // Body | |
| ctx.fillRect(invader.x + 5, invader.y + 5, invader.width - 10, invader.height - 10); | |
| // Eyes | |
| ctx.fillStyle = '#000'; | |
| ctx.fillRect(invader.x + 10, invader.y + 10, 8, 8); | |
| ctx.fillRect(invader.x + invader.width - 18, invader.y + 10, 8, 8); | |
| // Antennas | |
| ctx.strokeStyle = invader.type === 'strong' ? '#f0f' : '#0ff'; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| ctx.moveTo(invader.x + 10, invader.y); | |
| ctx.lineTo(invader.x + 5, invader.y - 5); | |
| ctx.moveTo(invader.x + invader.width - 10, invader.y); | |
| ctx.lineTo(invader.x + invader.width - 5, invader.y - 5); | |
| ctx.stroke(); | |
| } | |
| // Draw bullet | |
| function drawBullet(bullet, isPlayer = true) { | |
| ctx.fillStyle = isPlayer ? '#ff0' : '#f00'; | |
| ctx.shadowColor = isPlayer ? '#ff0' : '#f00'; | |
| ctx.shadowBlur = 5; | |
| ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height); | |
| ctx.shadowBlur = 0; | |
| } | |
| // Draw particles | |
| function drawParticles() { | |
| particles.forEach((particle, index) => { | |
| if (particle.life <= 0) { | |
| particles.splice(index, 1); | |
| return; | |
| } | |
| ctx.fillStyle = particle.color; | |
| ctx.globalAlpha = particle.life / 30; | |
| ctx.fillRect(particle.x, particle.y, 3, 3); | |
| ctx.globalAlpha = 1; | |
| particle.x += particle.vx; | |
| particle.y += particle.vy; | |
| particle.life--; | |
| }); | |
| } | |
| // Update game logic | |
| function update(deltaTime) { | |
| if (!gameRunning || gamePaused) return; | |
| // Player movement | |
| if (keys['ArrowLeft'] && player.x > 0) { | |
| player.x -= player.speed; | |
| } | |
| if (keys['ArrowRight'] && player.x < canvas.width - player.width) { | |
| player.x += player.speed; | |
| } | |
| // Player shooting | |
| if (keys['Space']) { | |
| if (bullets.length === 0 || bullets[bullets.length - 1].y < player.y - 50) { | |
| bullets.push({ | |
| x: player.x + player.width / 2 - 2, | |
| y: player.y, | |
| width: 4, | |
| height: 10, | |
| speed: 8 | |
| }); | |
| } | |
| } | |
| // Update bullets | |
| bullets.forEach((bullet, index) => { | |
| bullet.y -= bullet.speed; | |
| if (bullet.y < 0) { | |
| bullets.splice(index, 1); | |
| } | |
| }); | |
| // Update invader bullets | |
| invaderBullets.forEach((bullet, index) => { | |
| bullet.y += bullet.speed; | |
| if (bullet.y > canvas.height) { | |
| invaderBullets.splice(index, 1); | |
| } | |
| }); | |
| // Update invaders | |
| let shouldDrop = false; | |
| invaders.forEach(invader => { | |
| invader.x += invaderDirection * invaderSpeed; | |
| if (invader.x <= 0 || invader.x + invader.width >= canvas.width) { | |
| shouldDrop = true; | |
| } | |
| }); | |
| if (shouldDrop) { | |
| invaderDirection *= -1; | |
| invaders.forEach(invader => { | |
| invader.y += 20; | |
| }); | |
| } | |
| // Invader shooting | |
| if (Math.random() < 0.001 * level && invaders.length > 0) { | |
| const shooter = invaders[Math.floor(Math.random() * invaders.length)]; | |
| invaderBullets.push({ | |
| x: shooter.x + shooter.width / 2 - 2, | |
| y: shooter.y + shooter.height, | |
| width: 4, | |
| height: 8, | |
| speed: 3 | |
| }); | |
| } | |
| // Collision detection - bullets vs invaders | |
| bullets.forEach((bullet, bulletIndex) => { | |
| invaders.forEach((invader, invaderIndex) => { | |
| if (bullet.x < invader.x + invader.width && | |
| bullet.x + bullet.width > invader.x && | |
| bullet.y < invader.y + invader.height && | |
| bullet.y + bullet.height > invader.y) { | |
| createParticles(invader.x + invader.width / 2, invader.y + invader.height / 2, '#0ff'); | |
| bullets.splice(bulletIndex, 1); | |
| invaders.splice(invaderIndex, 1); | |
| score += invader.points; | |
| updateUI(); | |
| } | |
| }); | |
| }); | |
| // Collision detection - invader bullets vs player | |
| invaderBullets.forEach((bullet, index) => { | |
| if (bullet.x < player.x + player.width && | |
| bullet.x + bullet.width > player.x && | |
| bullet.y < player.y + player.height && | |
| bullet.y + bullet.height > player.y) { | |
| createParticles(player.x + player.width / 2, player.y + player.height / 2, '#0f0'); | |
| invaderBullets.splice(index, 1); | |
| lives--; | |
| updateUI(); | |
| if (lives <= 0) { | |
| gameOver(); | |
| } | |
| } | |
| }); | |
| // Check if invaders reached player | |
| invaders.forEach(invader => { | |
| if (invader.y + invader.height >= player.y) { | |
| gameOver(); | |
| } | |
| }); | |
| // Check if all invaders destroyed | |
| if (invaders.length === 0) { | |
| nextLevel(); | |
| } | |
| } | |
| // Render game | |
| function render() { | |
| // Clear canvas | |
| ctx.fillStyle = '#000'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Draw stars | |
| ctx.fillStyle = '#fff'; | |
| for (let i = 0; i < 50; i++) { | |
| const x = (i * 137.5) % canvas.width; | |
| const y = (i * 234.7) % canvas.height; | |
| ctx.fillRect(x, y, 1, 1); | |
| } | |
| // Draw game objects | |
| drawPlayer(); | |
| invaders.forEach(drawInvader); | |
| bullets.forEach(bullet => drawBullet(bullet, true)); | |
| invaderBullets.forEach(bullet => drawBullet(bullet, false)); | |
| drawParticles(); | |
| // Draw pause text | |
| if (gamePaused) { | |
| ctx.fillStyle = '#0f0'; | |
| ctx.font = '48px Orbitron'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText('PAUSED', canvas.width / 2, canvas.height / 2); | |
| ctx.textAlign = 'left'; | |
| } | |
| } | |
| // Game loop | |
| function gameLoop(currentTime) { | |
| const deltaTime = currentTime - lastTime; | |
| lastTime = currentTime; | |
| update(deltaTime); | |
| render(); | |
| requestAnimationFrame(gameLoop); | |
| } | |
| // Update UI | |
| function updateUI() { | |
| document.getElementById('score').textContent = score; | |
| document.getElementById('lives').textContent = lives; | |
| document.getElementById('level').textContent = level; | |
| } | |
| // Start game | |
| function startGame() { | |
| document.getElementById('startScreen').style.display = 'none'; | |
| document.getElementById('gameOverScreen').style.display = 'none'; | |
| gameRunning = true; | |
| gamePaused = false; | |
| score = 0; | |
| lives = 3; | |
| level = 1; | |
| invaderSpeed = 0.5; | |
| bullets.length = 0; | |
| invaderBullets.length = 0; | |
| particles.length = 0; | |
| player.x = canvas.width / 2 - 25; | |
| player.y = canvas.height - 60; | |
| createInvaders(); | |
| updateUI(); | |
| } | |
| // Next level | |
| function nextLevel() { | |
| level++; | |
| invaderSpeed += 0.2; | |
| createInvaders(); | |
| updateUI(); | |
| } | |
| // Game over | |
| function gameOver() { | |
| gameRunning = false; | |
| document.getElementById('finalScore').textContent = score; | |
| document.getElementById('gameOverScreen').style.display = 'block'; | |
| } | |
| // Restart game | |
| function restartGame() { | |
| startGame(); | |
| } | |
| // Initialize game | |
| updateUI(); | |
| requestAnimationFrame(gameLoop); | |
| </script> | |
| </body> | |
| </html> | |