diff --git "a/index.html" "b/index.html" --- "a/index.html" +++ "b/index.html" @@ -602,14 +602,22 @@ `; } - if (gameState.timeSlow > 0 && gameState.timeSlow < Date.now()) { - gameState.timeSlow = 0; - createEffect('时间恢复正常', 'cyan', 1500); + if (gameState.timeSlow > Date.now()) { + const timeLeft = Math.ceil((gameState.timeSlow - Date.now()) / 1000); + powerupStatus.innerHTML += ` +
+ +
+ `; } - if (gameState.doubleScore > 0 && gameState.doubleScore < Date.now()) { - gameState.doubleScore = 0; - createEffect('双倍分数结束', 'pink', 1500); + if (gameState.doubleScore > Date.now()) { + const timeLeft = Math.ceil((gameState.doubleScore - Date.now()) / 1000); + powerupStatus.innerHTML += ` +
+ +
+ `; } // 更新Boss血条 @@ -745,10 +753,8 @@ // 创建敌人子弹 function createEnemyBullet(x, y) { - const size = 6; - const speed = 8; - - // 计算子弹方向(朝向玩家) + const size = 8; + const speed = 5 + gameState.speed / 50; const angle = Math.atan2( gameState.plane.y - y, gameState.plane.x - x @@ -759,7 +765,8 @@ y, size, speed, - angle + angle, + damage: 1 }); playSound('enemyShootSound'); @@ -774,8 +781,7 @@ const speed = Math.random() * 2 + 2 + gameState.speed / 50; const type = Math.random() > 0.5 ? 'rectangle' : 'circle'; const health = type === 'rectangle' ? (width > 80 ? 3 : 2) : 1; - const canShoot = Math.random() > 0.7; // 30%概率敌人会射击 - const shootRate = Math.random() * 2000 + 1000; // 射击间隔1-3秒 + const canShoot = Math.random() > 0.7; // 30%的敌人会射击 // 碰撞体积比实际显示大20% const collisionWidth = width * 1.2; @@ -794,8 +800,8 @@ maxHealth: health, isLarge: width > 80, canShoot, - shootRate, - lastShootTime: 0 + lastShootTime: 0, + shootCooldown: Math.random() * 2000 + 1000 // 射击冷却时间1-3秒 }); } @@ -827,8 +833,8 @@ isLarge: true, isBoss: true, canShoot: true, - shootRate: 500, // Boss射击更快 - lastShootTime: 0 + lastShootTime: 0, + shootCooldown: 500 // Boss射击冷却时间0.5秒 }); createEffect('BOSS出现!', 'red', 2000); @@ -903,1379 +909,1354 @@ rotationSpeed: (Math.random() - 0.5) * 0.2, opacity: 1 }); - } - } + } + } - // 创建子弹 - function createBullet() { - // 检查弹药是否足够 - if (gameState.ammo <= 0) { - // 播放弹药不足音效 - if (!gameState.playedEmptySound) { - playSound('emptySound'); - gameState.playedEmptySound = true; - setTimeout(() => { - gameState.playedEmptySound = false; - }, 500); - } - return false; // 没有弹药了 - } + // 创建子弹 + function createBullet() { + if (gameState.ammo <= 0) return false; // 没有弹药了 - // 根据火力增强状态调整子弹属性 - const isRapidFire = gameState.plane.powerups.rapidFire > Date.now(); - const bulletSize = isRapidFire ? 10 : 8; - const bulletDamage = isRapidFire ? 2 : 1; - const bulletSpeed = isRapidFire ? 18 : 15; - - // 计算子弹起始位置(从飞机前端发射) - const bulletX = gameState.plane.x + 30; - const bulletY = gameState.plane.y; - - // 创建子弹对象 - const newBullet = { - x: bulletX, - y: bulletY, - size: bulletSize, - speed: bulletSpeed, - damage: bulletDamage, - // 添加子弹轨迹点用于绘制尾迹 - trail: [] - }; - - // 添加子弹到数组 - gameState.bullets.push(newBullet); - - // 如果有跟踪导弹能力且冷却结束,有70%概率发射跟踪导弹 - if (gameState.plane.powerups.homingMissiles > Date.now() && Math.random() > 0.3) { - setTimeout(() => { - createHomingMissile(); - }, 100); // 稍微延迟发射导弹,看起来更自然 - } - - // 如果弹药不是无限的,减少弹药 - if (gameState.ammo !== Infinity) { - gameState.ammo--; - } - - // 播放射击音效(火力增强时音效更高频) - shootSound.playbackRate = isRapidFire ? 1.5 : 1.0; - playSound('shootSound'); - - // 更新UI - updateUI(); - - // 添加射击反冲效果 - gameState.plane.x -= 2 * Math.cos(gameState.plane.rotation * Math.PI / 180); - gameState.plane.y -= 2 * Math.sin(gameState.plane.rotation * Math.PI / 180); - - // 添加枪口闪光效果 - createParticles( - bulletX, - bulletY, - 5, - isRapidFire ? '#ff9900' : '#ff6600' - ); - - return true; + const size = gameState.plane.powerups.rapidFire > Date.now() ? 10 : 8; // 火力增强时子弹更大 + const damage = gameState.plane.powerups.rapidFire > Date.now() ? 2 : 1; // 火力增强时伤害更高 + const speed = gameState.plane.powerups.rapidFire > Date.now() ? 18 : 15; // 火力增强时速度更快 + const x = gameState.plane.x + 30; // 从飞机前端发射 + const y = gameState.plane.y; + + gameState.bullets.push({ + x, + y, + size, + speed, + damage + }); + + // 如果有跟踪导弹能力且冷却结束 + if (gameState.plane.powerups.homingMissiles > Date.now() && + Math.random() > 0.7) { // 70%概率发射跟踪导弹 + requestAnimationFrame(createHomingMissile); } - - // 创建爆炸效果 - function createExplosion(x, y) { - gameState.explosions.push({ - x, - y, - size: 0, - maxSize: Math.random() * 40 + 40, - alpha: 1 - }); - createParticles(x, y, 20, '#ff6600'); - playSound('explosionSound'); + + // 如果弹药不是无限的,减少弹药 + if (gameState.ammo !== Infinity) { + gameState.ammo--; } + + playSound('shootSound'); + updateUI(); + + return true; + } - // 检测碰撞 - function checkCollision(rect1, rect2) { - return ( - rect1.x < rect2.x + rect2.width && - rect1.x + rect1.width > rect2.x && - rect1.y < rect2.y + rect2.height && - rect1.y + rect1.height > rect2.y - ); - } + // 创建爆炸效果 + function createExplosion(x, y) { + gameState.explosions.push({ + x, + y, + size: 0, + maxSize: Math.random() * 40 + 40, + alpha: 1 + }); + createParticles(x, y, 20, '#ff6600'); + playSound('explosionSound'); + } - // 游戏结束 - function endGame() { - gameState.gameOver = true; - gameUI.classList.add('hidden'); - gameOverScreen.classList.remove('hidden'); - finalScore.textContent = gameState.score; - - // 更新最高分 - if (gameState.score > gameState.highScore) { - gameState.highScore = gameState.score; - localStorage.setItem('highScore', gameState.highScore); - achievementsDisplay.innerHTML += `
🎉 新纪录!
`; - } - - // 显示成就 - if (!gameState.achievements.firstBlood) { - achievementsDisplay.innerHTML += `
🏆 首次击杀!
`; - } - if (!gameState.achievements.combo5) { - achievementsDisplay.innerHTML += `
🔥 连续5次击杀!
`; - } - if (!gameState.achievements.noDamage && gameState.lives === 3) { - achievementsDisplay.innerHTML += `
🛡️ 无伤通关!
`; - } - if (!gameState.achievements.bossSlayer && gameState.bossActive) { - achievementsDisplay.innerHTML += `
👹 Boss杀手!
`; - } - - // 隐藏触摸控制按钮 - leftBtn.classList.add('hidden'); - rightBtn.classList.add('hidden'); - upBtn.classList.add('hidden'); - downBtn.classList.add('hidden'); - fireBtn.classList.add('hidden'); - joystick.classList.add('hidden'); - - bgMusic.pause(); - } + // 检测碰撞 +function checkCollision(rect1, rect2) { + return ( + rect1.x < rect2.x + rect2.width && + rect1.x + rect1.width > rect2.x && + rect1.y < rect2.y + rect2.height && + rect1.y + rect1.height > rect2.y + ); +} - // 游戏主循环 - function gameLoop(timestamp) { - if (!gameState.started || gameState.gameOver) return; - - // 清除画布 - ctx.clearRect(0, 0, canvas.width, canvas.height); - - // 更新游戏状态 - updateGame(timestamp); - - // 绘制游戏元素 - drawGame(); - - // 继续循环 - requestAnimationFrame(gameLoop); - } +// 游戏结束 +function endGame() { + gameState.gameOver = true; + gameUI.classList.add('hidden'); + gameOverScreen.classList.remove('hidden'); + finalScore.textContent = gameState.score; + + // 更新最高分 + if (gameState.score > gameState.highScore) { + gameState.highScore = gameState.score; + localStorage.setItem('highScore', gameState.highScore); + achievementsDisplay.innerHTML += `
🎉 新纪录!
`; + } + + // 显示成就 + if (!gameState.achievements.firstBlood) { + achievementsDisplay.innerHTML += `
🏆 首次击杀!
`; + } + if (!gameState.achievements.combo5) { + achievementsDisplay.innerHTML += `
🔥 连续5次击杀!
`; + } + if (!gameState.achievements.noDamage && gameState.lives === 3) { + achievementsDisplay.innerHTML += `
🛡️ 无伤通关!
`; + } + if (!gameState.achievements.bossSlayer && gameState.bossActive) { + achievementsDisplay.innerHTML += `
👹 Boss杀手!
`; + } + + // 隐藏触摸控制按钮 + leftBtn.classList.add('hidden'); + rightBtn.classList.add('hidden'); + upBtn.classList.add('hidden'); + downBtn.classList.add('hidden'); + fireBtn.classList.add('hidden'); + joystick.classList.add('hidden'); + + bgMusic.pause(); +} - // 更新游戏状态 - function updateGame(timestamp) { - // 随着分数增加难度 - gameState.difficulty = 1 + Math.min(gameState.score / 1000, 3); - - // 检查Boss生成条件 - if (!gameState.bossActive && - gameState.score > 0 && - gameState.score % 5000 === 0 && - timestamp - gameState.lastBossSpawnTime > 30000) { // 每5000分且30秒内未生成Boss - createBoss(); - } - - // 时间减速因子 - const timeSlowFactor = gameState.timeSlow > Date.now() ? 0.5 : 1; - - // 处理开火 - if ((gameState.keys.Space || gameState.isFiring) && - timestamp - gameState.plane.lastFireTime > gameState.plane.fireRate) { // 射击冷却 - createBullet(); - gameState.plane.lastFireTime = timestamp; - } - - // 检查道具是否过期 - if (gameState.plane.powerups.rapidFire > 0 && gameState.plane.powerups.rapidFire < Date.now()) { - gameState.plane.powerups.rapidFire = 0; - gameState.plane.fireRate = 200; // 恢复默认射击速度 - createEffect('火力增强结束', 'red', 1500); - } - - if (gameState.plane.powerups.homingMissiles > 0 && gameState.plane.powerups.homingMissiles < Date.now()) { - gameState.plane.powerups.homingMissiles = 0; - createEffect('跟踪导弹结束', 'purple', 1500); - } - - if (gameState.plane.hasShield && gameState.plane.shieldDuration < Date.now()) { - gameState.plane.hasShield = false; - createEffect('保护罩消失', 'blue', 1500); - } - - if (gameState.timeSlow > 0 && gameState.timeSlow < Date.now()) { - gameState.timeSlow = 0; - createEffect('时间恢复正常', 'cyan', 1500); - } - - if (gameState.doubleScore > 0 && gameState.doubleScore < Date.now()) { - gameState.doubleScore = 0; - createEffect('双倍分数结束', 'pink', 1500); - } - - // 更新飞机速度 - if (gameState.keys.ArrowUp || gameState.keys.ArrowDown) { - gameState.speed = Math.max(50, - Math.min(200, - gameState.speed + (gameState.keys.ArrowUp ? 0.5 : -0.5) - ) - ); - } - - // 更新水平方向移动 - if (gameState.keys.ArrowLeft || gameState.joystickAngle < -Math.PI/4) { - gameState.plane.rotation = Math.max(gameState.plane.rotation - 2, -20); - gameState.plane.velocity = Math.max(gameState.plane.velocity - 0.5, -5); - } else if (gameState.keys.ArrowRight || gameState.joystickAngle > Math.PI/4) { - gameState.plane.rotation = Math.min(gameState.plane.rotation + 2, 20); - gameState.plane.velocity = Math.min(gameState.plane.velocity + 0.5, 5); - } else { - // 如果没有按左右键,飞机逐渐回正 - gameState.plane.rotation *= 0.95; - gameState.plane.velocity *= 0.95; - if (Math.abs(gameState.plane.rotation) < 0.5) gameState.plane.rotation = 0; - if (Math.abs(gameState.plane.velocity) < 0.5) gameState.plane.velocity = 0; - } - - // 更新垂直方向移动 - if (gameState.keys.ArrowUp || (gameState.joystickActive && gameState.joystickAngle < -Math.PI/4 && gameState.joystickAngle > -3*Math.PI/4)) { - gameState.plane.verticalVelocity = Math.max(gameState.plane.verticalVelocity - 0.5, -5); - } else if (gameState.keys.ArrowDown || (gameState.joystickActive && gameState.joystickAngle > Math.PI/4 && gameState.joystickAngle < 3*Math.PI/4)) { - gameState.plane.verticalVelocity = Math.min(gameState.plane.verticalVelocity + 0.5, 5); - } else { - // 如果没有按上下键,垂直速度逐渐归零 - gameState.plane.verticalVelocity *= 0.95; - if (Math.abs(gameState.plane.verticalVelocity) < 0.5) gameState.plane.verticalVelocity = 0; - } - - // 更新飞机位置 - gameState.plane.x += gameState.plane.velocity; - gameState.plane.y += gameState.plane.verticalVelocity; - - // 限制飞机在屏幕内 - gameState.plane.x = Math.max(gameState.plane.width / 2, Math.min(gameState.plane.x, canvas.width - gameState.plane.width / 2)); - gameState.plane.y = Math.max(gameState.plane.height / 2, Math.min(gameState.plane.y, canvas.height - gameState.plane.height / 2)); - - // 生成新星星 - if (timestamp - gameState.lastStarTime > 2000 / gameState.difficulty) { - createStar(); - gameState.lastStarTime = timestamp; - } - - // 生成新障碍物 - if (timestamp - gameState.lastObstacleTime > 1500 / gameState.difficulty * timeSlowFactor) { - createObstacle(); - gameState.lastObstacleTime = timestamp; - } - - // 生成新云朵 - if (timestamp - gameState.lastCloudTime > 1000 * timeSlowFactor) { - createCloud(); - gameState.lastCloudTime = timestamp; - } - - // 生成新道具 (每5-8秒) - if (timestamp - gameState.lastPowerupTime > (Math.random() * 3000 + 5000) / gameState.difficulty * timeSlowFactor) { - createPowerup(); - gameState.lastPowerupTime = timestamp; +// 游戏主循环 +function gameLoop(timestamp) { + if (!gameState.started || gameState.gameOver) return; + + // 清除画布 + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // 更新游戏状态 + updateGame(timestamp); + + // 绘制游戏元素 + drawGame(); + + // 继续循环 + requestAnimationFrame(gameLoop); +} + +// 更新游戏状态 +function updateGame(timestamp) { + // 随着分数增加难度 + gameState.difficulty = 1 + Math.min(gameState.score / 1000, 3); + + // 检查Boss生成条件 + if (!gameState.bossActive && + gameState.score > 0 && + gameState.score % 5000 === 0 && + timestamp - gameState.lastBossSpawnTime > 30000) { // 每5000分且30秒内未生成Boss + createBoss(); + } + + // 时间减速因子 + const timeSlowFactor = gameState.timeSlow > Date.now() ? 0.5 : 1; + + // 处理开火 + if ((gameState.keys.Space || gameState.isFiring) && + timestamp - gameState.plane.lastFireTime > gameState.plane.fireRate) { // 射击冷却 + createBullet(); + gameState.plane.lastFireTime = timestamp; + } + + // 敌人射击 + if (timestamp - gameState.lastEnemyShootTime > 1000 / gameState.difficulty) { + gameState.obstacles.forEach(obstacle => { + if (obstacle.canShoot && timestamp - obstacle.lastShootTime > obstacle.shootCooldown) { + createEnemyBullet(obstacle.x - obstacle.width/2, obstacle.y); + obstacle.lastShootTime = timestamp; } + }); + gameState.lastEnemyShootTime = timestamp; + } + + // 更新敌人子弹 + gameState.enemyBullets.forEach(bullet => { + bullet.x += Math.cos(bullet.angle) * bullet.speed * timeSlowFactor; + bullet.y += Math.sin(bullet.angle) * bullet.speed * timeSlowFactor; + }); + gameState.enemyBullets = gameState.enemyBullets.filter(bullet => + bullet.x > 0 && bullet.x < canvas.width && + bullet.y > 0 && bullet.y < canvas.height + ); + + // 检查道具是否过期 + if (gameState.plane.powerups.rapidFire > 0 && gameState.plane.powerups.rapidFire < Date.now()) { + gameState.plane.powerups.rapidFire = 0; + gameState.plane.fireRate = 200; // 恢复默认射击速度 + createEffect('火力增强结束', 'red', 1500); + } + + if (gameState.plane.powerups.homingMissiles > 0 && gameState.plane.powerups.homingMissiles < Date.now()) { + gameState.plane.powerups.homingMissiles = 0; + createEffect('跟踪导弹结束', 'purple', 1500); + } + + if (gameState.plane.hasShield && gameState.plane.shieldDuration < Date.now()) { + gameState.plane.hasShield = false; + createEffect('保护罩消失', 'blue', 1500); + } + + if (gameState.timeSlow > 0 && gameState.timeSlow < Date.now()) { + gameState.timeSlow = 0; + createEffect('时间恢复正常', 'cyan', 1500); + } + + if (gameState.doubleScore > 0 && gameState.doubleScore < Date.now()) { + gameState.doubleScore = 0; + createEffect('双倍分数结束', 'pink', 1500); + } + + // 更新飞机速度 + if (gameState.keys.ArrowUp || gameState.keys.ArrowDown) { + gameState.speed = Math.max(50, + Math.min(200, + gameState.speed + (gameState.keys.ArrowUp ? 0.5 : -0.5) + ) + ); + } + + // 更新水平方向移动 + if (gameState.keys.ArrowLeft || gameState.joystickAngle < -Math.PI/4) { + gameState.plane.rotation = Math.max(gameState.plane.rotation - 2, -20); + gameState.plane.velocity = Math.max(gameState.plane.velocity - 0.5, -5); + } else if (gameState.keys.ArrowRight || gameState.joystickAngle > Math.PI/4) { + gameState.plane.rotation = Math.min(gameState.plane.rotation + 2, 20); + gameState.plane.velocity = Math.min(gameState.plane.velocity + 0.5, 5); + } else { + // 如果没有按左右键,飞机逐渐回正 + gameState.plane.rotation *= 0.95; + gameState.plane.velocity *= 0.95; + if (Math.abs(gameState.plane.rotation) < 0.5) gameState.plane.rotation = 0; + if (Math.abs(gameState.plane.velocity) < 0.5) gameState.plane.velocity = 0; + } + + // 更新垂直方向移动 + if (gameState.keys.ArrowUp || (gameState.joystickActive && gameState.joystickAngle < -Math.PI/4 && gameState.joystickAngle > -3*Math.PI/4)) { + gameState.plane.verticalVelocity = Math.max(gameState.plane.verticalVelocity - 0.5, -5); + } else if (gameState.keys.ArrowDown || (gameState.joystickActive && gameState.joystickAngle > Math.PI/4 && gameState.joystickAngle < 3*Math.PI/4)) { + gameState.plane.verticalVelocity = Math.min(gameState.plane.verticalVelocity + 0.5, 5); + } else { + // 如果没有按上下键,垂直速度逐渐归零 + gameState.plane.verticalVelocity *= 0.95; + if (Math.abs(gameState.plane.verticalVelocity) < 0.5) gameState.plane.verticalVelocity = 0; + } + + // 更新飞机位置 + gameState.plane.x += gameState.plane.velocity; + gameState.plane.y += gameState.plane.verticalVelocity; + + // 限制飞机在屏幕内 + gameState.plane.x = Math.max(gameState.plane.width / 2, Math.min(gameState.plane.x, canvas.width - gameState.plane.width / 2)); + gameState.plane.y = Math.max(gameState.plane.height / 2, Math.min(gameState.plane.y, canvas.height - gameState.plane.height / 2)); + + // 生成新星星 + if (timestamp - gameState.lastStarTime > 2000 / gameState.difficulty) { + createStar(); + gameState.lastStarTime = timestamp; + } + + // 生成新障碍物 + if (timestamp - gameState.lastObstacleTime > 1500 / gameState.difficulty * timeSlowFactor) { + createObstacle(); + gameState.lastObstacleTime = timestamp; + } + + // 生成新云朵 + if (timestamp - gameState.lastCloudTime > 1000 * timeSlowFactor) { + createCloud(); + gameState.lastCloudTime = timestamp; + } + + // 生成新道具 (每5-8秒) + if (timestamp - gameState.lastPowerupTime > (Math.random() * 3000 + 5000) / gameState.difficulty * timeSlowFactor) { + createPowerup(); + gameState.lastPowerupTime = timestamp; + } + + // 更新粒子 + gameState.particles.forEach(particle => { + particle.x += particle.speedX; + particle.y += particle.speedY; + particle.life--; + }); + gameState.particles = gameState.particles.filter(p => p.life > 0); + + // 更新特效 + gameState.effects = gameState.effects.filter(effect => + Date.now() - effect.startTime < effect.duration + ); + + // 更新碎片 + gameState.debris.forEach(debris => { + debris.x += debris.speedX; + debris.y += debris.speedY; + debris.rotation += debris.rotationSpeed; + debris.opacity -= 0.02; + }); + gameState.debris = gameState.debris.filter(debris => debris.opacity > 0); + + // 更新跟踪导弹 + gameState.homingMissiles.forEach(missile => { + if (!missile.target || missile.target.hit) { + // 如果没有目标或目标已被击中,则直线飞行 + missile.x += Math.cos(missile.angle) * missile.speed; + missile.y += Math.sin(missile.angle) * missile.speed; + } else { + // 计算新的角度以跟踪目标 + const dx = missile.target.x - missile.x; + const dy = missile.target.y - missile.y; + const targetAngle = Math.atan2(dy, dx); + + // 平滑转向 + let angleDiff = targetAngle - missile.angle; + if (angleDiff > Math.PI) angleDiff -= Math.PI * 2; + if (angleDiff < -Math.PI) angleDiff += Math.PI * 2; + + missile.angle += angleDiff * 0.1; + missile.x += Math.cos(missile.angle) * missile.speed; + missile.y += Math.sin(missile.angle) * missile.speed; + + // 检测碰撞 + const missileRect = { + x: missile.x - missile.size / 2, + y: missile.y - missile.size / 2, + width: missile.size, + height: missile.size + }; - // 敌人射击 - gameState.obstacles.forEach(obstacle => { - if (obstacle.canShoot && timestamp - obstacle.lastShootTime > obstacle.shootRate) { - createEnemyBullet(obstacle.x - obstacle.width/2, obstacle.y); - obstacle.lastShootTime = timestamp; - } - }); - - // 更新敌人子弹 - gameState.enemyBullets.forEach(bullet => { - bullet.x -= bullet.speed * Math.cos(bullet.angle) * timeSlowFactor; - bullet.y -= bullet.speed * Math.sin(bullet.angle) * timeSlowFactor; - }); - gameState.enemyBullets = gameState.enemyBullets.filter(bullet => - bullet.x > 0 && bullet.x < canvas.width && - bullet.y > 0 && bullet.y < canvas.height - ); - - // 更新粒子 - gameState.particles.forEach(particle => { - particle.x += particle.speedX; - particle.y += particle.speedY; - particle.life--; - }); - gameState.particles = gameState.particles.filter(p => p.life > 0); - - // 更新特效 - gameState.effects = gameState.effects.filter(effect => - Date.now() - effect.startTime < effect.duration - ); - - // 更新碎片 - gameState.debris.forEach(debris => { - debris.x += debris.speedX; - debris.y += debris.speedY; - debris.rotation += debris.rotationSpeed; - debris.opacity -= 0.02; - }); - gameState.debris = gameState.debris.filter(debris => debris.opacity > 0); + const targetRect = { + x: missile.target.x - missile.target.collisionWidth / 2, + y: missile.target.y - missile.target.collisionHeight / 2, + width: missile.target.collisionWidth, + height: missile.target.collisionHeight + }; - // 更新跟踪导弹 - gameState.homingMissiles.forEach(missile => { - if (!missile.target || missile.target.hit) { - // 如果没有目标或目标已被击中,则直线飞行 - missile.x += Math.cos(missile.angle) * missile.speed; - missile.y += Math.sin(missile.angle) * missile.speed; - } else { - // 计算新的角度以跟踪目标 - const dx = missile.target.x - missile.x; - const dy = missile.target.y - missile.y; - const targetAngle = Math.atan2(dy, dx); - - // 平滑转向 - let angleDiff = targetAngle - missile.angle; - if (angleDiff > Math.PI) angleDiff -= Math.PI * 2; - if (angleDiff < -Math.PI) angleDiff += Math.PI * 2; - - missile.angle += angleDiff * 0.1; - missile.x += Math.cos(missile.angle) * missile.speed; - missile.y += Math.sin(missile.angle) * missile.speed; - - // 检测碰撞 - const missileRect = { - x: missile.x - missile.size / 2, - y: missile.y - missile.size / 2, - width: missile.size, - height: missile.size - }; - - const targetRect = { - x: missile.target.x - missile.target.collisionWidth / 2, - y: missile.target.y - missile.target.collisionHeight / 2, - width: missile.target.collisionWidth, - height: missile.target.collisionHeight - }; + if (checkCollision(missileRect, targetRect)) { + missile.hit = true; + missile.target.health -= 3; // 导弹伤害更高 + + if (missile.target.health <= 0) { + missile.target.hit = true; + const scoreBonus = missile.target.isBoss ? 500 : (missile.target.isLarge ? 30 : 15); + gameState.score += gameState.doubleScore > Date.now() ? scoreBonus * 2 : scoreBonus; + updateUI(); - if (checkCollision(missileRect, targetRect)) { - missile.hit = true; - missile.target.health -= 3; // 导弹伤害更高 - - if (missile.target.health <= 0) { - missile.target.hit = true; - const scoreBonus = missile.target.isBoss ? 500 : (missile.target.isLarge ? 30 : 15); - gameState.score += gameState.doubleScore > Date.now() ? scoreBonus * 2 : scoreBonus; - updateUI(); - - // 如果是大型障碍物但不是Boss,分裂成小型障碍物 - if (missile.target.isLarge && !missile.target.isBoss) { - for (let i = 0; i < 3; i++) { - gameState.obstacles.push({ - x: missile.target.x + (Math.random() - 0.5) * 50, - y: missile.target.y + (Math.random() - 0.5) * 50, - width: missile.target.width / 2, - height: missile.target.height / 2, - collisionWidth: missile.target.collisionWidth / 2, - collisionHeight: missile.target.collisionHeight / 2, - speed: missile.target.speed * 1.2, - type: missile.target.type, - health: 1, - maxHealth: 1, - isLarge: false - }); - } - } - - // 如果是Boss,标记Boss已击败 - if (missile.target.isBoss) { - gameState.bossActive = false; - gameState.achievements.bossSlayer = true; - } - - // 创建碎片效果 - createDebris(missile.target, missile.target.isBoss ? 30 : 12); + // 如果是大型障碍物但不是Boss,分裂成小型障碍物 + if (missile.target.isLarge && !missile.target.isBoss) { + for (let i = 0; i < 3; i++) { + gameState.obstacles.push({ + x: missile.target.x + (Math.random() - 0.5) * 50, + y: missile.target.y + (Math.random() - 0.5) * 50, + width: missile.target.width / 2, + height: missile.target.height / 2, + collisionWidth: missile.target.collisionWidth / 2, + collisionHeight: missile.target.collisionHeight / 2, + speed: missile.target.speed * 1.2, + type: missile.target.type, + health: 1, + maxHealth: 1, + isLarge: false + }); } - - // 创建爆炸效果 - createExplosion(missile.x, missile.y); } + + // 如果是Boss,标记Boss已击败 + if (missile.target.isBoss) { + gameState.bossActive = false; + gameState.achievements.bossSlayer = true; + } + + // 创建碎片效果 + createDebris(missile.target, missile.target.isBoss ? 30 : 12); } - }); - gameState.homingMissiles = gameState.homingMissiles.filter(missile => - missile.x < canvas.width && missile.x > 0 && - missile.y < canvas.height && missile.y > 0 && - !missile.hit - ); - - // 更新云朵 - gameState.clouds.forEach(cloud => { - cloud.x -= cloud.speed * timeSlowFactor; - }); - gameState.clouds = gameState.clouds.filter(cloud => cloud.x + cloud.size > 0); - - // 更新道具 - gameState.powerups.forEach(powerup => { - powerup.x -= powerup.speed * timeSlowFactor; - // 检测与飞机的碰撞 - const planeRect = { - x: gameState.plane.x - gameState.plane.width / 2, - y: gameState.plane.y - gameState.plane.height / 2, - width: gameState.plane.width, - height: gameState.plane.height - }; - - const powerupRect = { - x: powerup.x - powerup.size / 2, - y: powerup.y - powerup.size / 2, - width: powerup.size, - height: powerup.size - }; + // 创建爆炸效果 + createExplosion(missile.x, missile.y); + } + } + }); + gameState.homingMissiles = gameState.homingMissiles.filter(missile => + missile.x < canvas.width && missile.x > 0 && + missile.y < canvas.height && missile.y > 0 && + !missile.hit + ); + + // 更新云朵 + gameState.clouds.forEach(cloud => { + cloud.x -= cloud.speed * timeSlowFactor; + }); + gameState.clouds = gameState.clouds.filter(cloud => cloud.x + cloud.size > 0); + + // 更新道具 + gameState.powerups.forEach(powerup => { + powerup.x -= powerup.speed * timeSlowFactor; + + // 检测与飞机的碰撞 + const planeRect = { + x: gameState.plane.x - gameState.plane.width / 2, + y: gameState.plane.y - gameState.plane.height / 2, + width: gameState.plane.width, + height: gameState.plane.height + }; + + const powerupRect = { + x: powerup.x - powerup.size / 2, + y: powerup.y - powerup.size / 2, + width: powerup.size, + height: powerup.size + }; + + if (checkCollision(planeRect, powerupRect) && !gameState.gameOver) { + powerup.collected = true; + powerup.type.effect(gameState); + updateUI(); + createParticles(powerup.x, powerup.y, 15, powerup.type.color); + } + }); + gameState.powerups = gameState.powerups.filter(powerup => + powerup.x + powerup.size > 0 && !powerup.collected + ); + + // 更新子弹 + gameState.bullets.forEach(bullet => { + bullet.x += bullet.speed * timeSlowFactor; + }); + gameState.bullets = gameState.bullets.filter(bullet => bullet.x < canvas.width); + + // 检查敌人子弹与飞机的碰撞 + gameState.enemyBullets.forEach(bullet => { + const bulletRect = { + x: bullet.x - bullet.size / 2, + y: bullet.y - bullet.size / 2, + width: bullet.size, + height: bullet.size + }; + + const planeRect = { + x: gameState.plane.x - gameState.plane.width / 2, + y: gameState.plane.y - gameState.plane.height / 2, + width: gameState.plane.width, + height: gameState.plane.height + }; + + if (checkCollision(bulletRect, planeRect) { + bullet.hit = true; + if (!gameState.plane.hasShield || gameState.plane.shieldDuration < Date.now()) { + gameState.lives--; + updateUI(); + createExplosion(gameState.plane.x, gameState.plane.y); - if (checkCollision(planeRect, powerupRect) && !gameState.gameOver) { - powerup.collected = true; - powerup.type.effect(gameState); - updateUI(); - createParticles(powerup.x, powerup.y, 15, powerup.type.color); + if (gameState.lives <= 0) { + endGame(); } - }); - gameState.powerups = gameState.powerups.filter(powerup => - powerup.x + powerup.size > 0 && !powerup.collected - ); - - // 更新子弹 - gameState.bullets.forEach(bullet => { - bullet.x += bullet.speed * timeSlowFactor; - }); - gameState.bullets = gameState.bullets.filter(bullet => bullet.x < canvas.width); - - // 更新星星 - gameState.stars.forEach(star => { - star.x -= star.speed * timeSlowFactor; - star.rotation += star.rotationSpeed; - - // 检测碰撞 - const planeRect = { - x: gameState.plane.x - gameState.plane.width / 2, - y: gameState.plane.y - gameState.plane.height / 2, - width: gameState.plane.width, - height: gameState.plane.height - }; - - const starRect = { - x: star.x - star.size / 2, - y: star.y - star.size / 2, - width: star.size, - height: star.size - }; + } else { + // 保护罩被击中 + createExplosion(bullet.x, bullet.y); + } + } + }); + gameState.enemyBullets = gameState.enemyBullets.filter(bullet => !bullet.hit); + + // 更新星星 + gameState.stars.forEach(star => { + star.x -= star.speed * timeSlowFactor; + star.rotation += star.rotationSpeed; + + // 检测碰撞 + const planeRect = { + x: gameState.plane.x - gameState.plane.width / 2, + y: gameState.plane.y - gameState.plane.height / 2, + width: gameState.plane.width, + height: gameState.plane.height + }; + + const starRect = { + x: star.x - star.size / 2, + y: star.y - star.size / 2, + width: star.size, + height: star.size + }; + + if (checkCollision(planeRect, starRect) && !gameState.gameOver) { + star.collected = true; + const scoreBonus = 10; + gameState.score += gameState.doubleScore > Date.now() ? scoreBonus * 2 : scoreBonus; + updateUI(); + createParticles(star.x, star.y, 10, 'gold'); + } + }); + gameState.stars = gameState.stars.filter(star => star.x + star.size > 0 && !star.collected); + + // 更新障碍物 + let comboCount = 0; // 连续击杀计数器 + gameState.obstacles.forEach(obstacle => { + obstacle.x -= obstacle.speed * timeSlowFactor; + + // 检测与飞机的碰撞 + const planeRect = { + x: gameState.plane.x - gameState.plane.width / 2, + y: gameState.plane.y - gameState.plane.height / 2, + width: gameState.plane.width, + height: gameState.plane.height + }; + + const obstacleRect = { + x: obstacle.x - obstacle.collisionWidth / 2, + y: obstacle.y - obstacle.collisionHeight / 2, + width: obstacle.collisionWidth, + height: obstacle.collisionHeight + }; + + if (checkCollision(planeRect, obstacleRect) && !gameState.gameOver) { + // 如果有保护罩则不会受伤 + if (!gameState.plane.hasShield || gameState.plane.shieldDuration < Date.now()) { + obstacle.hit = true; + gameState.lives--; + updateUI(); + createExplosion(gameState.plane.x, gameState.plane.y); - if (checkCollision(planeRect, starRect) && !gameState.gameOver) { - star.collected = true; - const scoreBonus = 10; - gameState.score += gameState.doubleScore > Date.now() ? scoreBonus * 2 : scoreBonus; - updateUI(); - createParticles(star.x, star.y, 10, 'gold'); + if (gameState.lives <= 0) { + endGame(); } - }); - gameState.stars = gameState.stars.filter(star => star.x + star.size > 0 && !star.collected); + } else { + // 保护罩被击中 + obstacle.hit = true; + createExplosion(obstacle.x, obstacle.y); + createDebris(obstacle, 4); + } + } + + // 检测与子弹的碰撞 + if (!obstacle.hit) { + const bulletHits = []; - // 更新障碍物 - let comboCount = 0; // 连续击杀计数器 - gameState.obstacles.forEach(obstacle => { - obstacle.x -= obstacle.speed * timeSlowFactor; - - // 检测与飞机的碰撞 - const planeRect = { - x: gameState.plane.x - gameState.plane.width / 2, - y: gameState.plane.y - gameState.plane.height / 2, - width: gameState.plane.width, - height: gameState.plane.height + gameState.bullets.forEach((bullet, bulletIndex) => { + const bulletRect = { + x: bullet.x - bullet.size / 2, + y: bullet.y - bullet.size / 2, + width: bullet.size, + height: bullet.size }; - const obstacleRect = { + const obstacleCollisionRect = { x: obstacle.x - obstacle.collisionWidth / 2, y: obstacle.y - obstacle.collisionHeight / 2, width: obstacle.collisionWidth, height: obstacle.collisionHeight }; - if (checkCollision(planeRect, obstacleRect) && !gameState.gameOver) { - // 如果有保护罩则不会受伤 - if (!gameState.plane.hasShield || gameState.plane.shieldDuration < Date.now()) { + if (checkCollision(bulletRect, obstacleCollisionRect)) { + obstacle.health -= bullet.damage; + bulletHits.push(bulletIndex); + createExplosion(bullet.x, bullet.y); + + if (obstacle.health <= 0) { obstacle.hit = true; - gameState.lives--; + const scoreBonus = obstacle.isBoss ? 500 : (obstacle.isLarge ? 30 : 15); + gameState.score += gameState.doubleScore > Date.now() ? scoreBonus * 2 : scoreBonus; updateUI(); - createExplosion(gameState.plane.x, gameState.plane.y); + comboCount++; - if (gameState.lives <= 0) { - endGame(); + // 如果是大型障碍物但不是Boss,分裂成小型障碍物 + if (obstacle.isLarge && !obstacle.isBoss) { + for (let i = 0; i < 3; i++) { + gameState.obstacles.push({ + x: obstacle.x + (Math.random() - 0.5) * 50, + y: obstacle.y + (Math.random() - 0.5) * 50, + width: obstacle.width / 2, + height: obstacle.height / 2, + collisionWidth: obstacle.collisionWidth / 2, + collisionHeight: obstacle.collisionHeight / 2, + speed: obstacle.speed * 1.2, + type: obstacle.type, + health: 1, + maxHealth: 1, + isLarge: false + }); + } } - } else { - // 保护罩被击中 - obstacle.hit = true; - createExplosion(obstacle.x, obstacle.y); - createDebris(obstacle, 4); - } - } - - // 检测与子弹的碰撞 - if (!obstacle.hit) { - const bulletHits = []; - - gameState.bullets.forEach((bullet, bulletIndex) => { - const bulletRect = { - x: bullet.x - bullet.size / 2, - y: bullet.y - bullet.size / 2, - width: bullet.size, - height: bullet.size - }; - - const obstacleCollisionRect = { - x: obstacle.x - obstacle.collisionWidth / 2, - y: obstacle.y - obstacle.collisionHeight / 2, - width: obstacle.collisionWidth, - height: obstacle.collisionHeight - }; - if (checkCollision(bulletRect, obstacleCollisionRect)) { - obstacle.health -= bullet.damage; - bulletHits.push(bulletIndex); - createExplosion(bullet.x, bullet.y); - - if (obstacle.health <= 0) { - obstacle.hit = true; - const scoreBonus = obstacle.isBoss ? 500 : (obstacle.isLarge ? 30 : 15); - gameState.score += gameState.doubleScore > Date.now() ? scoreBonus * 2 : scoreBonus; - updateUI(); - comboCount++; - - // 如果是大型障碍物但不是Boss,分裂成小型障碍物 - if (obstacle.isLarge && !obstacle.isBoss) { - for (let i = 0; i < 3; i++) { - gameState.obstacles.push({ - x: obstacle.x + (Math.random() - 0.5) * 50, - y: obstacle.y + (Math.random() - 0.5) * 50, - width: obstacle.width / 2, - height: obstacle.height / 2, - collisionWidth: obstacle.collisionWidth / 2, - collisionHeight: obstacle.collisionHeight / 2, - speed: obstacle.speed * 1.2, - type: obstacle.type, - health: 1, - maxHealth: 1, - isLarge: false - }); - } - } - - // 如果是Boss,标记Boss已击败 - if (obstacle.isBoss) { - gameState.bossActive = false; - gameState.achievements.bossSlayer = true; - } - - // 创建碎片效果 - createDebris(obstacle, obstacle.isBoss ? 30 : 8); - - // 首次击杀成就 - if (!gameState.achievements.firstBlood) { - gameState.achievements.firstBlood = true; - createEffect('首次击杀!', 'green', 2000); - } - } + // 如果是Boss,标记Boss已击败 + if (obstacle.isBoss) { + gameState.bossActive = false; + gameState.achievements.bossSlayer = true; } - }); - - // 连续击杀成就 - if (comboCount >= 5 && !gameState.achievements.combo5) { - gameState.achievements.combo5 = true; - createEffect('连续5次击杀!', 'blue', 2000); - } - - // 移除已经击中的子弹 - for (let i = bulletHits.length - 1; i >= 0; i--) { - gameState.bullets.splice(bulletHits[i], 1); - } - } - }); - gameState.obstacles = gameState.obstacles.filter(obstacle => obstacle.x + obstacle.width > 0 && !obstacle.hit); - - // 检测玩家与敌人子弹的碰撞 - gameState.enemyBullets.forEach((bullet, index) => { - const bulletRect = { - x: bullet.x - bullet.size / 2, - y: bullet.y - bullet.size / 2, - width: bullet.size, - height: bullet.size - }; - - const planeRect = { - x: gameState.plane.x - gameState.plane.width / 2, - y: gameState.plane.y - gameState.plane.height / 2, - width: gameState.plane.width, - height: gameState.plane.height - }; - - if (checkCollision(bulletRect, planeRect) && !gameState.gameOver) { - // 如果有保护罩则不会受伤 - if (!gameState.plane.hasShield || gameState.plane.shieldDuration < Date.now()) { - gameState.enemyBullets.splice(index, 1); - gameState.lives--; - updateUI(); - createExplosion(bullet.x, bullet.y); - if (gameState.lives <= 0) { - endGame(); + // 创建碎片效果 + createDebris(obstacle, obstacle.isBoss ? 30 : 8); + + // 首次击杀成就 + if (!gameState.achievements.firstBlood) { + gameState.achievements.firstBlood = true; + createEffect('首次击杀!', 'green', 2000); } - } else { - // 保护罩被击中 - gameState.enemyBullets.splice(index, 1); - createExplosion(bullet.x, bullet.y); } } }); - // 更新爆炸效果 - gameState.explosions.forEach(explosion => { - explosion.size += 2; - explosion.alpha -= 0.02; - }); - gameState.explosions = gameState.explosions.filter(explosion => explosion.alpha > 0); + // 连续击杀成就 + if (comboCount >= 5 && !gameState.achievements.combo5) { + gameState.achievements.combo5 = true; + createEffect('连续5次击杀!', 'blue', 2000); + } + + // 移除已经击中的子弹 + for (let i = bulletHits.length - 1; i >= 0; i--) { + gameState.bullets.splice(bulletHits[i], 1); + } } + }); + gameState.obstacles = gameState.obstacles.filter(obstacle => obstacle.x + obstacle.width > 0 && !obstacle.hit); + + // 更新爆炸效果 + gameState.explosions.forEach(explosion => { + explosion.size += 2; + explosion.alpha -= 0.02; + }); + gameState.explosions = gameState.explosions.filter(explosion => explosion.alpha > 0); +} - // 绘制游戏元素 - function drawGame() { - // 绘制背景渐变 - const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height); - gradient.addColorStop(0, '#1e3c72'); - gradient.addColorStop(1, '#2a5298'); - ctx.fillStyle = gradient; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - // 绘制云朵 - gameState.clouds.forEach(cloud => { - cloud.parts.forEach(part => { - ctx.beginPath(); - ctx.arc( - cloud.x + part.offsetX, - cloud.y + part.offsetY, - part.size / 2, - 0, - Math.PI * 2 - ); - ctx.fillStyle = `rgba(255, 255, 255, ${0.7 + Math.random() * 0.3})`; - ctx.fill(); - }); - }); - - // 绘制粒子 - gameState.particles.forEach(particle => { - ctx.beginPath(); - ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); - ctx.fillStyle = particle.color; - ctx.globalAlpha = particle.life / 100; - ctx.fill(); - ctx.globalAlpha = 1; - }); +// 绘制游戏元素 +function drawGame() { + // 绘制背景渐变 + const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height); + gradient.addColorStop(0, '#1e3c72'); + gradient.addColorStop(1, '#2a5298'); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // 绘制云朵 + gameState.clouds.forEach(cloud => { + cloud.parts.forEach(part => { + ctx.beginPath(); + ctx.arc( + cloud.x + part.offsetX, + cloud.y + part.offsetY, + part.size / 2, + 0, + Math.PI * 2 + ); + ctx.fillStyle = `rgba(255, 255, 255, ${0.7 + Math.random() * 0.3})`; + ctx.fill(); + }); + }); + + // 绘制粒子 + gameState.particles.forEach(particle => { + ctx.beginPath(); + ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); + ctx.fillStyle = particle.color; + ctx.globalAlpha = particle.life / 100; + ctx.fill(); + ctx.globalAlpha = 1; + }); + + // 绘制碎片 + gameState.debris.forEach(debris => { + ctx.save(); + ctx.translate(debris.x, debris.y); + ctx.rotate(debris.rotation); + + ctx.fillStyle = `rgba(100, 100, 100, ${debris.opacity})`; + ctx.fillRect( + -debris.width / 2, + -debris.height / 2, + debris.width, + debris.height + ); + + ctx.restore(); + }); + + // 绘制敌人子弹 + gameState.enemyBullets.forEach(bullet => { + const gradient = ctx.createRadialGradient( + bullet.x, bullet.y, 0, + bullet.x, bullet.y, bullet.size + ); + gradient.addColorStop(0, '#f00'); + gradient.addColorStop(1, '#800'); + + ctx.beginPath(); + ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2); + ctx.fillStyle = gradient; + ctx.fill(); + + // 子弹尾迹 + ctx.beginPath(); + ctx.moveTo(bullet.x - Math.cos(bullet.angle) * 10, bullet.y - Math.sin(bullet.angle) * 10); + ctx.lineTo(bullet.x, bullet.y); + ctx.strokeStyle = 'rgba(255, 100, 100, 0.8)'; + ctx.lineWidth = bullet.size / 2; + ctx.stroke(); + }); + + // 绘制道具 + gameState.powerups.forEach(powerup => { + ctx.save(); + ctx.translate(powerup.x, powerup.y); + + // 绘制闪光效果 + ctx.beginPath(); + ctx.arc(0, 0, powerup.size / 2, 0, Math.PI * 2); + const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, powerup.size / 2); + gradient.addColorStop(0, powerup.type.color); + gradient.addColorStop(1, 'rgba(255,255,255,0)'); + ctx.fillStyle = gradient; + ctx.globalAlpha = 0.3; + ctx.fill(); + ctx.globalAlpha = 1; + + // 绘制道具图标 + ctx.fillStyle = powerup.type.color; + ctx.beginPath(); + ctx.arc(0, 0, powerup.size / 2 - 3, 0, Math.PI * 2); + ctx.fill(); + + // 绘制边框 + ctx.strokeStyle = 'white'; + ctx.lineWidth = 2; + ctx.stroke(); + + // 绘制道具图标 + ctx.fillStyle = 'white'; + ctx.font = '20px FontAwesome'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(String.fromCharCode(parseInt(getIconCode(powerup.type.icon), 16)), 0, 1); + + ctx.restore(); + }); + + // 绘制跟踪导弹 + gameState.homingMissiles.forEach(missile => { + ctx.save(); + ctx.translate(missile.x, missile.y); + ctx.rotate(missile.angle); + + // 导弹主体 + ctx.fillStyle = 'red'; + ctx.beginPath(); + ctx.moveTo(missile.size / 2, 0); + ctx.lineTo(-missile.size / 2, -missile.size / 3); + ctx.lineTo(-missile.size / 2, missile.size / 3); + ctx.closePath(); + ctx.fill(); + + // 火焰效果 + ctx.fillStyle = 'orange'; + ctx.beginPath(); + ctx.moveTo(-missile.size / 2, -missile.size / 4); + ctx.lineTo(-missile.size, 0); + ctx.lineTo(-missile.size / 2, missile.size / 4); + ctx.closePath(); + ctx.fill(); + + ctx.restore(); + }); + + // 绘制子弹 + gameState.bullets.forEach(bullet => { + const gradient = ctx.createRadialGradient( + bullet.x, bullet.y, 0, + bullet.x, bullet.y, bullet.size + ); + gradient.addColorStop(0, '#ff0'); + gradient.addColorStop(1, '#f80'); + + ctx.beginPath(); + ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2); + ctx.fillStyle = gradient; + ctx.fill(); + + // 子弹尾迹 + ctx.beginPath(); + ctx.moveTo(bullet.x - bullet.speed, bullet.y); + ctx.lineTo(bullet.x, bullet.y); + ctx.strokeStyle = 'rgba(255, 200, 0, 0.8)'; + ctx.lineWidth = bullet.size / 2; + ctx.stroke(); + }); + + // 绘制星星 + gameState.stars.forEach(star => { + ctx.save(); + ctx.translate(star.x, star.y); + ctx.rotate(star.rotation); + + ctx.beginPath(); + for (let i = 0; i < 5; i++) { + const angle = (i * 2 * Math.PI / 5) - Math.PI / 2; + const innerAngle = angle + Math.PI / 5; + const outerRadius = star.size / 2; + const innerRadius = star.size / 4; + + if (i === 0) { + ctx.moveTo( + Math.cos(angle) * outerRadius, + Math.sin(angle) * outerRadius + ); + } else { + ctx.lineTo( + Math.cos(angle) * outerRadius, + Math.sin(angle) * outerRadius + ); + } - // 绘制碎片 - gameState.debris.forEach(debris => { - ctx.save(); - ctx.translate(debris.x, debris.y); - ctx.rotate(debris.rotation); - - ctx.fillStyle = `rgba(100, 100, 100, ${debris.opacity})`; + ctx.lineTo( + Math.cos(innerAngle) * innerRadius, + Math.sin(innerAngle) * innerRadius + ); + } + ctx.closePath(); + + const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, star.size / 2); + gradient.addColorStop(0, 'gold'); + gradient.addColorStop(1, 'yellow'); + ctx.fillStyle = gradient; + ctx.shadowColor = 'yellow'; + ctx.shadowBlur = 10; + ctx.fill(); + + ctx.restore(); + }); + + // 绘制障碍物 + gameState.obstacles.forEach(obstacle => { + ctx.save(); + ctx.translate(obstacle.x, obstacle.y); + + if (obstacle.type === 'rectangle') { + // 绘制健康条 (如果是矩形障碍物) + if (obstacle.health < obstacle.maxHealth) { + const healthBarWidth = obstacle.isBoss ? 100 : 20; + ctx.fillStyle = 'red'; ctx.fillRect( - -debris.width / 2, - -debris.height / 2, - debris.width, - debris.height + -healthBarWidth / 2, + -obstacle.height / 2 - 15, + healthBarWidth, + 5 ); - - ctx.restore(); - }); + ctx.fillStyle = obstacle.isBoss ? 'purple' : 'lime'; + ctx.fillRect( + -healthBarWidth / 2, + -obstacle.height / 2 - 15, + healthBarWidth * (obstacle.health / obstacle.maxHealth), + 5 + ); + } - // 绘制道具 - gameState.powerups.forEach(powerup => { - ctx.save(); - ctx.translate(powerup.x, powerup.y); - - // 绘制闪光效果 + // 绘制敌人飞机 + if (obstacle.canShoot) { + // 飞机主体 + ctx.fillStyle = obstacle.isBoss ? '#8B0000' : '#333'; ctx.beginPath(); - ctx.arc(0, 0, powerup.size / 2, 0, Math.PI * 2); - const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, powerup.size / 2); - gradient.addColorStop(0, powerup.type.color); - gradient.addColorStop(1, 'rgba(255,255,255,0)'); - ctx.fillStyle = gradient; - ctx.globalAlpha = 0.3; + ctx.moveTo(-obstacle.width/2, 0); + ctx.lineTo(obstacle.width/2, -obstacle.height/3); + ctx.lineTo(obstacle.width/2, obstacle.height/3); + ctx.closePath(); ctx.fill(); - ctx.globalAlpha = 1; - // 绘制道具图标 - ctx.fillStyle = powerup.type.color; + // 机翼 + ctx.fillStyle = obstacle.isBoss ? '#600000' : '#222'; ctx.beginPath(); - ctx.arc(0, 0, powerup.size / 2 - 3, 0, Math.PI * 2); + ctx.moveTo(-obstacle.width/4, 0); + ctx.lineTo(obstacle.width/4, -obstacle.height/2); + ctx.lineTo(obstacle.width/2, -obstacle.height/3); + ctx.lineTo(obstacle.width/4, 0); + ctx.closePath(); ctx.fill(); - // 绘制边框 - ctx.strokeStyle = 'white'; - ctx.lineWidth = 2; - ctx.stroke(); - - // 绘制道具图标 - ctx.fillStyle = 'white'; - ctx.font = '20px FontAwesome'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText(String.fromCharCode(parseInt(getIconCode(powerup.type.icon), 16)), 0, 1); - - ctx.restore(); - }); - - // 绘制跟踪导弹 - gameState.homingMissiles.forEach(missile => { - ctx.save(); - ctx.translate(missile.x, missile.y); - ctx.rotate(missile.angle); - - // 导弹主体 - ctx.fillStyle = 'red'; ctx.beginPath(); - ctx.moveTo(missile.size / 2, 0); - ctx.lineTo(-missile.size / 2, -missile.size / 3); - ctx.lineTo(-missile.size / 2, missile.size / 3); + ctx.moveTo(-obstacle.width/4, 0); + ctx.lineTo(obstacle.width/4, obstacle.height/2); + ctx.lineTo(obstacle.width/2, obstacle.height/3); + ctx.lineTo(obstacle.width/4, 0); ctx.closePath(); ctx.fill(); - // 火焰效果 - ctx.fillStyle = 'orange'; + // 尾翼 ctx.beginPath(); - ctx.moveTo(-missile.size / 2, -missile.size / 4); - ctx.lineTo(-missile.size, 0); - ctx.lineTo(-missile.size / 2, missile.size / 4); + ctx.moveTo(-obstacle.width/2, 0); + ctx.lineTo(-obstacle.width/3, -obstacle.height/4); + ctx.lineTo(-obstacle.width/4, -obstacle.height/4); + ctx.lineTo(-obstacle.width/3, 0); ctx.closePath(); ctx.fill(); - ctx.restore(); - }); - - // 绘制子弹 - gameState.bullets.forEach(bullet => { - const gradient = ctx.createRadialGradient( - bullet.x, bullet.y, 0, - bullet.x, bullet.y, bullet.size - ); - gradient.addColorStop(0, '#ff0'); - gradient.addColorStop(1, '#f80'); - ctx.beginPath(); - ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2); - ctx.fillStyle = gradient; + ctx.moveTo(-obstacle.width/2, 0); + ctx.lineTo(-obstacle.width/3, obstacle.height/4); + ctx.lineTo(-obstacle.width/4, obstacle.height/4); + ctx.lineTo(-obstacle.width/3, 0); + ctx.closePath(); ctx.fill(); - // 子弹尾迹 + // 驾驶舱 + ctx.fillStyle = '#3498db'; ctx.beginPath(); - ctx.moveTo(bullet.x - bullet.speed, bullet.y); - ctx.lineTo(bullet.x, bullet.y); - ctx.strokeStyle = 'rgba(255, 200, 0, 0.8)'; - ctx.lineWidth = bullet.size / 2; - ctx.stroke(); - }); - - // 绘制敌人子弹 - gameState.enemyBullets.forEach(bullet => { - const gradient = ctx.createRadialGradient( - bullet.x, bullet.y, 0, - bullet.x, bullet.y, bullet.size - ); - gradient.addColorStop(0, '#f00'); - gradient.addColorStop(1, '#800'); - - ctx.beginPath(); - ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2); - ctx.fillStyle = gradient; + ctx.arc(obstacle.width/4, 0, obstacle.width/8, 0, Math.PI * 2); ctx.fill(); - // 子弹尾迹 - ctx.beginPath(); - ctx.moveTo(bullet.x + bullet.speed * Math.cos(bullet.angle), bullet.y + bullet.speed * Math.sin(bullet.angle)); - ctx.lineTo(bullet.x, bullet.y); - ctx.strokeStyle = 'rgba(255, 100, 100, 0.8)'; - ctx.lineWidth = bullet.size / 2; - ctx.stroke(); - }); - - // 绘制星星 - gameState.stars.forEach(star => { - ctx.save(); - ctx.translate(star.x, star.y); - ctx.rotate(star.rotation); + // 如果是Boss,添加特殊标记 + if (obstacle.isBoss) { + ctx.fillStyle = 'gold'; + ctx.font = 'bold 20px Arial'; +ctx.textAlign = 'center'; +ctx.textBaseline = 'middle'; +ctx.fillText('BOSS', 0, 0); +} +} else { +// 绘制普通矩形障碍物 +ctx.fillStyle = obstacle.isBoss ? '#8B0000' : (obstacle.isLarge ? '#333' : '#555'); +ctx.fillRect( +-obstacle.width / 2, +-obstacle.height / 2, +obstacle.width, +obstacle.height +); + // 添加一些细节 + ctx.fillStyle = obstacle.isBoss ? '#600000' : (obstacle.isLarge ? '#222' : '#444'); + ctx.fillRect( + -obstacle.width / 2 + 5, + -obstacle.height / 2 + 5, + obstacle.width - 10, + obstacle.height - 10 + ); - ctx.beginPath(); - for (let i = 0; i < 5; i++) { - const angle = (i * 2 * Math.PI / 5) - Math.PI / 2; - const innerAngle = angle + Math.PI / 5; - const outerRadius = star.size / 2; - const innerRadius = star.size / 4; - - if (i === 0) { + // 添加裂缝效果 (对于受损的大型障碍物) + if ((obstacle.isLarge || obstacle.isBoss) && obstacle.health < obstacle.maxHealth) { + ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'; + ctx.lineWidth = 2; + for (let i = 0; i < (obstacle.isBoss ? 10 : 3); i++) { + ctx.beginPath(); ctx.moveTo( - Math.cos(angle) * outerRadius, - Math.sin(angle) * outerRadius + -obstacle.width / 2 + Math.random() * obstacle.width, + -obstacle.height / 2 + Math.random() * obstacle.height / 3 ); - } else { ctx.lineTo( - Math.cos(angle) * outerRadius, - Math.sin(angle) * outerRadius + -obstacle.width / 2 + Math.random() * obstacle.width, + obstacle.height / 2 - Math.random() * obstacle.height / 3 ); + ctx.stroke(); } - - ctx.lineTo( - Math.cos(innerAngle) * innerRadius, - Math.sin(innerAngle) * innerRadius - ); } - ctx.closePath(); - - const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, star.size / 2); - gradient.addColorStop(0, 'gold'); - gradient.addColorStop(1, 'yellow'); - ctx.fillStyle = gradient; - ctx.shadowColor = 'yellow'; - ctx.shadowBlur = 10; - ctx.fill(); - - ctx.restore(); - }); - - // 绘制障碍物 - gameState.obstacles.forEach(obstacle => { - ctx.save(); - ctx.translate(obstacle.x, obstacle.y); - if (obstacle.type === 'rectangle') { - // 绘制健康条 (如果是矩形障碍物) - if (obstacle.health < obstacle.maxHealth) { - const healthBarWidth = obstacle.isBoss ? 100 : 20; - ctx.fillStyle = 'red'; - ctx.fillRect( - -healthBarWidth / 2, - -obstacle.height / 2 - 15, - healthBarWidth, - 5 - ); - ctx.fillStyle = obstacle.isBoss ? 'purple' : 'lime'; - ctx.fillRect( - -healthBarWidth / 2, - -obstacle.height / 2 - 15, - healthBarWidth * (obstacle.health / obstacle.maxHealth), - 5 - ); - } - - // 绘制敌人飞机 - if (obstacle.canShoot) { - // 飞机主体 - ctx.fillStyle = obstacle.isBoss ? '#8B0000' : '#333'; - ctx.beginPath(); - ctx.moveTo(-obstacle.width/2, 0); - ctx.lineTo(obstacle.width/2, -obstacle.height/4); - ctx.lineTo(obstacle.width/2, obstacle.height/4); - ctx.closePath(); - ctx.fill(); - - // 飞机机翼 - ctx.fillStyle = obstacle.isBoss ? '#600000' : '#222'; - ctx.beginPath(); - ctx.moveTo(-obstacle.width/4, 0); - ctx.lineTo(0, -obstacle.height/2); - ctx.lineTo(obstacle.width/4, -obstacle.height/4); - ctx.lineTo(obstacle.width/4, obstacle.height/4); - ctx.lineTo(0, obstacle.height/2); - ctx.closePath(); - ctx.fill(); - - // 飞机尾翼 - ctx.fillStyle = obstacle.isBoss ? '#400000' : '#111'; - ctx.beginPath(); - ctx.moveTo(-obstacle.width/2, 0); - ctx.lineTo(-obstacle.width/2, -obstacle.height/4); - ctx.lineTo(-obstacle.width/2 + 10, -obstacle.height/4); - ctx.lineTo(-obstacle.width/4, 0); - ctx.closePath(); - ctx.fill(); - - ctx.beginPath(); - ctx.moveTo(-obstacle.width/2, 0); - ctx.lineTo(-obstacle.width/2, obstacle.height/4); - ctx.lineTo(-obstacle.width/2 + 10, obstacle.height/4); - ctx.lineTo(-obstacle.width/4, 0); - ctx.closePath(); - ctx.fill(); - - // 如果是Boss,添加特殊标记 - if (obstacle.isBoss) { - ctx.fillStyle = 'gold'; - ctx.font = 'bold 20px Arial'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText('BOSS', 0, 0); - } - } else { - // 绘制普通矩形障碍物 - ctx.fillStyle = obstacle.isBoss ? '#8B0000' : (obstacle.isLarge ? '#333' : '#555'); - ctx.fillRect( - -obstacle.width / 2, - -obstacle.height / 2, - obstacle.width, - obstacle.height - ); - - // 添加一些细节 - ctx.fillStyle = obstacle.isBoss ? '#600000' : (obstacle.isLarge ? '#222' : '#444'); - ctx.fillRect( - -obstacle.width / 2 + 5, - -obstacle.height / 2 + 5, - obstacle.width - 10, - obstacle.height - 10 - ); - - // 添加裂缝效果 (对于受损的大型障碍物) - if ((obstacle.isLarge || obstacle.isBoss) && obstacle.health < obstacle.maxHealth) { - ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'; - ctx.lineWidth = 2; - for (let i = 0; i < (obstacle.isBoss ? 10 : 3); i++) { - ctx.beginPath(); - ctx.moveTo( - -obstacle.width / 2 + Math.random() * obstacle.width, - -obstacle.height / 2 + Math.random() * obstacle.height / 3 - ); - ctx.lineTo( - -obstacle.width / 2 + Math.random() * obstacle.width, - obstacle.height / 2 - Math.random() * obstacle.height / 3 - ); - ctx.stroke(); - } - } - - // 如果是Boss,添加特殊标记 - if (obstacle.isBoss) { - ctx.fillStyle = 'gold'; - ctx.font = 'bold 20px Arial'; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText('BOSS', 0, 0); - } - } - } else { - // 圆形障碍物 - ctx.beginPath(); - ctx.arc(0, 0, obstacle.width / 2, 0, Math.PI * 2); - ctx.fillStyle = '#555'; - ctx.fill(); - - // 添加一些细节 - ctx.beginPath(); - ctx.arc(0, 0, obstacle.width / 2 - 5, 0, Math.PI * 2); - ctx.fillStyle = '#444'; - ctx.fill(); - - // 添加裂缝效果 (对于受损的大型障碍物) - if (obstacle.isLarge && obstacle.health < obstacle.maxHealth) { - ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'; - ctx.lineWidth = 2; - for (let i = 0; i < 3; i++) { - ctx.beginPath(); - ctx.moveTo( - Math.cos(i * 2) * obstacle.width / 4, - Math.sin(i * 2) * obstacle.width / 4 - ); - ctx.lineTo( - Math.cos(i * 2 + 1) * obstacle.width / 3, - Math.sin(i * 2 + 1) * obstacle.width / 3 - ); - ctx.stroke(); - } - } + // 如果是Boss,添加特殊标记 + if (obstacle.isBoss) { + ctx.fillStyle = 'gold'; + ctx.font = 'bold 20px Arial'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText('BOSS', 0, 0); } - - ctx.restore(); - }); - - // 绘制飞机 - ctx.save(); - ctx.translate(gameState.plane.x, gameState.plane.y); - ctx.rotate(gameState.plane.rotation * Math.PI / 180); - - // 绘制保护罩 - if (gameState.plane.hasShield && gameState.plane.shieldDuration > Date.now()) { - ctx.beginPath(); - ctx.arc(0, 0, 45, 0, Math.PI * 2); - ctx.strokeStyle = `rgba(0, 204, 255, ${0.3 + Math.sin(Date.now() / 200) * 0.3})`; - ctx.lineWidth = 3; - ctx.stroke(); - - // 保护罩光晕 - const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 45); - gradient.addColorStop(0, 'rgba(0, 204, 255, 0.2)'); - gradient.addColorStop(1, 'rgba(0, 204, 255, 0)'); - ctx.fillStyle = gradient; - ctx.fill(); } - - // 飞机主体 - ctx.beginPath(); - ctx.moveTo(30, 0); - ctx.lineTo(-20, -15); - ctx.lineTo(-25, 0); - ctx.lineTo(-20, 15); - ctx.closePath(); - ctx.fillStyle = '#e74c3c'; - ctx.fill(); - - // 飞机窗户 + } else { + // 圆形障碍物 ctx.beginPath(); - ctx.arc(10, 0, 5, 0, Math.PI * 2); - ctx.fillStyle = '#3498db'; + ctx.arc(0, 0, obstacle.width / 2, 0, Math.PI * 2); + ctx.fillStyle = '#555'; ctx.fill(); - // 飞机机翼 + // 添加一些细节 ctx.beginPath(); - ctx.moveTo(5, 0); - ctx.lineTo(-5, -20); - ctx.lineTo(-15, -20); - ctx.lineTo(-5, 0); - ctx.closePath(); - ctx.fillStyle = '#c0392b'; + ctx.arc(0, 0, obstacle.width / 2 - 5, 0, Math.PI * 2); + ctx.fillStyle = '#444'; ctx.fill(); - ctx.beginPath(); - ctx.moveTo(5, 0); - ctx.lineTo(-5, 20); - ctx.lineTo(-15, 20); - ctx.lineTo(-5, 0); - ctx.closePath(); - ctx.fillStyle = '#c0392b'; - ctx.fill(); - - // 飞机尾翼 - ctx.beginPath(); - ctx.moveTo(-20, 0); - ctx.lineTo(-25, -10); - ctx.lineTo(-30, -10); - ctx.lineTo(-25, 0); - ctx.closePath(); - ctx.fillStyle = '#a5281b'; - ctx.fill(); - - ctx.beginPath(); - ctx.moveTo(-20, 0); - ctx.lineTo(-25, 10); - ctx.lineTo(-30, 10); - ctx.lineTo(-25, 0); - ctx.closePath(); - ctx.fillStyle = '#a5281b'; - ctx.fill(); - - ctx.restore(); - - // 绘制爆炸效果 - gameState.explosions.forEach(explosion => { - ctx.save(); - ctx.translate(explosion.x, explosion.y); - - const gradient = ctx.createRadialGradient( - 0, 0, 0, - 0, 0, explosion.size - ); - gradient.addColorStop(0, `rgba(255, 100, 0, ${explosion.alpha})`); - gradient.addColorStop(0.5, `rgba(255, 200, 0, ${explosion.alpha * 0.6})`); - gradient.addColorStop(1, `rgba(255, 255, 255, 0)`); - - ctx.beginPath(); - ctx.arc(0, 0, explosion.size, 0, Math.PI * 2); - ctx.fillStyle = gradient; - ctx.fill(); - - ctx.restore(); - }); - - // 绘制特效文字 - gameState.effects.forEach(effect => { - const timePassed = Date.now() - effect.startTime; - const progress = timePassed / effect.duration; - - ctx.save(); - ctx.translate(effect.x, effect.y - progress * 50); // 文字向上移动 - ctx.globalAlpha = 1 - progress * 0.8; - ctx.font = 'bold 20px Arial'; - ctx.fillStyle = effect.color; - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - ctx.fillText(effect.text, 0, 0); - - ctx.restore(); - }); - - // 绘制速度线 - if (gameState.speed > 120) { - for (let i = 0; i < 10; i++) { - const x = Math.random() * canvas.width; - const y = Math.random() * canvas.height; - const length = Math.random() * 20 + 10; - const angle = Math.atan2( - gameState.plane.y - y, - gameState.plane.x - x - ); - - ctx.save(); - ctx.translate(x, y); - ctx.rotate(angle); - + // 添加裂缝效果 (对于受损的大型障碍物) + if (obstacle.isLarge && obstacle.health < obstacle.maxHealth) { + ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'; + ctx.lineWidth = 2; + for (let i = 0; i < 3; i++) { ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.lineTo(length, 0); - ctx.strokeStyle = `rgba(255, 255, 255, ${Math.random() * 0.5 + 0.3})`; - ctx.lineWidth = 1; + ctx.moveTo( + Math.cos(i * 2) * obstacle.width / 4, + Math.sin(i * 2) * obstacle.width / 4 + ); + ctx.lineTo( + Math.cos(i * 2 + 1) * obstacle.width / 3, + Math.sin(i * 2 + 1) * obstacle.width / 3 + ); ctx.stroke(); - - ctx.restore(); } } - - // 绘制难度提示 - if (gameState.difficulty > 1.5) { - ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; - ctx.font = '20px Arial'; - ctx.textAlign = 'right'; - ctx.fillText(`难度: ${gameState.difficulty.toFixed(1)}x`, canvas.width - 20, 30); - } - } - - // 辅助函数: 获取FontAwesome图标的Unicode - function getIconCode(iconClass) { - const icons = { - 'fas fa-bolt': 'f0e7', - 'fas fa-rocket': 'f135', - 'fas fa-shield-alt': 'f3ed', - 'fas fa-heart': 'f004', - 'fas fa-clock': 'f017', - 'fas fa-bomb': 'f1e2', - 'fas fa-star': 'f005' - }; - return icons[iconClass] || 'f128'; // 默认返回问号图标 } - - // 虚拟摇杆控制 - function setupJoystick() { - const joystickArea = joystick; - const handle = joystickHandle; - let active = false; - let startX = 0; - let startY = 0; - let handleX = 0; - let handleY = 0; - const maxDistance = 40; - - joystickArea.addEventListener('touchstart', (e) => { - e.preventDefault(); - const touch = e.touches[0]; - const rect = joystickArea.getBoundingClientRect(); - startX = rect.left + rect.width / 2; - startY = rect.top + rect.height / 2; - handleX = touch.clientX - startX; - handleY = touch.clientY - startY; - - // 限制摇杆移动范围 - const distance = Math.sqrt(handleX * handleX + handleY * handleY); - if (distance > maxDistance) { - handleX = (handleX / distance) * maxDistance; - handleY = (handleY / distance) * maxDistance; - } - - handle.style.transform = `translate(${handleX}px, ${handleY}px)`; - - // 计算角度和距离 - gameState.joystickAngle = Math.atan2(handleY, handleX); - gameState.joystickDistance = distance / maxDistance; - gameState.joystickActive = true; - active = true; - }); - - joystickArea.addEventListener('touchmove', (e) => { - if (!active) return; - e.preventDefault(); - const touch = e.touches[0]; - handleX = touch.clientX - startX; - handleY = touch.clientY - startY; - - // 限制摇杆移动范围 - const distance = Math.sqrt(handleX * handleX + handleY * handleY); - if (distance > maxDistance) { - handleX = (handleX / distance) * maxDistance; - handleY = (handleY / distance) * maxDistance; - } - - handle.style.transform = `translate(${handleX}px, ${handleY}px)`; - - // 计算角度和距离 - gameState.joystickAngle = Math.atan2(handleY, handleX); - gameState.joystickDistance = distance / maxDistance; - }); - - joystickArea.addEventListener('touchend', (e) => { - e.preventDefault(); - handle.style.transform = 'translate(0, 0)'; - gameState.joystickActive = false; - active = false; - }); - } - - // 事件监听 - window.addEventListener('resize', resizeCanvas); - // 键盘控制 - document.addEventListener('keydown', (e) => { - if (gameState.keys.hasOwnProperty(e.key)) { - gameState.keys[e.key] = true; - e.preventDefault(); - } - - if (e.key === ' ' || e.key === 'Spacebar') { // 空格键射击 - gameState.keys.Space = true; - e.preventDefault(); - } - }); + ctx.restore(); + }); + + // 绘制飞机 + ctx.save(); + ctx.translate(gameState.plane.x, gameState.plane.y); + ctx.rotate(gameState.plane.rotation * Math.PI / 180); + + // 绘制保护罩 + if (gameState.plane.hasShield && gameState.plane.shieldDuration > Date.now()) { + ctx.beginPath(); + ctx.arc(0, 0, 45, 0, Math.PI * 2); + ctx.strokeStyle = `rgba(0, 204, 255, ${0.3 + Math.sin(Date.now() / 200) * 0.3})`; + ctx.lineWidth = 3; + ctx.stroke(); - document.addEventListener('keyup', (e) => { - if (gameState.keys.hasOwnProperty(e.key)) { - gameState.keys[e.key] = false; - e.preventDefault(); - } - - if (e.key === ' ' || e.key === 'Spacebar') { - gameState.keys.Space = false; - e.preventDefault(); - } - }); + // 保护罩光晕 + const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 45); + gradient.addColorStop(0, 'rgba(0, 204, 255, 0.2)'); + gradient.addColorStop(1, 'rgba(0, 204, 255, 0)'); + ctx.fillStyle = gradient; + ctx.fill(); + } + + // 飞机主体 + ctx.beginPath(); + ctx.moveTo(30, 0); + ctx.lineTo(-20, -15); + ctx.lineTo(-25, 0); + ctx.lineTo(-20, 15); + ctx.closePath(); + ctx.fillStyle = '#e74c3c'; + ctx.fill(); + + // 飞机窗户 + ctx.beginPath(); + ctx.arc(10, 0, 5, 0, Math.PI * 2); + ctx.fillStyle = '#3498db'; + ctx.fill(); + + // 飞机机翼 + ctx.beginPath(); + ctx.moveTo(5, 0); + ctx.lineTo(-5, -20); + ctx.lineTo(-15, -20); + ctx.lineTo(-5, 0); + ctx.closePath(); + ctx.fillStyle = '#c0392b'; + ctx.fill(); + + ctx.beginPath(); + ctx.moveTo(5, 0); + ctx.lineTo(-5, 20); + ctx.lineTo(-15, 20); + ctx.lineTo(-5, 0); + ctx.closePath(); + ctx.fillStyle = '#c0392b'; + ctx.fill(); + + // 飞机尾翼 + ctx.beginPath(); + ctx.moveTo(-20, 0); + ctx.lineTo(-25, -10); + ctx.lineTo(-30, -10); + ctx.lineTo(-25, 0); + ctx.closePath(); + ctx.fillStyle = '#a5281b'; + ctx.fill(); + + ctx.beginPath(); + ctx.moveTo(-20, 0); + ctx.lineTo(-25, 10); + ctx.lineTo(-30, 10); + ctx.lineTo(-25, 0); + ctx.closePath(); + ctx.fillStyle = '#a5281b'; + ctx.fill(); + + ctx.restore(); + + // 绘制爆炸效果 + gameState.explosions.forEach(explosion => { + ctx.save(); + ctx.translate(explosion.x, explosion.y); - // 触摸控制按钮事件 - leftBtn.addEventListener('touchstart', (e) => { - e.preventDefault(); - gameState.keys.ArrowLeft = true; - }); - leftBtn.addEventListener('touchend', (e) => { - e.preventDefault(); - gameState.keys.ArrowLeft = false; - }); + const gradient = ctx.createRadialGradient( + 0, 0, 0, + 0, 0, explosion.size + ); + gradient.addColorStop(0, `rgba(255, 100, 0, ${explosion.alpha})`); + gradient.addColorStop(0.5, `rgba(255, 200, 0, ${explosion.alpha * 0.6})`); + gradient.addColorStop(1, `rgba(255, 255, 255, 0)`); - rightBtn.addEventListener('touchstart', (e) => { - e.preventDefault(); - gameState.keys.ArrowRight = true; - }); - rightBtn.addEventListener('touchend', (e) => { - e.preventDefault(); - gameState.keys.ArrowRight = false; - }); + ctx.beginPath(); + ctx.arc(0, 0, explosion.size, 0, Math.PI * 2); + ctx.fillStyle = gradient; + ctx.fill(); - upBtn.addEventListener('touchstart', (e) => { - e.preventDefault(); - gameState.keys.ArrowUp = true; - }); - upBtn.addEventListener('touchend', (e) => { - e.preventDefault(); - gameState.keys.ArrowUp = false; - }); - - downBtn.addEventListener('touchstart', (e) => { - e.preventDefault(); - gameState.keys.ArrowDown = true; - }); - downBtn.addEventListener('touchend', (e) => { - e.preventDefault(); - gameState.keys.ArrowDown = false; - }); - - fireBtn.addEventListener('touchstart', (e) => { - e.preventDefault(); - gameState.isFiring = true; - }); - fireBtn.addEventListener('touchend', (e) => { - e.preventDefault(); - gameState.isFiring = false; - }); + ctx.restore(); + }); + + // 绘制特效文字 + gameState.effects.forEach(effect => { + const timePassed = Date.now() - effect.startTime; + const progress = timePassed / effect.duration; - // 鼠标控制按钮事件(用于桌面浏览器测试) - leftBtn.addEventListener('mousedown', (e) => { - e.preventDefault(); - gameState.keys.ArrowLeft = true; - }); - leftBtn.addEventListener('mouseup', (e) => { - e.preventDefault(); - gameState.keys.ArrowLeft = false; - }); - leftBtn.addEventListener('mouseleave', (e) => { - e.preventDefault(); - gameState.keys.ArrowLeft = false; - }); + ctx.save(); + ctx.translate(effect.x, effect.y - progress * 50); // 文字向上移动 + ctx.globalAlpha = 1 - progress * 0.8; + + ctx.font = 'bold 20px Arial'; + ctx.fillStyle = effect.color; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(effect.text, 0, 0); - rightBtn.addEventListener('mousedown', (e) => { - e.preventDefault(); - gameState.keys.ArrowRight = true; - }); - rightBtn.addEventListener('mouseup', (e) => { - e.preventDefault(); - gameState.keys.ArrowRight = false; - }); - rightBtn.addEventListener('mouseleave', (e) => { - e.preventDefault(); - gameState.keys.ArrowRight = false; - }); + ctx.restore(); + }); + + // 绘制速度线 + if (gameState.speed > 120) { + for (let i = 0; i < 10; i++) { + const x = Math.random() * canvas.width; + const y = Math.random() * canvas.height; + const length = Math.random() * 20 + 10; + const angle = Math.atan2( + gameState.plane.y - y, + gameState.plane.x - x + ); + + ctx.save(); + ctx.translate(x, y); + ctx.rotate(angle); + + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.lineTo(length, 0); + ctx.strokeStyle = `rgba(255, 255, 255, ${Math.random() * 0.5 + 0.3})`; + ctx.lineWidth = 1; + ctx.stroke(); + + ctx.restore(); + } + } + + // 绘制难度提示 + if (gameState.difficulty > 1.5) { + ctx.fillStyle = 'rgba(255, 0, 0, 0.5)'; + ctx.font = '20px Arial'; + ctx.textAlign = 'right'; + ctx.fillText(`难度: ${gameState.difficulty.toFixed(1)}x`, canvas.width - 20, 30); + } +} + +// 辅助函数: 获取FontAwesome图标的Unicode +function getIconCode(iconClass) { + const icons = { + 'fas fa-bolt': 'f0e7', + 'fas fa-rocket': 'f135', + 'fas fa-shield-alt': 'f3ed', + 'fas fa-heart': 'f004', + 'fas fa-clock': 'f017', + 'fas fa-bomb': 'f1e2', + 'fas fa-star': 'f005' + }; + return icons[iconClass] || 'f128'; // 默认返回问号图标 +} + +// 虚拟摇杆控制 +function setupJoystick() { + const joystickArea = joystick; + const handle = joystickHandle; + let active = false; + let startX = 0; + let startY = 0; + let handleX = 0; + let handleY = 0; + const maxDistance = 40; + + joystickArea.addEventListener('touchstart', (e) => { + e.preventDefault(); + const touch = e.touches[0]; + const rect = joystickArea.getBoundingClientRect(); + startX = rect.left + rect.width / 2; + startY = rect.top + rect.height / 2; + handleX = touch.clientX - startX; + handleY = touch.clientY - startY; - upBtn.addEventListener('mousedown', (e) => { - e.preventDefault(); - gameState.keys.ArrowUp = true; - }); - upBtn.addEventListener('mouseup', (e) => { - e.preventDefault(); - gameState.keys.ArrowUp = false; - }); - upBtn.addEventListener('mouseleave', (e) => { - e.preventDefault(); - gameState.keys.ArrowUp = false; - }); + // 限制摇杆移动范围 + const distance = Math.sqrt(handleX * handleX + handleY * handleY); + if (distance > maxDistance) { + handleX = (handleX / distance) * maxDistance; + handleY = (handleY / distance) * maxDistance; + } - downBtn.addEventListener('mousedown', (e) => { - e.preventDefault(); - gameState.keys.ArrowDown = true; - }); - downBtn.addEventListener('mouseup', (e) => { - e.preventDefault(); - gameState.keys.ArrowDown = false; - }); - downBtn.addEventListener('mouseleave', (e) => { - e.preventDefault(); - gameState.keys.ArrowDown = false; - }); + handle.style.transform = `translate(${handleX}px, ${handleY}px)`; - fireBtn.addEventListener('mousedown', (e) => { - e.preventDefault(); - gameState.isFiring = true; - }); - fireBtn.addEventListener('mouseup', (e) => { - e.preventDefault(); - gameState.isFiring = false; - }); - fireBtn.addEventListener('mouseleave', (e) => { - e.preventDefault(); - gameState.isFiring = false; - }); + // 计算角度和距离 + gameState.joystickAngle = Math.atan2(handleY, handleX); + gameState.joystickDistance = distance / maxDistance; + gameState.joystickActive = true; + active = true; + }); + + joystickArea.addEventListener('touchmove', (e) => { + if (!active) return; + e.preventDefault(); + const touch = e.touches[0]; + handleX = touch.clientX - startX; + handleY = touch.clientY - startY; - // 按钮事件 - startButton.addEventListener('click', initGame); - restartButton.addEventListener('click', initGame); + // 限制摇杆移动范围 + const distance = Math.sqrt(handleX * handleX + handleY * handleY); + if (distance > maxDistance) { + handleX = (handleX / distance) * maxDistance; + handleY = (handleY / distance) * maxDistance; + } - // 初始调整画布大小 - resizeCanvas(); - setupJoystick(); + handle.style.transform = `translate(${handleX}px, ${handleY}px)`; - // 显示最高分 - highScoreDisplay.textContent = gameState.highScore; - -

Made with DeepSite LogoDeepSite - 🧬 Remix

- - \ No newline at end of file + // 计算角度和距离 + gameState.joystickAngle = Math.atan2(handleY, handleX); + gameState.joystickDistance = distance / maxDistance; + }); + + joystickArea.addEventListener('touchend', (e) => { + e.preventDefault(); + handle.style.transform = 'translate(0, 0)'; + gameState.joystickActive = false; + active = false; + }); +} + +// 事件监听 +window.addEventListener('resize', resizeCanvas); + +// 键盘控制 +document.addEventListener('keydown', (e) => { + if (gameState.keys.hasOwnProperty(e.key)) { + gameState.keys[e.key] = true; + e.preventDefault(); + } + + if (e.key === ' ' || e.key === 'Spacebar') { // 空格键射击 + gameState.keys.Space = true; + e.preventDefault(); + } +}); + +document.addEventListener('keyup', (e) => { + if (gameState.keys.hasOwnProperty(e.key)) { + gameState.keys[e.key] = false; + e.preventDefault(); + } + + if (e.key === ' ' || e.key === 'Spacebar') { + gameState.keys.Space = false; + e.preventDefault(); + } +}); + +// 触摸控制按钮事件 +leftBtn.addEventListener('touchstart', (e) => { + e.preventDefault(); + gameState.keys.ArrowLeft = true; +}); +leftBtn.addEventListener('touchend', (e) => { + e.preventDefault(); + gameState.keys.ArrowLeft = false; +}); + +rightBtn.addEventListener('touchstart', (e) => { + e.preventDefault(); + gameState.keys.ArrowRight = true; +}); +rightBtn.addEventListener('touchend', (e) => { + e.preventDefault(); + gameState.keys.ArrowRight = false; +}); + +upBtn.addEventListener('touchstart', (e) => { + e.preventDefault(); + gameState.keys.ArrowUp = true; +}); +upBtn.addEventListener('touchend', (e) => { + e.preventDefault(); + gameState.keys.ArrowUp = false; +}); + +downBtn.addEventListener('touchstart', (e) => { + e.preventDefault(); + gameState.keys.ArrowDown = true; +}); +downBtn.addEventListener('touchend', (e) => { + e.preventDefault(); + gameState.keys.ArrowDown = false; +}); + +fireBtn.addEventListener('touchstart', (e) => { + e.preventDefault(); + gameState.isFiring = true; +}); +fireBtn.addEventListener('touchend', (e) => { + e.preventDefault(); + gameState.isFiring = false; +}); + +// 鼠标控制按钮事件(用于桌面浏览器测试) +leftBtn.addEventListener('mousedown', (e) => { + e.preventDefault(); + gameState.keys.ArrowLeft = true; +}); +leftBtn.addEventListener('mouseup', (e) => { + e.preventDefault(); + gameState.keys.ArrowLeft = false; +}); +leftBtn.addEventListener('mouseleave', (e) => { + e.preventDefault(); + gameState.keys.ArrowLeft = false; +}); + +rightBtn.addEventListener('mousedown', (e) => { + e.preventDefault(); + gameState.keys.ArrowRight = true; +}); +rightBtn.addEventListener('mouseup', (e) => { + e.preventDefault(); + gameState.keys.ArrowRight = false; +}); +rightBtn.addEventListener('mouseleave', (e) => { + e.preventDefault(); + gameState.keys.ArrowRight = false; +}); + +upBtn.addEventListener('mousedown', (e) => { + e.preventDefault(); + gameState.keys.ArrowUp = true; +}); +upBtn.addEventListener('mouseup', (e) => { + e.preventDefault(); + gameState.keys.ArrowUp = false; +}); +upBtn.addEventListener('mouseleave', (e) => { + e.preventDefault(); + gameState.keys.ArrowUp = false; +}); + +downBtn.addEventListener('mousedown', (e) => { + e.preventDefault(); + gameState.keys.ArrowDown = true; +}); +downBtn.addEventListener('mouseup', (e) => { + e.preventDefault(); + gameState.keys.ArrowDown = false; +}); +downBtn.addEventListener('mouseleave', (e) => { + e.preventDefault(); + gameState.keys.ArrowDown = false; +}); + +fireBtn.addEventListener('mousedown', (e) => { + e.preventDefault(); + gameState.isFiring = true; +}); +fireBtn.addEventListener('mouseup', (e) => { + e.preventDefault(); + gameState.isFiring = false; +}); +fireBtn.addEventListener('mouseleave', (e) => { + e.preventDefault(); + gameState.isFiring = false; +}); + +// 按钮事件 +startButton.addEventListener('click', initGame); +restartButton.addEventListener('click', initGame); + +// 初始调整画布大小 +resizeCanvas(); +setupJoystick(); + +// 显示最高分 +highScoreDisplay.textContent = gameState.highScore; +

Made with DeepSite LogoDeepSite - 🧬 Remix

+ + \ No newline at end of file