script.js

11.45 KB
06/11/2025 16:18
JS
script.js
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();