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();