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 - Transparent Sprites</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| padding: 20px; | |
| background: #000; | |
| font-family: 'Courier New', monospace; | |
| color: #00ff00; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| user-select: none; | |
| } | |
| .title { | |
| margin-bottom: 20px; | |
| text-shadow: 0 0 10px #00ff00; | |
| } | |
| #gameCanvas { | |
| border: 3px solid #00ff00; | |
| box-shadow: 0 0 20px rgba(0, 255, 0, 0.3); | |
| background: #000; | |
| } | |
| .hud { | |
| margin: 15px 0; | |
| font-size: 20px; | |
| font-weight: bold; | |
| text-shadow: 0 0 5px #00ff00; | |
| } | |
| .controls { | |
| margin: 10px 0; | |
| color: #00cc00; | |
| font-size: 14px; | |
| } | |
| .game-btn { | |
| margin: 15px 0; | |
| padding: 12px 24px; | |
| background: transparent; | |
| border: 2px solid #00ff00; | |
| color: #00ff00; | |
| font-family: 'Courier New', monospace; | |
| cursor: pointer; | |
| font-size: 16px; | |
| transition: all 0.3s; | |
| } | |
| .game-btn:hover { | |
| background: #00ff00; | |
| color: #000; | |
| box-shadow: 0 0 15px #00ff00; | |
| } | |
| .status { | |
| margin: 10px 0; | |
| font-size: 14px; | |
| color: #008800; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1 class="title">๐ SPACE INVADERS - TRANSPARENT SPRITES ๐</h1> | |
| <div class="hud"> | |
| SCORE: <span id="score">000000</span> | | |
| LIVES: <span id="lives">3</span> | | |
| LEVEL: <span id="level">1</span> | |
| </div> | |
| <canvas id="gameCanvas" width="800" height="600"></canvas> | |
| <div class="controls"> | |
| ๐ฎ MOVE: Arrow Keys or A/D | ๐ SHOOT: SPACEBAR | |
| </div> | |
| <button class="game-btn" id="startBtn" onclick="startGame()">START GAME</button> | |
| <button class="game-btn hidden" id="restartBtn" onclick="restartGame()">RESTART</button> | |
| <div class="status" id="status">Loading transparent sprites...</div> | |
| <script> | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| let gameState = 'menu'; | |
| let score = 0; | |
| let lives = 3; | |
| let level = 1; | |
| // Game objects | |
| let player = { | |
| x: 380, | |
| y: 550, | |
| width: 40, | |
| height: 30, | |
| speed: 6, | |
| lastShot: 0, | |
| shootDelay: 250 | |
| }; | |
| let invaders = []; | |
| let bullets = []; | |
| // Your transparent sprites | |
| let sprites = { | |
| loaded: false, | |
| ship: null, | |
| invader: null, | |
| bullet: null | |
| }; | |
| // Game settings | |
| const INVADER_ROWS = 5; | |
| const INVADER_COLS = 11; | |
| const INVADER_WIDTH = 35; | |
| const INVADER_HEIGHT = 25; | |
| // Movement | |
| let invaderDirection = 1; | |
| let invaderSpeed = 1; | |
| let invaderDropDistance = 20; | |
| // Input | |
| const keys = {}; | |
| // Setup | |
| function init() { | |
| updateStatus('Loading transparent sprites...'); | |
| loadTransparentSprites(); | |
| createInvaders(); | |
| drawInitialScreen(); | |
| // Keyboard events | |
| document.addEventListener('keydown', (e) => { | |
| keys[e.code] = true; | |
| if (e.code === 'Space') { | |
| e.preventDefault(); | |
| if (gameState === 'playing') { | |
| shoot(); | |
| } | |
| } | |
| }); | |
| document.addEventListener('keyup', (e) => { | |
| keys[e.code] = false; | |
| }); | |
| } | |
| function loadTransparentSprites() { | |
| console.log('Loading your transparent sprites...'); | |
| // Use your transparent sprites | |
| const spriteFiles = { | |
| ship: 'assets/ship-transparent.png', | |
| invader: 'assets/invader-transparent.png', | |
| bullet: 'assets/bullet-transparent.png' | |
| }; | |
| let loadedCount = 0; | |
| const totalSprites = Object.keys(spriteFiles).length; | |
| Object.keys(spriteFiles).forEach(key => { | |
| const img = new Image(); | |
| img.onload = () => { | |
| console.log(`โ Loaded transparent ${key} sprite: ${img.width}x${img.height}px`); | |
| sprites[key] = img; | |
| loadedCount++; | |
| if (loadedCount === totalSprites) { | |
| sprites.loaded = true; | |
| updateStatus('โ All transparent sprites loaded! Game ready!'); | |
| console.log('All transparent sprites loaded successfully!'); | |
| } | |
| }; | |
| img.onerror = () => { | |
| console.error(`โ Failed to load transparent ${key} sprite`); | |
| updateStatus(`โ Failed to load ${key} sprite - using fallback shapes`); | |
| }; | |
| img.src = spriteFiles[key]; | |
| }); | |
| } | |
| function drawInitialScreen() { | |
| // Clear | |
| ctx.fillStyle = '#000'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Draw stars | |
| ctx.fillStyle = '#003300'; | |
| for (let i = 0; i < 50; i++) { | |
| const x = (i * 137) % canvas.width; | |
| const y = (i * 173) % canvas.height; | |
| ctx.fillRect(x, y, 2, 2); | |
| } | |
| // Draw title text | |
| ctx.fillStyle = '#00ff00'; | |
| ctx.font = '32px Courier New'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText('SPACE INVADERS - TRANSPARENT SPRITES', canvas.width/2, canvas.height/2 - 50); | |
| ctx.font = '20px Courier New'; | |
| ctx.fillText('Click START GAME to begin!', canvas.width/2, canvas.height/2 + 20); | |
| ctx.fillText('Use ARROW KEYS + SPACEBAR', canvas.width/2, canvas.height/2 + 50); | |
| // Test sprite display | |
| if (sprites.loaded) { | |
| ctx.drawImage(sprites.ship, canvas.width/2 - 20, canvas.height/2 + 80, 40, 30); | |
| ctx.drawImage(sprites.invader, canvas.width/2 - 17, canvas.height/2 + 120, 35, 25); | |
| } | |
| } | |
| function createInvaders() { | |
| invaders = []; | |
| const startX = 50; | |
| const startY = 80; | |
| const spacingX = 60; | |
| const spacingY = 40; | |
| for (let row = 0; row < INVADER_ROWS; row++) { | |
| for (let col = 0; col < INVADER_COLS; col++) { | |
| invaders.push({ | |
| x: startX + col * spacingX, | |
| y: startY + row * spacingY, | |
| width: INVADER_WIDTH, | |
| height: INVADER_HEIGHT, | |
| alive: true, | |
| row: row, | |
| col: col | |
| }); | |
| } | |
| } | |
| } | |
| function startGame() { | |
| gameState = 'playing'; | |
| score = 0; | |
| lives = 3; | |
| level = 1; | |
| player.x = 380; | |
| createInvaders(); | |
| bullets = []; | |
| invaderDirection = 1; | |
| invaderSpeed = 1; | |
| document.getElementById('startBtn').classList.add('hidden'); | |
| document.getElementById('restartBtn').classList.add('hidden'); | |
| updateStatus('Game started! Destroy all invaders!'); | |
| gameLoop(); | |
| } | |
| function restartGame() { | |
| gameState = 'menu'; | |
| document.getElementById('startBtn').classList.remove('hidden'); | |
| document.getElementById('restartBtn').classList.add('hidden'); | |
| drawInitialScreen(); | |
| updateStatus('Game ready! Click START GAME'); | |
| } | |
| function shoot() { | |
| const now = Date.now(); | |
| if (now - player.lastShot < player.shootDelay) return; | |
| player.lastShot = now; | |
| bullets.push({ | |
| x: player.x + player.width/2 - 2, | |
| y: player.y, | |
| width: 4, | |
| height: 10 | |
| }); | |
| } | |
| function update() { | |
| if (gameState !== 'playing') return; | |
| // Player movement | |
| if (keys['ArrowLeft'] || keys['KeyA']) { | |
| player.x = Math.max(0, player.x - player.speed); | |
| } | |
| if (keys['ArrowRight'] || keys['KeyD']) { | |
| player.x = Math.min(canvas.width - player.width, player.x + player.speed); | |
| } | |
| // Update bullets | |
| bullets = bullets.filter(bullet => { | |
| bullet.y -= 8; | |
| return bullet.y > -20; | |
| }); | |
| // Update invaders | |
| updateInvaders(); | |
| // Check collisions | |
| checkCollisions(); | |
| // Check win/lose conditions | |
| checkGameConditions(); | |
| } | |
| function updateInvaders() { | |
| const aliveInvaders = invaders.filter(inv => inv.alive); | |
| if (aliveInvaders.length === 0) return; | |
| const rightmost = Math.max(...aliveInvaders.map(inv => inv.x + inv.width)); | |
| const leftmost = Math.min(...aliveInvaders.map(inv => inv.x)); | |
| let shouldDrop = false; | |
| if ((rightmost >= canvas.width - 50 && invaderDirection > 0) || | |
| (leftmost <= 50 && invaderDirection < 0)) { | |
| shouldDrop = true; | |
| } | |
| if (shouldDrop) { | |
| invaderDirection *= -1; | |
| aliveInvaders.forEach(invader => { | |
| invader.y += invaderDropDistance; | |
| }); | |
| invaderSpeed += 0.1; | |
| } else { | |
| aliveInvaders.forEach(invader => { | |
| invader.x += invaderSpeed * invaderDirection; | |
| }); | |
| } | |
| } | |
| function checkCollisions() { | |
| bullets.forEach((bullet, bulletIndex) => { | |
| invaders.forEach(invader => { | |
| if (invader.alive && | |
| bullet.x < invader.x + invader.width && | |
| bullet.x + bullet.width > invader.x && | |
| bullet.y < invader.y + invader.height && | |
| bullet.y + bullet.height > invader.y) { | |
| invader.alive = false; | |
| bullets.splice(bulletIndex, 1); | |
| score += 10 * level; | |
| updateHUD(); | |
| createExplosion(invader.x + invader.width/2, invader.y + invader.height/2); | |
| } | |
| }); | |
| }); | |
| } | |
| function createExplosion(x, y) { | |
| ctx.save(); | |
| ctx.fillStyle = '#ff6600'; | |
| ctx.beginPath(); | |
| ctx.arc(x, y, 15, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.fillStyle = '#ffff00'; | |
| ctx.beginPath(); | |
| ctx.arc(x, y, 8, 0, Math.PI * 2); | |
| ctx.fill(); | |
| ctx.restore(); | |
| } | |
| function checkGameConditions() { | |
| const aliveInvaders = invaders.filter(inv => inv.alive); | |
| if (aliveInvaders.length === 0) { | |
| levelComplete(); | |
| return; | |
| } | |
| const bottomInvader = Math.max(...aliveInvaders.map(inv => inv.y)); | |
| if (bottomInvader >= canvas.height - 100) { | |
| gameOver(); | |
| return; | |
| } | |
| } | |
| function levelComplete() { | |
| updateStatus(`Level ${level} complete!`); | |
| level++; | |
| createInvaders(); | |
| updateHUD(); | |
| } | |
| function gameOver() { | |
| gameState = 'gameover'; | |
| updateStatus(`GAME OVER! Final Score: ${score}`); | |
| document.getElementById('restartBtn').classList.remove('hidden'); | |
| } | |
| function draw() { | |
| ctx.fillStyle = '#000'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| drawStars(); | |
| if (gameState === 'playing') { | |
| drawPlayer(); | |
| drawInvaders(); | |
| drawBullets(); | |
| } | |
| } | |
| function drawStars() { | |
| ctx.fillStyle = '#003300'; | |
| for (let i = 0; i < 30; i++) { | |
| const x = (i * 47) % canvas.width; | |
| const y = (i * 83) % canvas.height; | |
| ctx.fillRect(x, y, 1, 1); | |
| } | |
| } | |
| function drawPlayer() { | |
| if (sprites.loaded && sprites.ship) { | |
| ctx.drawImage(sprites.ship, player.x, player.y, player.width, player.height); | |
| } else { | |
| // Fallback to red triangle | |
| ctx.fillStyle = '#ff0000'; | |
| ctx.beginPath(); | |
| ctx.moveTo(player.x + player.width/2, player.y); | |
| ctx.lineTo(player.x, player.y + player.height); | |
| ctx.lineTo(player.x + player.width, player.y + player.height); | |
| ctx.closePath(); | |
| ctx.fill(); | |
| } | |
| } | |
| function drawInvaders() { | |
| invaders.forEach(invader => { | |
| if (invader.alive) { | |
| if (sprites.loaded && sprites.invader) { | |
| ctx.drawImage(sprites.invader, invader.x, invader.y, invader.width, invader.height); | |
| } else { | |
| // Fallback to green rectangle | |
| ctx.fillStyle = '#00ff00'; | |
| ctx.fillRect(invader.x, invader.y, invader.width, invader.height); | |
| ctx.fillStyle = '#008800'; | |
| ctx.fillRect(invader.x + 5, invader.y + 5, invader.width - 10, invader.height - 10); | |
| } | |
| } | |
| }); | |
| } | |
| function drawBullets() { | |
| if (sprites.loaded && sprites.bullet) { | |
| bullets.forEach(bullet => { | |
| ctx.drawImage(sprites.bullet, bullet.x, bullet.y, bullet.width, bullet.height); | |
| }); | |
| } else { | |
| // Fallback to yellow rectangles | |
| ctx.fillStyle = '#ffff00'; | |
| bullets.forEach(bullet => { | |
| ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height); | |
| }); | |
| } | |
| } | |
| function updateHUD() { | |
| document.getElementById('score').textContent = score.toString().padStart(6, '0'); | |
| document.getElementById('lives').textContent = lives; | |
| document.getElementById('level').textContent = level; | |
| } | |
| function updateStatus(message) { | |
| document.getElementById('status').textContent = message; | |
| console.log(message); | |
| } | |
| function gameLoop() { | |
| if (gameState !== 'playing') return; | |
| update(); | |
| draw(); | |
| requestAnimationFrame(gameLoop); | |
| } | |
| // Initialize game | |
| init(); | |
| updateHUD(); | |
| </script> | |
| </body> | |
| </html> |