const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // Game State let gameState = 'start'; // start, playing, gameover let score = 0; let lives = 3; let highScore = 0; let animFrame = 0; // Game Objects const game = { player: { x: 375, y: 520, width: 40, height: 40, speed: 6, angle: 0 }, bullets: [], enemies: [], powerups: [], particles: [], keys: {}, lastShot: 0, shootDelay: 250, enemySpawnRate: 1500, lastEnemySpawn: 0, powerupSpawnChance: 0.02 }; // Draw Functions function drawStars() { ctx.fillStyle = '#ffffff'; for (let i = 0; i < 100; i++) { const x = (i * 73) % canvas.width; const y = (i * 97) % canvas.height; const size = (i % 3) + 1; ctx.fillRect(x, y, size, size); } } function drawPlayer() { const p = game.player; ctx.save(); ctx.translate(p.x + p.width / 2, p.y + p.height / 2); ctx.rotate(p.angle); // จรวดสีฟ้า-เงิน ctx.fillStyle = '#00d4ff'; ctx.beginPath(); ctx.moveTo(0, -20); ctx.lineTo(-15, 20); ctx.lineTo(15, 20); ctx.closePath(); ctx.fill(); // หน้าต่างกระจก ctx.fillStyle = '#ffff00'; ctx.beginPath(); ctx.arc(0, -5, 5, 0, Math.PI * 2); ctx.fill(); // ปีกข้าง ctx.fillStyle = '#ff0066'; ctx.fillRect(-15, 10, 8, 5); ctx.fillRect(7, 10, 8, 5); // ไฟพุ่ง if (Math.floor(animFrame / 3) % 2 === 0) { ctx.fillStyle = '#ff6600'; ctx.beginPath(); ctx.moveTo(-8, 20); ctx.lineTo(-5, 28); ctx.lineTo(-2, 20); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(2, 20); ctx.lineTo(5, 28); ctx.lineTo(8, 20); ctx.closePath(); ctx.fill(); } ctx.restore(); } function drawBullet(bullet) { ctx.fillStyle = '#00ff00'; ctx.shadowColor = '#00ff00'; ctx.shadowBlur = 10; ctx.fillRect(bullet.x - 2, bullet.y - 8, 4, 8); ctx.shadowBlur = 0; } function drawEnemy(enemy) { ctx.save(); ctx.translate(enemy.x, enemy.y); ctx.rotate(enemy.rotation); // อุกกาบาต ctx.fillStyle = enemy.color; ctx.beginPath(); const sides = 8; for (let i = 0; i < sides; i++) { const angle = (i / sides) * Math.PI * 2; const radius = enemy.size + Math.sin(i * 2) * 5; const x = Math.cos(angle) * radius; const y = Math.sin(angle) * radius; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.closePath(); ctx.fill(); // รอยแตก ctx.strokeStyle = '#000000'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(-enemy.size / 2, 0); ctx.lineTo(enemy.size / 2, 0); ctx.stroke(); ctx.restore(); } function drawPowerup(powerup) { ctx.save(); ctx.translate(powerup.x, powerup.y); ctx.rotate(powerup.rotation); // ดาวสีทอง ctx.fillStyle = '#ffd700'; ctx.shadowColor = '#ffd700'; ctx.shadowBlur = 15; ctx.beginPath(); for (let i = 0; i < 5; i++) { const angle = (i / 5) * Math.PI * 2 - Math.PI / 2; const x = Math.cos(angle) * 15; const y = Math.sin(angle) * 15; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); const innerAngle = angle + Math.PI / 5; const ix = Math.cos(innerAngle) * 7; const iy = Math.sin(innerAngle) * 7; ctx.lineTo(ix, iy); } ctx.closePath(); ctx.fill(); ctx.shadowBlur = 0; ctx.restore(); } function drawParticle(particle) { ctx.fillStyle = particle.color; ctx.globalAlpha = particle.life; ctx.fillRect(particle.x, particle.y, particle.size, particle.size); ctx.globalAlpha = 1; } // Game Functions function createExplosion(x, y, color) { for (let i = 0; i < 20; i++) { const angle = Math.random() * Math.PI * 2; const speed = Math.random() * 5 + 2; game.particles.push({ x, y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, color: color || '#ff6600', size: Math.random() * 4 + 2, life: 1 }); } } function spawnEnemy() { const size = Math.random() * 20 + 20; const colors = ['#8b4513', '#696969', '#a0522d', '#808080']; game.enemies.push({ x: Math.random() * (canvas.width - size * 2) + size, y: -size, size, speed: Math.random() * 2 + 1 + score / 500, rotation: 0, rotationSpeed: (Math.random() - 0.5) * 0.1, color: colors[Math.floor(Math.random() * colors.length)] }); } function spawnPowerup(x, y) { game.powerups.push({ x, y, rotation: 0, speed: 2 }); } function checkCollision(obj1, obj2, size1, size2) { const dx = obj1.x - obj2.x; const dy = obj1.y - obj2.y; const distance = Math.sqrt(dx * dx + dy * dy); return distance < size1 + size2; } function playSound(type) { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const oscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); if (type === 'powerup') { oscillator.frequency.value = 1200; gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); oscillator.frequency.exponentialRampToValueAtTime(1600, audioContext.currentTime + 0.2); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2); oscillator.start(audioContext.currentTime); oscillator.stop(audioContext.currentTime + 0.2); } else if (type === 'explosion') { oscillator.type = 'sawtooth'; oscillator.frequency.value = 200; gainNode.gain.setValueAtTime(0.5, audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3); oscillator.start(audioContext.currentTime); oscillator.stop(audioContext.currentTime + 0.3); } else if (type === 'shoot') { oscillator.frequency.value = 800; gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1); oscillator.start(audioContext.currentTime); oscillator.stop(audioContext.currentTime + 0.1); } } function update() { if (gameState !== 'playing') return; const now = Date.now(); animFrame++; // อัพเดทผู้เล่น if (game.keys['ArrowLeft']) { game.player.x = Math.max(0, game.player.x - game.player.speed); } if (game.keys['ArrowRight']) { game.player.x = Math.min(canvas.width - game.player.width, game.player.x + game.player.speed); } // ยิงกระสุน if (game.keys[' '] && now - game.lastShot > game.shootDelay) { game.bullets.push({ x: game.player.x + game.player.width / 2, y: game.player.y, speed: 8 }); game.lastShot = now; playSound('shoot'); } // อัพเดทกระสุน game.bullets = game.bullets.filter(bullet => { bullet.y -= bullet.speed; return bullet.y > -10; }); // สร้างศัตรู if (now - game.lastEnemySpawn > game.enemySpawnRate) { spawnEnemy(); game.lastEnemySpawn = now; game.enemySpawnRate = Math.max(500, 1500 - score * 2); } // อัพเดทศัตรู game.enemies = game.enemies.filter(enemy => { enemy.y += enemy.speed; enemy.rotation += enemy.rotationSpeed; // ชนผู้เล่น if (checkCollision(game.player, enemy, 20, enemy.size)) { createExplosion(enemy.x, enemy.y, '#ff0000'); playSound('explosion'); lives--; if (lives <= 0) { gameState = 'gameover'; if (score > highScore) highScore = score; } return false; } return enemy.y < canvas.height + enemy.size; }); // ตรวจสอบกระสุนโดนศัตรู for (let i = game.bullets.length - 1; i >= 0; i--) { for (let j = game.enemies.length - 1; j >= 0; j--) { if (checkCollision(game.bullets[i], game.enemies[j], 5, game.enemies[j].size)) { createExplosion(game.enemies[j].x, game.enemies[j].y, game.enemies[j].color); playSound('explosion'); game.bullets.splice(i, 1); game.enemies.splice(j, 1); score += 10; // สุ่มดรอป powerup if (Math.random() < game.powerupSpawnChance) { spawnPowerup(game.enemies[j] ? game.enemies[j].x : canvas.width / 2, game.enemies[j] ? game.enemies[j].y : 100); } break; } } } // อัพเดท powerup game.powerups = game.powerups.filter(powerup => { powerup.y += powerup.speed; powerup.rotation += 0.1; if (checkCollision(game.player, powerup, 20, 15)) { playSound('powerup'); score += 50; createExplosion(powerup.x, powerup.y, '#ffd700'); game.shootDelay = Math.max(100, game.shootDelay - 20); return false; } return powerup.y < canvas.height; }); // อัพเดทอนุภาค game.particles = game.particles.filter(particle => { particle.x += particle.vx; particle.y += particle.vy; particle.vy += 0.2; particle.life -= 0.02; return particle.life > 0; }); } function draw() { ctx.fillStyle = '#0a0a1a'; ctx.fillRect(0, 0, canvas.width, canvas.height); drawStars(); if (gameState === 'start') { ctx.fillStyle = '#00ff00'; ctx.font = 'bold 48px "Courier New"'; ctx.textAlign = 'center'; ctx.fillText('🚀 SPACE SHOOTER 🚀', canvas.width / 2, 200); ctx.fillStyle = '#ffffff'; ctx.font = '24px "Courier New"'; ctx.fillText('กด SPACE เพื่อเริ่มเกม', canvas.width / 2, 300); ctx.font = '18px "Courier New"'; ctx.fillText('ลูกศร ←→ เคลื่อนที่', canvas.width / 2, 350); ctx.fillText('SPACE ยิงกระสุน', canvas.width / 2, 380); ctx.fillStyle = '#ffd700'; ctx.fillText(`สถิติสูงสุด: ${highScore}`, canvas.width / 2, 450); } else if (gameState === 'playing') { game.particles.forEach(drawParticle); game.bullets.forEach(drawBullet); game.enemies.forEach(drawEnemy); game.powerups.forEach(drawPowerup); drawPlayer(); // HUD ctx.fillStyle = '#00ff00'; ctx.font = 'bold 24px "Courier New"'; ctx.textAlign = 'left'; ctx.fillText(`คะแนน: ${score}`, 20, 40); ctx.fillText(`ชีวิต: ${'❤️'.repeat(lives)}`, 20, 70); } else if (gameState === 'gameover') { ctx.fillStyle = '#ff0000'; ctx.font = 'bold 48px "Courier New"'; ctx.textAlign = 'center'; ctx.fillText('GAME OVER', canvas.width / 2, 200); ctx.fillStyle = '#ffffff'; ctx.font = '32px "Courier New"'; ctx.fillText(`คะแนนของคุณ: ${score}`, canvas.width / 2, 280); ctx.fillStyle = '#ffd700'; ctx.fillText(`สถิติสูงสุด: ${highScore}`, canvas.width / 2, 330); ctx.fillStyle = '#00ff00'; ctx.font = '24px "Courier New"'; ctx.fillText('กด R เพื่อเล่นใหม่', canvas.width / 2, 400); } } function gameLoop() { update(); draw(); requestAnimationFrame(gameLoop); } // Event Listeners window.addEventListener('keydown', (e) => { game.keys[e.key] = true; if (e.key === ' ') { e.preventDefault(); if (gameState === 'start') { gameState = 'playing'; score = 0; lives = 3; game.enemies = []; game.bullets = []; game.powerups = []; game.particles = []; game.shootDelay = 250; game.player.x = 375; } } if (e.key === 'r' || e.key === 'R') { if (gameState === 'gameover') { gameState = 'start'; } } }); window.addEventListener('keyup', (e) => { game.keys[e.key] = false; }); // Start Game Loop gameLoop();