zdwalter commited on
Commit
1e546e0
·
verified ·
1 Parent(s): 2773ee0

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +751 -180
index.html CHANGED
@@ -10,6 +10,8 @@
10
  body {
11
  overflow: hidden;
12
  touch-action: none;
 
 
13
  }
14
  #gameCanvas {
15
  display: block;
@@ -145,6 +147,42 @@
145
  pointer-events: none;
146
  animation: pulse 1.5s infinite;
147
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  </style>
149
  </head>
150
  <body class="bg-gray-900 text-white flex flex-col items-center justify-center h-screen">
@@ -159,7 +197,7 @@
159
  <button id="startButton" class="bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-3 px-8 rounded-full text-xl transition-all duration-300 transform hover:scale-105 pointer-events-auto">
160
  开始游戏
161
  </button>
162
- <div class="mt-8 grid grid-cols-2 gap-4 text-left max-w-md px-8">
163
  <div class="flex items-center">
164
  <div class="powerup bg-red-500 mr-2"><i class="fas fa-bolt"></i></div>
165
  <span>火力增强</span>
@@ -176,6 +214,21 @@
176
  <div class="powerup bg-green-500 mr-2"><i class="fas fa-heart"></i></div>
177
  <span>恢复生命</span>
178
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  </div>
180
  </div>
181
 
@@ -212,6 +265,11 @@
212
  <!-- 这里会被JavaScript动态填充 -->
213
  </div>
214
 
 
 
 
 
 
215
  <!-- 触摸控制按钮 -->
216
  <div id="leftBtn" class="control-btn hidden">
217
  <i class="fas fa-arrow-left"></i>
@@ -228,6 +286,11 @@
228
  <div id="fireBtn" class="control-btn hidden">
229
  <i class="fas fa-bolt text-yellow-400"></i>
230
  </div>
 
 
 
 
 
231
  </div>
232
 
233
  <!-- 游戏结束界面 -->
@@ -236,12 +299,20 @@
236
  <div class="text-3xl mb-8">
237
  得分: <span id="finalScore" class="text-yellow-400">0</span>
238
  </div>
 
 
 
239
  <button id="restartButton" class="bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-3 px-8 rounded-full text-xl transition-all duration-300 transform hover:scale-105 pointer-events-auto">
240
  再玩一次
241
  </button>
242
  </div>
243
  </div>
244
 
 
 
 
 
 
245
  <script>
246
  // 游戏状态
247
  const gameState = {
@@ -252,6 +323,11 @@
252
  ammo: Infinity,
253
  speed: 100,
254
  difficulty: 1,
 
 
 
 
 
255
  plane: {
256
  x: 0,
257
  y: 0,
@@ -276,13 +352,16 @@
276
  explosions: [],
277
  bullets: [],
278
  debris: [],
279
- powerups: [], // 新增: 道具
280
- homingMissiles: [], // 新增: 跟踪导弹
281
- effects: [], // 新增: 特效
 
 
282
  lastStarTime: 0,
283
  lastObstacleTime: 0,
284
  lastCloudTime: 0,
285
- lastPowerupTime: 0, // 新增: 上次生成道具时间
 
286
  keys: {
287
  ArrowUp: false,
288
  ArrowDown: false,
@@ -290,7 +369,17 @@
290
  ArrowRight: false,
291
  Space: false
292
  },
293
- isMobile: false
 
 
 
 
 
 
 
 
 
 
294
  };
295
 
296
  // 道具类型
@@ -304,6 +393,7 @@
304
  game.plane.fireRate = 100; // 更快射击
305
  game.plane.powerups.rapidFire = Date.now() + POWERUP_TYPES.RAPID_FIRE.duration;
306
  createEffect('火力增强!', 'red', 1500);
 
307
  }
308
  },
309
  HOMING_MISSILE: {
@@ -314,6 +404,7 @@
314
  effect: (game) => {
315
  game.plane.powerups.homingMissiles = Date.now() + POWERUP_TYPES.HOMING_MISSILE.duration;
316
  createEffect('跟踪导弹已激活!', 'purple', 1500);
 
317
  }
318
  },
319
  SHIELD: {
@@ -325,6 +416,7 @@
325
  game.plane.hasShield = true;
326
  game.plane.shieldDuration = Date.now() + POWERUP_TYPES.SHIELD.duration;
327
  createEffect('保护罩已启用!', 'blue', 1500);
 
328
  }
329
  },
330
  HEALTH: {
@@ -335,6 +427,44 @@
335
  game.lives = Math.min(game.lives + 1, 5); // 最多5条命
336
  updateUI();
337
  createEffect('生命值恢复!', 'green', 1500);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  }
339
  }
340
  };
@@ -358,6 +488,30 @@
358
  const upBtn = document.getElementById('upBtn');
359
  const downBtn = document.getElementById('downBtn');
360
  const fireBtn = document.getElementById('fireBtn');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
 
362
  // 检测是否移动设备
363
  function detectMobile() {
@@ -387,12 +541,28 @@
387
  });
388
  }
389
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  // 更新UI
391
  function updateUI() {
392
  scoreDisplay.textContent = gameState.score;
393
  livesDisplay.textContent = gameState.lives;
394
  speedDisplay.textContent = Math.floor(gameState.speed);
395
  ammoDisplay.textContent = gameState.ammo === Infinity ? "∞" : gameState.ammo;
 
396
 
397
  // 更新道具状态显示
398
  powerupStatus.innerHTML = '';
@@ -423,6 +593,32 @@
423
  </div>
424
  `;
425
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
  }
427
 
428
  // 初始化游戏
@@ -436,6 +632,11 @@
436
  gameState.ammo = Infinity;
437
  gameState.speed = 100;
438
  gameState.difficulty = 1;
 
 
 
 
 
439
  gameState.plane = {
440
  x: canvas.width / 2,
441
  y: canvas.height / 2,
@@ -462,11 +663,20 @@
462
  gameState.debris = [];
463
  gameState.powerups = [];
464
  gameState.homingMissiles = [];
 
465
  gameState.effects = [];
 
466
  gameState.lastStarTime = 0;
467
  gameState.lastObstacleTime = 0;
468
  gameState.lastCloudTime = 0;
469
  gameState.lastPowerupTime = 0;
 
 
 
 
 
 
 
470
 
471
  startScreen.classList.add('hidden');
472
  gameOverScreen.classList.add('hidden');
@@ -474,15 +684,17 @@
474
 
475
  // 显示触摸控制按钮(如果是移动设备)
476
  if (gameState.isMobile) {
477
- leftBtn.classList.remove('hidden');
478
- rightBtn.classList.remove('hidden');
479
- upBtn.classList.remove('hidden');
480
- downBtn.classList.remove('hidden');
481
  fireBtn.classList.remove('hidden');
 
482
  }
483
 
484
  updateUI();
485
  createInitialClouds();
 
486
  requestAnimationFrame(gameLoop);
487
  }
488
 
@@ -530,7 +742,7 @@
530
  });
531
  }
532
 
533
- // 创建障碍物
534
  function createObstacle() {
535
  const width = Math.random() * 80 + 40;
536
  const height = Math.random() * 80 + 40;
@@ -555,10 +767,45 @@
555
  type,
556
  health,
557
  maxHealth: health,
558
- isLarge: width > 80
 
 
559
  });
560
  }
561
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  // 创建道具
563
  function createPowerup() {
564
  const size = 40;
@@ -631,13 +878,13 @@
631
  }
632
  }
633
 
634
- // 创建子弹
635
  function createBullet() {
636
  if (gameState.ammo <= 0) return false; // 没有弹药了
637
 
638
  const size = gameState.plane.powerups.rapidFire > Date.now() ? 10 : 8; // 火力增强时子弹更大
639
- const damage = gameState.plane.powerups.rapidFire > Date.now() ? 2 : 1; // 火力增强时伤害更高
640
- const speed = gameState.plane.powerups.rapidFire > Date.now() ? 18 : 15; // 火力增强时速度更快
641
  const x = gameState.plane.x + 30; // 从飞机前端发射
642
  const y = gameState.plane.y;
643
 
@@ -649,7 +896,7 @@
649
  damage
650
  });
651
 
652
- // 如果有跟踪导弹能力且冷却结束
653
  if (gameState.plane.powerups.homingMissiles > Date.now() &&
654
  Math.random() > 0.7) { // 70%概率发射跟踪导弹
655
  requestAnimationFrame(createHomingMissile);
@@ -660,6 +907,7 @@
660
  gameState.ammo--;
661
  }
662
 
 
663
  updateUI();
664
 
665
  return true;
@@ -674,9 +922,26 @@
674
  maxSize: Math.random() * 40 + 40,
675
  alpha: 1
676
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
677
  }
678
 
679
- // 检测碰撞
680
  function checkCollision(rect1, rect2) {
681
  return (
682
  rect1.x < rect2.x + rect2.width &&
@@ -693,12 +958,36 @@
693
  gameOverScreen.classList.remove('hidden');
694
  finalScore.textContent = gameState.score;
695
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
696
  // 隐藏触摸控制按钮
697
  leftBtn.classList.add('hidden');
698
  rightBtn.classList.add('hidden');
699
  upBtn.classList.add('hidden');
700
  downBtn.classList.add('hidden');
701
  fireBtn.classList.add('hidden');
 
 
 
702
  }
703
 
704
  // 游戏主循环
@@ -723,14 +1012,27 @@
723
  // 随着分数增加难度
724
  gameState.difficulty = 1 + Math.min(gameState.score / 1000, 3);
725
 
726
- // 处理开火
 
 
 
 
 
 
 
 
 
 
 
 
 
727
  if ((gameState.keys.Space || gameState.isFiring) &&
728
- timestamp - gameState.plane.lastFireTime > gameState.plane.fireRate) { // 射击冷却
729
  createBullet();
730
  gameState.plane.lastFireTime = timestamp;
731
  }
732
 
733
- // 检查道具是否过期
734
  if (gameState.plane.powerups.rapidFire > 0 && gameState.plane.powerups.rapidFire < Date.now()) {
735
  gameState.plane.powerups.rapidFire = 0;
736
  gameState.plane.fireRate = 200; // 恢复默认射击速度
@@ -747,20 +1049,29 @@
747
  createEffect('保护罩消失', 'blue', 1500);
748
  }
749
 
750
- // 更新飞机速度
 
 
 
 
 
 
 
 
 
 
751
  if (gameState.keys.ArrowUp || gameState.keys.ArrowDown) {
752
- gameState.speed = Math.max(50,
753
- Math.min(200,
754
- gameState.speed + (gameState.keys.ArrowUp ? 0.5 : -0.5)
755
- )
756
  );
757
  }
758
 
759
  // 更新水平方向移动
760
- if (gameState.keys.ArrowLeft) {
761
  gameState.plane.rotation = Math.max(gameState.plane.rotation - 2, -20);
762
  gameState.plane.velocity = Math.max(gameState.plane.velocity - 0.5, -5);
763
- } else if (gameState.keys.ArrowRight) {
764
  gameState.plane.rotation = Math.min(gameState.plane.rotation + 2, 20);
765
  gameState.plane.velocity = Math.min(gameState.plane.velocity + 0.5, 5);
766
  } else {
@@ -772,9 +1083,9 @@
772
  }
773
 
774
  // 更新垂直方向移动
775
- if (gameState.keys.ArrowUp) {
776
  gameState.plane.verticalVelocity = Math.max(gameState.plane.verticalVelocity - 0.5, -5);
777
- } else if (gameState.keys.ArrowDown) {
778
  gameState.plane.verticalVelocity = Math.min(gameState.plane.verticalVelocity + 0.5, 5);
779
  } else {
780
  // 如果没有按上下键,垂直速度逐渐归零
@@ -797,23 +1108,31 @@
797
  }
798
 
799
  // 生成新障碍物
800
- if (timestamp - gameState.lastObstacleTime > 1500 / gameState.difficulty) {
801
  createObstacle();
802
  gameState.lastObstacleTime = timestamp;
803
  }
804
 
805
  // 生成新云朵
806
- if (timestamp - gameState.lastCloudTime > 1000) {
807
  createCloud();
808
  gameState.lastCloudTime = timestamp;
809
  }
810
 
811
  // 生成新道具 (每5-8秒)
812
- if (timestamp - gameState.lastPowerupTime > (Math.random() * 3000 + 5000) / gameState.difficulty) {
813
  createPowerup();
814
  gameState.lastPowerupTime = timestamp;
815
  }
816
 
 
 
 
 
 
 
 
 
817
  // 更新特效
818
  gameState.effects = gameState.effects.filter(effect =>
819
  Date.now() - effect.startTime < effect.duration
@@ -870,11 +1189,12 @@
870
 
871
  if (missile.target.health <= 0) {
872
  missile.target.hit = true;
873
- gameState.score += missile.target.isLarge ? 30 : 15;
 
874
  updateUI();
875
 
876
- // 如果是大型障碍物,分裂成小型障碍物
877
- if (missile.target.isLarge) {
878
  for (let i = 0; i < 3; i++) {
879
  gameState.obstacles.push({
880
  x: missile.target.x + (Math.random() - 0.5) * 50,
@@ -887,13 +1207,21 @@
887
  type: missile.target.type,
888
  health: 1,
889
  maxHealth: 1,
890
- isLarge: false
 
 
891
  });
892
  }
893
  }
894
 
 
 
 
 
 
 
895
  // 创建碎片效果
896
- createDebris(missile.target, 12);
897
  }
898
 
899
  // 创建爆炸效果
@@ -909,13 +1237,13 @@
909
 
910
  // 更新云朵
911
  gameState.clouds.forEach(cloud => {
912
- cloud.x -= cloud.speed;
913
  });
914
  gameState.clouds = gameState.clouds.filter(cloud => cloud.x + cloud.size > 0);
915
 
916
  // 更新道具
917
  gameState.powerups.forEach(powerup => {
918
- powerup.x -= powerup.speed;
919
 
920
  // 检测与飞机的碰撞
921
  const planeRect = {
@@ -936,24 +1264,25 @@
936
  powerup.collected = true;
937
  powerup.type.effect(gameState);
938
  updateUI();
 
939
  }
940
  });
941
  gameState.powerups = gameState.powerups.filter(powerup =>
942
  powerup.x + powerup.size > 0 && !powerup.collected
943
  );
944
 
945
- // 更新子弹
946
  gameState.bullets.forEach(bullet => {
947
- bullet.x += bullet.speed;
948
  });
949
  gameState.bullets = gameState.bullets.filter(bullet => bullet.x < canvas.width);
950
 
951
  // 更新星星
952
  gameState.stars.forEach(star => {
953
- star.x -= star.speed;
954
  star.rotation += star.rotationSpeed;
955
 
956
- // 检测碰撞
957
  const planeRect = {
958
  x: gameState.plane.x - gameState.plane.width / 2,
959
  y: gameState.plane.y - gameState.plane.height / 2,
@@ -970,15 +1299,26 @@
970
 
971
  if (checkCollision(planeRect, starRect) && !gameState.gameOver) {
972
  star.collected = true;
973
- gameState.score += 10;
 
974
  updateUI();
 
975
  }
976
  });
977
  gameState.stars = gameState.stars.filter(star => star.x + star.size > 0 && !star.collected);
978
 
979
  // 更新障碍物
 
980
  gameState.obstacles.forEach(obstacle => {
981
- obstacle.x -= obstacle.speed;
 
 
 
 
 
 
 
 
982
 
983
  // 检测与飞机的碰撞
984
  const planeRect = {
@@ -1014,7 +1354,7 @@
1014
  }
1015
  }
1016
 
1017
- // 检测与子弹的碰撞
1018
  if (!obstacle.hit) {
1019
  const bulletHits = [];
1020
 
@@ -1026,25 +1366,20 @@
1026
  height: bullet.size
1027
  };
1028
 
1029
- const obstacleCollisionRect = {
1030
- x: obstacle.x - obstacle.collisionWidth / 2,
1031
- y: obstacle.y - obstacle.collisionHeight / 2,
1032
- width: obstacle.collisionWidth,
1033
- height: obstacle.collisionHeight
1034
- };
1035
-
1036
- if (checkCollision(bulletRect, obstacleCollisionRect)) {
1037
  obstacle.health -= bullet.damage;
1038
  bulletHits.push(bulletIndex);
1039
  createExplosion(bullet.x, bullet.y);
1040
 
1041
  if (obstacle.health <= 0) {
1042
  obstacle.hit = true;
1043
- gameState.score += obstacle.isLarge ? 30 : 15; // 大型障碍物更多分
 
1044
  updateUI();
 
1045
 
1046
- // 如果是大型障碍物,分裂成小型障碍物
1047
- if (obstacle.isLarge) {
1048
  for (let i = 0; i < 3; i++) {
1049
  gameState.obstacles.push({
1050
  x: obstacle.x + (Math.random() - 0.5) * 50,
@@ -1057,17 +1392,37 @@
1057
  type: obstacle.type,
1058
  health: 1,
1059
  maxHealth: 1,
1060
- isLarge: false
 
 
1061
  });
1062
  }
1063
  }
1064
 
 
 
 
 
 
 
1065
  // 创建碎片效果
1066
- createDebris(obstacle, 8);
 
 
 
 
 
 
1067
  }
1068
  }
1069
  });
1070
 
 
 
 
 
 
 
1071
  // 移除已经击中的子弹
1072
  for (let i = bulletHits.length - 1; i >= 0; i--) {
1073
  gameState.bullets.splice(bulletHits[i], 1);
@@ -1076,6 +1431,53 @@
1076
  });
1077
  gameState.obstacles = gameState.obstacles.filter(obstacle => obstacle.x + obstacle.width > 0 && !obstacle.hit);
1078
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1079
  // 更新爆炸效果
1080
  gameState.explosions.forEach(explosion => {
1081
  explosion.size += 2;
@@ -1109,6 +1511,16 @@
1109
  });
1110
  });
1111
 
 
 
 
 
 
 
 
 
 
 
1112
  // 绘制碎片
1113
  gameState.debris.forEach(debris => {
1114
  ctx.save();
@@ -1134,21 +1546,21 @@
1134
  // 绘制闪光效果
1135
  ctx.beginPath();
1136
  ctx.arc(0, 0, powerup.size / 2, 0, Math.PI * 2);
1137
- const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, powerup.size / 2);
1138
- gradient.addColorStop(0, powerup.type.color);
1139
- gradient.addColorStop(1, 'rgba(255,255,255,0)');
1140
- ctx.fillStyle = gradient;
1141
  ctx.globalAlpha = 0.3;
1142
  ctx.fill();
1143
  ctx.globalAlpha = 1;
1144
 
1145
- // 绘制道具图标
1146
  ctx.fillStyle = powerup.type.color;
1147
  ctx.beginPath();
1148
  ctx.arc(0, 0, powerup.size / 2 - 3, 0, Math.PI * 2);
1149
  ctx.fill();
1150
 
1151
- // 绘制边框
1152
  ctx.strokeStyle = 'white';
1153
  ctx.lineWidth = 2;
1154
  ctx.stroke();
@@ -1190,21 +1602,21 @@
1190
  ctx.restore();
1191
  });
1192
 
1193
- // 绘制子弹
1194
  gameState.bullets.forEach(bullet => {
1195
- const gradient = ctx.createRadialGradient(
1196
  bullet.x, bullet.y, 0,
1197
  bullet.x, bullet.y, bullet.size
1198
  );
1199
- gradient.addColorStop(0, '#ff0');
1200
- gradient.addColorStop(1, '#f80');
1201
 
1202
  ctx.beginPath();
1203
  ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2);
1204
- ctx.fillStyle = gradient;
1205
  ctx.fill();
1206
 
1207
- // 子弹尾迹
1208
  ctx.beginPath();
1209
  ctx.moveTo(bullet.x - bullet.speed, bullet.y);
1210
  ctx.lineTo(bullet.x, bullet.y);
@@ -1213,6 +1625,17 @@
1213
  ctx.stroke();
1214
  });
1215
 
 
 
 
 
 
 
 
 
 
 
 
1216
  // 绘制星星
1217
  gameState.stars.forEach(star => {
1218
  ctx.save();
@@ -1245,10 +1668,10 @@
1245
  }
1246
  ctx.closePath();
1247
 
1248
- const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, star.size / 2);
1249
- gradient.addColorStop(0, 'gold');
1250
- gradient.addColorStop(1, 'yellow');
1251
- ctx.fillStyle = gradient;
1252
  ctx.shadowColor = 'yellow';
1253
  ctx.shadowBlur = 10;
1254
  ctx.fill();
@@ -1256,122 +1679,194 @@
1256
  ctx.restore();
1257
  });
1258
 
1259
- // 绘制障碍物
1260
  gameState.obstacles.forEach(obstacle => {
1261
  ctx.save();
1262
  ctx.translate(obstacle.x, obstacle.y);
1263
 
1264
  if (obstacle.type === 'rectangle') {
1265
- // 绘制健康条 (如果是矩形障碍物)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1266
  if (obstacle.health < obstacle.maxHealth) {
1267
  const healthBarWidth = 20;
1268
  ctx.fillStyle = 'red';
1269
- ctx.fillRect(
1270
- -healthBarWidth / 2,
1271
- -obstacle.height / 2 - 15,
1272
- healthBarWidth,
1273
- 5
1274
- );
1275
  ctx.fillStyle = 'lime';
1276
  ctx.fillRect(
1277
  -healthBarWidth / 2,
1278
- -obstacle.height / 2 - 15,
1279
  healthBarWidth * (obstacle.health / obstacle.maxHealth),
1280
- 5
1281
  );
1282
  }
1283
 
1284
- ctx.fillStyle = obstacle.isLarge ? '#333' : '#555';
1285
- ctx.fillRect(
1286
- -obstacle.width / 2,
1287
- -obstacle.height / 2,
1288
- obstacle.width,
1289
- obstacle.height
1290
- );
1291
-
1292
- // 添加一些细节
1293
- ctx.fillStyle = obstacle.isLarge ? '#222' : '#444';
1294
- ctx.fillRect(
1295
- -obstacle.width / 2 + 5,
1296
- -obstacle.height / 2 + 5,
1297
- obstacle.width - 10,
1298
- obstacle.height - 10
1299
- );
1300
-
1301
- // 添加裂缝效果 (对于受损的大型障碍物)
1302
- if (obstacle.isLarge && obstacle.health < obstacle.maxHealth) {
1303
- ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)';
1304
- ctx.lineWidth = 2;
1305
- for (let i = 0; i < 3; i++) {
1306
- ctx.beginPath();
1307
- ctx.moveTo(
1308
- -obstacle.width / 2 + Math.random() * obstacle.width,
1309
- -obstacle.height / 2 + Math.random() * obstacle.height / 3
1310
- );
1311
- ctx.lineTo(
1312
- -obstacle.width / 2 + Math.random() * obstacle.width,
1313
- obstacle.height / 2 - Math.random() * obstacle.height / 3
1314
- );
1315
- ctx.stroke();
1316
- }
1317
- }
1318
- } else {
1319
  ctx.beginPath();
1320
- ctx.arc(0, 0, obstacle.width / 2, 0, Math.PI * 2);
1321
- ctx.fillStyle = '#555';
1322
  ctx.fill();
1323
 
1324
- // 添加一些细节
1325
  ctx.beginPath();
1326
- ctx.arc(0, 0, obstacle.width / 2 - 5, 0, Math.PI * 2);
1327
- ctx.fillStyle = '#444';
1328
  ctx.fill();
1329
 
1330
- // 添加裂缝效果 (对于受损的大型障碍物)
1331
- if (obstacle.isLarge && obstacle.health < obstacle.maxHealth) {
1332
- ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)';
1333
- ctx.lineWidth = 2;
1334
- for (let i = 0; i < 3; i++) {
1335
- ctx.beginPath();
1336
- ctx.moveTo(
1337
- Math.cos(i * 2) * obstacle.width / 4,
1338
- Math.sin(i * 2) * obstacle.width / 4
1339
- );
1340
- ctx.lineTo(
1341
- Math.cos(i * 2 + 1) * obstacle.width / 3,
1342
- Math.sin(i * 2 + 1) * obstacle.width / 3
1343
- );
1344
- ctx.stroke();
1345
- }
1346
  }
1347
  }
1348
 
1349
- // 调试用 - 显示碰撞体积
1350
- if (false) { // 设置为true可以显示碰撞体积
1351
- ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)';
1352
- ctx.lineWidth = 2;
1353
- ctx.beginPath();
1354
- if (obstacle.type === 'rectangle') {
1355
- ctx.rect(
1356
- -obstacle.collisionWidth / 2,
1357
- -obstacle.collisionHeight / 2,
1358
- obstacle.collisionWidth,
1359
- obstacle.collisionHeight
1360
- );
1361
- } else {
1362
- ctx.arc(
1363
- 0, 0,
1364
- obstacle.collisionWidth / 2,
1365
- 0, Math.PI * 2
1366
- );
1367
- }
1368
- ctx.stroke();
1369
- }
1370
-
1371
  ctx.restore();
1372
  });
1373
 
1374
- // 绘制飞机
1375
  ctx.save();
1376
  ctx.translate(gameState.plane.x, gameState.plane.y);
1377
  ctx.rotate(gameState.plane.rotation * Math.PI / 180);
@@ -1385,10 +1880,10 @@
1385
  ctx.stroke();
1386
 
1387
  // 保护罩光晕
1388
- const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 45);
1389
- gradient.addColorStop(0, 'rgba(0, 204, 255, 0.2)');
1390
- gradient.addColorStop(1, 'rgba(0, 204, 255, 0)');
1391
- ctx.fillStyle = gradient;
1392
  ctx.fill();
1393
  }
1394
 
@@ -1453,17 +1948,17 @@
1453
  ctx.save();
1454
  ctx.translate(explosion.x, explosion.y);
1455
 
1456
- const gradient = ctx.createRadialGradient(
1457
  0, 0, 0,
1458
  0, 0, explosion.size
1459
  );
1460
- gradient.addColorStop(0, `rgba(255, 100, 0, ${explosion.alpha})`);
1461
- gradient.addColorStop(0.5, `rgba(255, 200, 0, ${explosion.alpha * 0.6})`);
1462
- gradient.addColorStop(1, `rgba(255, 255, 255, 0)`);
1463
 
1464
  ctx.beginPath();
1465
  ctx.arc(0, 0, explosion.size, 0, Math.PI * 2);
1466
- ctx.fillStyle = gradient;
1467
  ctx.fill();
1468
 
1469
  ctx.restore();
@@ -1475,19 +1970,17 @@
1475
  const progress = timePassed / effect.duration;
1476
 
1477
  ctx.save();
1478
- ctx.translate(effect.x, effect.y - progress * 50); // 文字向上移动
1479
  ctx.globalAlpha = 1 - progress * 0.8;
1480
-
1481
  ctx.font = 'bold 20px Arial';
1482
  ctx.fillStyle = effect.color;
1483
  ctx.textAlign = 'center';
1484
  ctx.textBaseline = 'middle';
1485
  ctx.fillText(effect.text, 0, 0);
1486
-
1487
  ctx.restore();
1488
  });
1489
 
1490
- // 绘制速度线
1491
  if (gameState.speed > 120) {
1492
  for (let i = 0; i < 10; i++) {
1493
  const x = Math.random() * canvas.width;
@@ -1528,11 +2021,79 @@
1528
  'fas fa-bolt': 'f0e7',
1529
  'fas fa-rocket': 'f135',
1530
  'fas fa-shield-alt': 'f3ed',
1531
- 'fas fa-heart': 'f004'
 
 
 
1532
  };
1533
  return icons[iconClass] || 'f128'; // 默认返回问号图标
1534
  }
1535
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1536
  // 事件监听
1537
  window.addEventListener('resize', resizeCanvas);
1538
 
@@ -1679,6 +2240,16 @@
1679
 
1680
  // 初始调整画布大小
1681
  resizeCanvas();
 
 
 
 
1682
  </script>
1683
- <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=zdwalter/plane-fighter-2" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
1684
- </html>
 
 
 
 
 
 
 
10
  body {
11
  overflow: hidden;
12
  touch-action: none;
13
+ margin: 0;
14
+ padding: 0;
15
  }
16
  #gameCanvas {
17
  display: block;
 
147
  pointer-events: none;
148
  animation: pulse 1.5s infinite;
149
  }
150
+ .joystick {
151
+ position: absolute;
152
+ width: 100px;
153
+ height: 100px;
154
+ background: rgba(255, 255, 255, 0.2);
155
+ border-radius: 50%;
156
+ bottom: 30px;
157
+ left: 30px;
158
+ pointer-events: auto;
159
+ display: none;
160
+ }
161
+ .joystick-handle {
162
+ position: absolute;
163
+ width: 40px;
164
+ height: 40px;
165
+ background: rgba(255, 255, 255, 0.4);
166
+ border-radius: 50%;
167
+ top: 30px;
168
+ left: 30px;
169
+ }
170
+ .boss-health-bar {
171
+ position: absolute;
172
+ top: 10px;
173
+ left: 50%;
174
+ transform: translateX(-50%);
175
+ width: 200px;
176
+ height: 20px;
177
+ background: rgba(0, 0, 0, 0.5);
178
+ border-radius: 10px;
179
+ overflow: hidden;
180
+ }
181
+ .boss-health-fill {
182
+ height: 100%;
183
+ background: linear-gradient(to right, #ff0000, #ff9900);
184
+ width: 100%;
185
+ }
186
  </style>
187
  </head>
188
  <body class="bg-gray-900 text-white flex flex-col items-center justify-center h-screen">
 
197
  <button id="startButton" class="bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-3 px-8 rounded-full text-xl transition-all duration-300 transform hover:scale-105 pointer-events-auto">
198
  开始游戏
199
  </button>
200
+ <div class="mt-8 grid grid-cols-3 gap-4 text-left max-w-md px-8">
201
  <div class="flex items-center">
202
  <div class="powerup bg-red-500 mr-2"><i class="fas fa-bolt"></i></div>
203
  <span>火力增强</span>
 
214
  <div class="powerup bg-green-500 mr-2"><i class="fas fa-heart"></i></div>
215
  <span>恢复生命</span>
216
  </div>
217
+ <div class="flex items-center">
218
+ <div class="powerup bg-cyan-500 mr-2"><i class="fas fa-clock"></i></div>
219
+ <span>时间减速</span>
220
+ </div>
221
+ <div class="flex items-center">
222
+ <div class="powerup bg-orange-500 mr-2"><i class="fas fa-bomb"></i></div>
223
+ <span>清屏炸弹</span>
224
+ </div>
225
+ <div class="flex items-center">
226
+ <div class="powerup bg-pink-500 mr-2"><i class="fas fa-star"></i></div>
227
+ <span>双倍分数</span>
228
+ </div>
229
+ </div>
230
+ <div class="mt-4 text-sm text-gray-300">
231
+ 最高分: <span id="highScoreDisplay">0</span>
232
  </div>
233
  </div>
234
 
 
265
  <!-- 这里会被JavaScript动态填充 -->
266
  </div>
267
 
268
+ <!-- Boss血条 -->
269
+ <div id="bossHealthBar" class="boss-health-bar hidden">
270
+ <div id="bossHealthFill" class="boss-health-fill"></div>
271
+ </div>
272
+
273
  <!-- 触摸控制按钮 -->
274
  <div id="leftBtn" class="control-btn hidden">
275
  <i class="fas fa-arrow-left"></i>
 
286
  <div id="fireBtn" class="control-btn hidden">
287
  <i class="fas fa-bolt text-yellow-400"></i>
288
  </div>
289
+
290
+ <!-- 虚拟摇杆 -->
291
+ <div id="joystick" class="joystick hidden">
292
+ <div id="joystickHandle" class="joystick-handle"></div>
293
+ </div>
294
  </div>
295
 
296
  <!-- 游戏结束界面 -->
 
299
  <div class="text-3xl mb-8">
300
  得分: <span id="finalScore" class="text-yellow-400">0</span>
301
  </div>
302
+ <div id="achievements" class="mb-4 text-center">
303
+ <!-- 成就提示 -->
304
+ </div>
305
  <button id="restartButton" class="bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-3 px-8 rounded-full text-xl transition-all duration-300 transform hover:scale-105 pointer-events-auto">
306
  再玩一次
307
  </button>
308
  </div>
309
  </div>
310
 
311
+ <audio id="shootSound" src="https://assets.mixkit.co/sfx/preview/mixkit-laser-weapon-shot-1680.mp3" preload="auto"></audio>
312
+ <audio id="explosionSound" src="https://assets.mixkit.co/sfx/preview/mixkit-explosion-impact-1684.mp3" preload="auto"></audio>
313
+ <audio id="powerupSound" src="https://assets.mixkit.co/sfx/preview/mixkit-achievement-bell-600.mp3" preload="auto"></audio>
314
+ <audio id="bgMusic" loop src="https://assets.mixkit.co/music/preview/mixkit-game-show-suspense-waiting-668.mp3" preload="auto"></audio>
315
+
316
  <script>
317
  // 游戏状态
318
  const gameState = {
 
323
  ammo: Infinity,
324
  speed: 100,
325
  difficulty: 1,
326
+ timeSlow: 0, // 时间减速结束时间
327
+ doubleScore: 0, // 双倍分数结束时间
328
+ bossActive: false, // Boss是否激活
329
+ bossHealth: 0, // Boss当前血量
330
+ bossMaxHealth: 0, // Boss最大血量
331
  plane: {
332
  x: 0,
333
  y: 0,
 
352
  explosions: [],
353
  bullets: [],
354
  debris: [],
355
+ powerups: [], // 道具
356
+ homingMissiles: [], // 跟踪导弹
357
+ effects: [], // 文字特效
358
+ particles: [], // 粒子效果
359
+ enemyBullets: [], // 敌人发射的子弹
360
  lastStarTime: 0,
361
  lastObstacleTime: 0,
362
  lastCloudTime: 0,
363
+ lastPowerupTime: 0,
364
+ lastBossSpawnTime: 0, // 上次生成Boss时间
365
  keys: {
366
  ArrowUp: false,
367
  ArrowDown: false,
 
369
  ArrowRight: false,
370
  Space: false
371
  },
372
+ isMobile: false,
373
+ joystickActive: false,
374
+ joystickAngle: 0,
375
+ joystickDistance: 0,
376
+ achievements: {
377
+ firstBlood: false, // 第一次击杀
378
+ combo5: false, // 连续5次击杀
379
+ noDamage: false, // 无伤通关
380
+ bossSlayer: false // 击败Boss
381
+ },
382
+ highScore: localStorage.getItem('highScore') || 0
383
  };
384
 
385
  // 道具类型
 
393
  game.plane.fireRate = 100; // 更快射击
394
  game.plane.powerups.rapidFire = Date.now() + POWERUP_TYPES.RAPID_FIRE.duration;
395
  createEffect('火力增强!', 'red', 1500);
396
+ playSound('powerupSound');
397
  }
398
  },
399
  HOMING_MISSILE: {
 
404
  effect: (game) => {
405
  game.plane.powerups.homingMissiles = Date.now() + POWERUP_TYPES.HOMING_MISSILE.duration;
406
  createEffect('跟踪导弹已激活!', 'purple', 1500);
407
+ playSound('powerupSound');
408
  }
409
  },
410
  SHIELD: {
 
416
  game.plane.hasShield = true;
417
  game.plane.shieldDuration = Date.now() + POWERUP_TYPES.SHIELD.duration;
418
  createEffect('保护罩已启用!', 'blue', 1500);
419
+ playSound('powerupSound');
420
  }
421
  },
422
  HEALTH: {
 
427
  game.lives = Math.min(game.lives + 1, 5); // 最多5条命
428
  updateUI();
429
  createEffect('生命值恢复!', 'green', 1500);
430
+ playSound('powerupSound');
431
+ }
432
+ },
433
+ TIME_SLOW: {
434
+ id: 'timeSlow',
435
+ icon: 'fas fa-clock',
436
+ color: 'cyan',
437
+ duration: 8000, // 8秒
438
+ effect: (game) => {
439
+ game.timeSlow = Date.now() + POWERUP_TYPES.TIME_SLOW.duration;
440
+ createEffect('时间减速!', 'cyan', 1500);
441
+ playSound('powerupSound');
442
+ }
443
+ },
444
+ CLEAR_SCREEN: {
445
+ id: 'clearScreen',
446
+ icon: 'fas fa-bomb',
447
+ color: 'orange',
448
+ effect: (game) => {
449
+ // 清除所有障碍物
450
+ game.obstacles.forEach(obstacle => {
451
+ createExplosion(obstacle.x, obstacle.y);
452
+ createDebris(obstacle, 8);
453
+ });
454
+ game.obstacles = [];
455
+ createEffect('清屏炸弹!', 'orange', 1500);
456
+ playSound('powerupSound');
457
+ }
458
+ },
459
+ DOUBLE_SCORE: {
460
+ id: 'doubleScore',
461
+ icon: 'fas fa-star',
462
+ color: 'pink',
463
+ duration: 10000, // 10秒
464
+ effect: (game) => {
465
+ game.doubleScore = Date.now() + POWERUP_TYPES.DOUBLE_SCORE.duration;
466
+ createEffect('双倍分数!', 'pink', 1500);
467
+ playSound('powerupSound');
468
  }
469
  }
470
  };
 
488
  const upBtn = document.getElementById('upBtn');
489
  const downBtn = document.getElementById('downBtn');
490
  const fireBtn = document.getElementById('fireBtn');
491
+ const joystick = document.getElementById('joystick');
492
+ const joystickHandle = document.getElementById('joystickHandle');
493
+ const bossHealthBar = document.getElementById('bossHealthBar');
494
+ const bossHealthFill = document.getElementById('bossHealthFill');
495
+ const achievementsDisplay = document.getElementById('achievements');
496
+ const highScoreDisplay = document.getElementById('highScoreDisplay');
497
+
498
+ // 音效
499
+ const shootSound = document.getElementById('shootSound');
500
+ const explosionSound = document.getElementById('explosionSound');
501
+ const powerupSound = document.getElementById('powerupSound');
502
+ const bgMusic = document.getElementById('bgMusic');
503
+
504
+ // 播放音效
505
+ function playSound(soundElement) {
506
+ if (soundElement === 'bgMusic') {
507
+ bgMusic.currentTime = 0;
508
+ bgMusic.play().catch(e => console.log('Autoplay prevented:', e));
509
+ } else {
510
+ const sound = document.getElementById(soundElement);
511
+ sound.currentTime = 0;
512
+ sound.play();
513
+ }
514
+ }
515
 
516
  // 检测是否移动设备
517
  function detectMobile() {
 
541
  });
542
  }
543
 
544
+ // 创建粒子效果
545
+ function createParticles(x, y, count, color) {
546
+ for (let i = 0; i < count; i++) {
547
+ gameState.particles.push({
548
+ x,
549
+ y,
550
+ size: Math.random() * 5 + 2,
551
+ speedX: (Math.random() - 0.5) * 4,
552
+ speedY: (Math.random() - 0.5) * 4,
553
+ color,
554
+ life: 100
555
+ });
556
+ }
557
+ }
558
+
559
  // 更新UI
560
  function updateUI() {
561
  scoreDisplay.textContent = gameState.score;
562
  livesDisplay.textContent = gameState.lives;
563
  speedDisplay.textContent = Math.floor(gameState.speed);
564
  ammoDisplay.textContent = gameState.ammo === Infinity ? "∞" : gameState.ammo;
565
+ highScoreDisplay.textContent = gameState.highScore;
566
 
567
  // 更新道具状态显示
568
  powerupStatus.innerHTML = '';
 
593
  </div>
594
  `;
595
  }
596
+
597
+ if (gameState.timeSlow > Date.now()) {
598
+ const timeLeft = Math.ceil((gameState.timeSlow - Date.now()) / 1000);
599
+ powerupStatus.innerHTML += `
600
+ <div class="bg-black bg-opacity-50 px-3 py-1 rounded-lg flex items-center" title="时间减速 (${timeLeft}s)">
601
+ <i class="fas fa-clock text-cyan-500 mr-2"></i>
602
+ </div>
603
+ `;
604
+ }
605
+
606
+ if (gameState.doubleScore > Date.now()) {
607
+ const timeLeft = Math.ceil((gameState.doubleScore - Date.now()) / 1000);
608
+ powerupStatus.innerHTML += `
609
+ <div class="bg-black bg-opacity-50 px-3 py-1 rounded-lg flex items-center" title="双倍分数 (${timeLeft}s)">
610
+ <i class="fas fa-star text-pink-500 mr-2"></i>
611
+ </div>
612
+ `;
613
+ }
614
+
615
+ // 更新Boss血条
616
+ if (gameState.bossActive) {
617
+ bossHealthBar.classList.remove('hidden');
618
+ bossHealthFill.style.width = `${(gameState.bossHealth / gameState.bossMaxHealth) * 100}%`;
619
+ } else {
620
+ bossHealthBar.classList.add('hidden');
621
+ }
622
  }
623
 
624
  // 初始化游戏
 
632
  gameState.ammo = Infinity;
633
  gameState.speed = 100;
634
  gameState.difficulty = 1;
635
+ gameState.timeSlow = 0;
636
+ gameState.doubleScore = 0;
637
+ gameState.bossActive = false;
638
+ gameState.bossHealth = 0;
639
+ gameState.bossMaxHealth = 0;
640
  gameState.plane = {
641
  x: canvas.width / 2,
642
  y: canvas.height / 2,
 
663
  gameState.debris = [];
664
  gameState.powerups = [];
665
  gameState.homingMissiles = [];
666
+ gameState.enemyBullets = [];
667
  gameState.effects = [];
668
+ gameState.particles = [];
669
  gameState.lastStarTime = 0;
670
  gameState.lastObstacleTime = 0;
671
  gameState.lastCloudTime = 0;
672
  gameState.lastPowerupTime = 0;
673
+ gameState.lastBossSpawnTime = 0;
674
+ gameState.achievements = {
675
+ firstBlood: false,
676
+ combo5: false,
677
+ noDamage: false,
678
+ bossSlayer: false
679
+ };
680
 
681
  startScreen.classList.add('hidden');
682
  gameOverScreen.classList.add('hidden');
 
684
 
685
  // 显示触摸控制按钮(如果是移动设备)
686
  if (gameState.isMobile) {
687
+ leftBtn.classList.add('hidden');
688
+ rightBtn.classList.add('hidden');
689
+ upBtn.classList.add('hidden');
690
+ downBtn.classList.add('hidden');
691
  fireBtn.classList.remove('hidden');
692
+ joystick.classList.remove('hidden');
693
  }
694
 
695
  updateUI();
696
  createInitialClouds();
697
+ playSound('bgMusic');
698
  requestAnimationFrame(gameLoop);
699
  }
700
 
 
742
  });
743
  }
744
 
745
+ // 创建障碍物(敌人)
746
  function createObstacle() {
747
  const width = Math.random() * 80 + 40;
748
  const height = Math.random() * 80 + 40;
 
767
  type,
768
  health,
769
  maxHealth: health,
770
+ isLarge: width > 80,
771
+ isBoss: false, // 普通敌人
772
+ lastShotTime: 0 // 用于控制敌人射击间隔
773
  });
774
  }
775
 
776
+ // 创建Boss障碍物
777
+ function createBoss() {
778
+ const width = 150;
779
+ const height = 150;
780
+ const x = canvas.width + width;
781
+ const y = canvas.height / 2 - height / 2;
782
+ const speed = 1 + gameState.speed / 100;
783
+ const health = 20 + Math.floor(gameState.score / 5000) * 5;
784
+
785
+ gameState.bossActive = true;
786
+ gameState.bossHealth = health;
787
+ gameState.bossMaxHealth = health;
788
+ gameState.lastBossSpawnTime = Date.now();
789
+
790
+ gameState.obstacles.push({
791
+ x,
792
+ y,
793
+ width,
794
+ height,
795
+ collisionWidth: width,
796
+ collisionHeight: height,
797
+ speed,
798
+ type: 'rectangle',
799
+ health,
800
+ maxHealth: health,
801
+ isLarge: true,
802
+ isBoss: true,
803
+ lastShotTime: 0
804
+ });
805
+
806
+ createEffect('BOSS出现!', 'red', 2000);
807
+ }
808
+
809
  // 创建道具
810
  function createPowerup() {
811
  const size = 40;
 
878
  }
879
  }
880
 
881
+ // 创建子弹(玩家发射)
882
  function createBullet() {
883
  if (gameState.ammo <= 0) return false; // 没有弹药了
884
 
885
  const size = gameState.plane.powerups.rapidFire > Date.now() ? 10 : 8; // 火力增强时子弹更大
886
+ const damage = gameState.plane.powerups.rapidFire > Date.now() ? 2 : 1; // 火力增强时伤害更高
887
+ const speed = gameState.plane.powerups.rapidFire > Date.now() ? 18 : 15; // 火力增强时速度更快
888
  const x = gameState.plane.x + 30; // 从飞机前端发射
889
  const y = gameState.plane.y;
890
 
 
896
  damage
897
  });
898
 
899
+ // 如果有跟踪导弹能力且冷却结束,随机几率触发
900
  if (gameState.plane.powerups.homingMissiles > Date.now() &&
901
  Math.random() > 0.7) { // 70%概率发射跟踪导弹
902
  requestAnimationFrame(createHomingMissile);
 
907
  gameState.ammo--;
908
  }
909
 
910
+ playSound('shootSound');
911
  updateUI();
912
 
913
  return true;
 
922
  maxSize: Math.random() * 40 + 40,
923
  alpha: 1
924
  });
925
+ createParticles(x, y, 20, '#ff6600');
926
+ playSound('explosionSound');
927
+ }
928
+
929
+ // 敌人(障碍物)发射子弹
930
+ function createEnemyBullet(obstacle) {
931
+ const angle = Math.atan2(gameState.plane.y - obstacle.y, gameState.plane.x - obstacle.x);
932
+ gameState.enemyBullets.push({
933
+ x: obstacle.x,
934
+ y: obstacle.y,
935
+ size: obstacle.isBoss ? 10 : 6,
936
+ speed: obstacle.isBoss ? 5 : 4,
937
+ angle,
938
+ damage: obstacle.isBoss ? 2 : 1,
939
+ hit: false
940
+ });
941
+ obstacle.lastShotTime = Date.now();
942
  }
943
 
944
+ // 碰撞检测
945
  function checkCollision(rect1, rect2) {
946
  return (
947
  rect1.x < rect2.x + rect2.width &&
 
958
  gameOverScreen.classList.remove('hidden');
959
  finalScore.textContent = gameState.score;
960
 
961
+ // 更新最高分
962
+ if (gameState.score > gameState.highScore) {
963
+ gameState.highScore = gameState.score;
964
+ localStorage.setItem('highScore', gameState.highScore);
965
+ achievementsDisplay.innerHTML += `<div class="text-yellow-400 mb-2">🎉 新纪录!</div>`;
966
+ }
967
+
968
+ // 显示成就(这里可根据需求自定义条件)
969
+ if (!gameState.achievements.firstBlood) {
970
+ achievementsDisplay.innerHTML += `<div class="text-green-400 mb-2">🏆 首次击杀!</div>`;
971
+ }
972
+ if (!gameState.achievements.combo5) {
973
+ achievementsDisplay.innerHTML += `<div class="text-blue-400 mb-2">🔥 连续5次击杀!</div>`;
974
+ }
975
+ if (!gameState.achievements.noDamage && gameState.lives === 3) {
976
+ achievementsDisplay.innerHTML += `<div class="text-purple-400 mb-2">🛡️ 无伤通关!</div>`;
977
+ }
978
+ if (!gameState.achievements.bossSlayer && gameState.bossActive) {
979
+ achievementsDisplay.innerHTML += `<div class="text-red-400 mb-2">👹 Boss杀手!</div>`;
980
+ }
981
+
982
  // 隐藏触摸控制按钮
983
  leftBtn.classList.add('hidden');
984
  rightBtn.classList.add('hidden');
985
  upBtn.classList.add('hidden');
986
  downBtn.classList.add('hidden');
987
  fireBtn.classList.add('hidden');
988
+ joystick.classList.add('hidden');
989
+
990
+ bgMusic.pause();
991
  }
992
 
993
  // 游戏主循环
 
1012
  // 随着分数增加难度
1013
  gameState.difficulty = 1 + Math.min(gameState.score / 1000, 3);
1014
 
1015
+ // 检查Boss生成条件
1016
+ if (
1017
+ !gameState.bossActive &&
1018
+ gameState.score > 0 &&
1019
+ gameState.score % 5000 === 0 &&
1020
+ timestamp - gameState.lastBossSpawnTime > 30000
1021
+ ) {
1022
+ createBoss();
1023
+ }
1024
+
1025
+ // 时间减速因子
1026
+ const timeSlowFactor = gameState.timeSlow > Date.now() ? 0.5 : 1;
1027
+
1028
+ // 处理开火(玩家)
1029
  if ((gameState.keys.Space || gameState.isFiring) &&
1030
+ timestamp - gameState.plane.lastFireTime > gameState.plane.fireRate) {
1031
  createBullet();
1032
  gameState.plane.lastFireTime = timestamp;
1033
  }
1034
 
1035
+ // 检查玩家道具过期
1036
  if (gameState.plane.powerups.rapidFire > 0 && gameState.plane.powerups.rapidFire < Date.now()) {
1037
  gameState.plane.powerups.rapidFire = 0;
1038
  gameState.plane.fireRate = 200; // 恢复默认射击速度
 
1049
  createEffect('保护罩消失', 'blue', 1500);
1050
  }
1051
 
1052
+ if (gameState.timeSlow > 0 && gameState.timeSlow < Date.now()) {
1053
+ gameState.timeSlow = 0;
1054
+ createEffect('时间恢复正常', 'cyan', 1500);
1055
+ }
1056
+
1057
+ if (gameState.doubleScore > 0 && gameState.doubleScore < Date.now()) {
1058
+ gameState.doubleScore = 0;
1059
+ createEffect('双倍分数结束', 'pink', 1500);
1060
+ }
1061
+
1062
+ // 更新飞机速度(上下加速模拟)
1063
  if (gameState.keys.ArrowUp || gameState.keys.ArrowDown) {
1064
+ gameState.speed = Math.max(
1065
+ 50,
1066
+ Math.min(200, gameState.speed + (gameState.keys.ArrowUp ? 0.5 : -0.5))
 
1067
  );
1068
  }
1069
 
1070
  // 更新水平方向移动
1071
+ if (gameState.keys.ArrowLeft || gameState.joystickAngle < -Math.PI/4) {
1072
  gameState.plane.rotation = Math.max(gameState.plane.rotation - 2, -20);
1073
  gameState.plane.velocity = Math.max(gameState.plane.velocity - 0.5, -5);
1074
+ } else if (gameState.keys.ArrowRight || gameState.joystickAngle > Math.PI/4) {
1075
  gameState.plane.rotation = Math.min(gameState.plane.rotation + 2, 20);
1076
  gameState.plane.velocity = Math.min(gameState.plane.velocity + 0.5, 5);
1077
  } else {
 
1083
  }
1084
 
1085
  // 更新垂直方向移动
1086
+ if (gameState.keys.ArrowUp || (gameState.joystickActive && gameState.joystickAngle < -Math.PI/4 && gameState.joystickAngle > -3*Math.PI/4)) {
1087
  gameState.plane.verticalVelocity = Math.max(gameState.plane.verticalVelocity - 0.5, -5);
1088
+ } else if (gameState.keys.ArrowDown || (gameState.joystickActive && gameState.joystickAngle > Math.PI/4 && gameState.joystickAngle < 3*Math.PI/4)) {
1089
  gameState.plane.verticalVelocity = Math.min(gameState.plane.verticalVelocity + 0.5, 5);
1090
  } else {
1091
  // 如果没有按上下键,垂直速度逐渐归零
 
1108
  }
1109
 
1110
  // 生成新障碍物
1111
+ if (timestamp - gameState.lastObstacleTime > 1500 / gameState.difficulty * timeSlowFactor) {
1112
  createObstacle();
1113
  gameState.lastObstacleTime = timestamp;
1114
  }
1115
 
1116
  // 生成新云朵
1117
+ if (timestamp - gameState.lastCloudTime > 1000 * timeSlowFactor) {
1118
  createCloud();
1119
  gameState.lastCloudTime = timestamp;
1120
  }
1121
 
1122
  // 生成新道具 (每5-8秒)
1123
+ if (timestamp - gameState.lastPowerupTime > (Math.random() * 3000 + 5000) / gameState.difficulty * timeSlowFactor) {
1124
  createPowerup();
1125
  gameState.lastPowerupTime = timestamp;
1126
  }
1127
 
1128
+ // 更新粒子
1129
+ gameState.particles.forEach(particle => {
1130
+ particle.x += particle.speedX;
1131
+ particle.y += particle.speedY;
1132
+ particle.life--;
1133
+ });
1134
+ gameState.particles = gameState.particles.filter(p => p.life > 0);
1135
+
1136
  // 更新特效
1137
  gameState.effects = gameState.effects.filter(effect =>
1138
  Date.now() - effect.startTime < effect.duration
 
1189
 
1190
  if (missile.target.health <= 0) {
1191
  missile.target.hit = true;
1192
+ const scoreBonus = missile.target.isBoss ? 500 : (missile.target.isLarge ? 30 : 15);
1193
+ gameState.score += gameState.doubleScore > Date.now() ? scoreBonus * 2 : scoreBonus;
1194
  updateUI();
1195
 
1196
+ // 如果是大型障碍物但不是Boss,分裂成小型障碍物
1197
+ if (missile.target.isLarge && !missile.target.isBoss) {
1198
  for (let i = 0; i < 3; i++) {
1199
  gameState.obstacles.push({
1200
  x: missile.target.x + (Math.random() - 0.5) * 50,
 
1207
  type: missile.target.type,
1208
  health: 1,
1209
  maxHealth: 1,
1210
+ isLarge: false,
1211
+ isBoss: false,
1212
+ lastShotTime: 0
1213
  });
1214
  }
1215
  }
1216
 
1217
+ // 如果是Boss,标记Boss已击败
1218
+ if (missile.target.isBoss) {
1219
+ gameState.bossActive = false;
1220
+ gameState.achievements.bossSlayer = true;
1221
+ }
1222
+
1223
  // 创建碎片效果
1224
+ createDebris(missile.target, missile.target.isBoss ? 30 : 12);
1225
  }
1226
 
1227
  // 创建爆炸效果
 
1237
 
1238
  // 更新云朵
1239
  gameState.clouds.forEach(cloud => {
1240
+ cloud.x -= cloud.speed * timeSlowFactor;
1241
  });
1242
  gameState.clouds = gameState.clouds.filter(cloud => cloud.x + cloud.size > 0);
1243
 
1244
  // 更新道具
1245
  gameState.powerups.forEach(powerup => {
1246
+ powerup.x -= powerup.speed * timeSlowFactor;
1247
 
1248
  // 检测与飞机的碰撞
1249
  const planeRect = {
 
1264
  powerup.collected = true;
1265
  powerup.type.effect(gameState);
1266
  updateUI();
1267
+ createParticles(powerup.x, powerup.y, 15, powerup.type.color);
1268
  }
1269
  });
1270
  gameState.powerups = gameState.powerups.filter(powerup =>
1271
  powerup.x + powerup.size > 0 && !powerup.collected
1272
  );
1273
 
1274
+ // 更新玩家子弹
1275
  gameState.bullets.forEach(bullet => {
1276
+ bullet.x += bullet.speed * timeSlowFactor;
1277
  });
1278
  gameState.bullets = gameState.bullets.filter(bullet => bullet.x < canvas.width);
1279
 
1280
  // 更新星星
1281
  gameState.stars.forEach(star => {
1282
+ star.x -= star.speed * timeSlowFactor;
1283
  star.rotation += star.rotationSpeed;
1284
 
1285
+ // 检测与飞机的碰撞
1286
  const planeRect = {
1287
  x: gameState.plane.x - gameState.plane.width / 2,
1288
  y: gameState.plane.y - gameState.plane.height / 2,
 
1299
 
1300
  if (checkCollision(planeRect, starRect) && !gameState.gameOver) {
1301
  star.collected = true;
1302
+ const scoreBonus = 10;
1303
+ gameState.score += gameState.doubleScore > Date.now() ? scoreBonus * 2 : scoreBonus;
1304
  updateUI();
1305
+ createParticles(star.x, star.y, 10, 'gold');
1306
  }
1307
  });
1308
  gameState.stars = gameState.stars.filter(star => star.x + star.size > 0 && !star.collected);
1309
 
1310
  // 更新障碍物
1311
+ let comboCount = 0; // 连续击杀计数器
1312
  gameState.obstacles.forEach(obstacle => {
1313
+ obstacle.x -= obstacle.speed * timeSlowFactor;
1314
+
1315
+ // 敌人射击逻辑:当在屏幕内时按间隔发射子弹
1316
+ if (!obstacle.hit && obstacle.x < canvas.width && obstacle.x > 0) {
1317
+ const shotCooldown = obstacle.isBoss ? 1500 : 3000;
1318
+ if (Date.now() - obstacle.lastShotTime > shotCooldown) {
1319
+ createEnemyBullet(obstacle);
1320
+ }
1321
+ }
1322
 
1323
  // 检测与飞机的碰撞
1324
  const planeRect = {
 
1354
  }
1355
  }
1356
 
1357
+ // 检测与玩家子弹的碰撞
1358
  if (!obstacle.hit) {
1359
  const bulletHits = [];
1360
 
 
1366
  height: bullet.size
1367
  };
1368
 
1369
+ if (checkCollision(bulletRect, obstacleRect)) {
 
 
 
 
 
 
 
1370
  obstacle.health -= bullet.damage;
1371
  bulletHits.push(bulletIndex);
1372
  createExplosion(bullet.x, bullet.y);
1373
 
1374
  if (obstacle.health <= 0) {
1375
  obstacle.hit = true;
1376
+ const scoreBonus = obstacle.isBoss ? 500 : (obstacle.isLarge ? 30 : 15);
1377
+ gameState.score += gameState.doubleScore > Date.now() ? scoreBonus * 2 : scoreBonus;
1378
  updateUI();
1379
+ comboCount++;
1380
 
1381
+ // 如果是大型障碍物但不是Boss,分裂成小型障碍物
1382
+ if (obstacle.isLarge && !obstacle.isBoss) {
1383
  for (let i = 0; i < 3; i++) {
1384
  gameState.obstacles.push({
1385
  x: obstacle.x + (Math.random() - 0.5) * 50,
 
1392
  type: obstacle.type,
1393
  health: 1,
1394
  maxHealth: 1,
1395
+ isLarge: false,
1396
+ isBoss: false,
1397
+ lastShotTime: 0
1398
  });
1399
  }
1400
  }
1401
 
1402
+ // 如果是Boss,标记Boss已击败
1403
+ if (obstacle.isBoss) {
1404
+ gameState.bossActive = false;
1405
+ gameState.achievements.bossSlayer = true;
1406
+ }
1407
+
1408
  // 创建碎片效果
1409
+ createDebris(obstacle, obstacle.isBoss ? 30 : 8);
1410
+
1411
+ // 首次击杀成就
1412
+ if (!gameState.achievements.firstBlood) {
1413
+ gameState.achievements.firstBlood = true;
1414
+ createEffect('首次击杀!', 'green', 2000);
1415
+ }
1416
  }
1417
  }
1418
  });
1419
 
1420
+ // 连续击杀成就
1421
+ if (comboCount >= 5 && !gameState.achievements.combo5) {
1422
+ gameState.achievements.combo5 = true;
1423
+ createEffect('连续5次击杀!', 'blue', 2000);
1424
+ }
1425
+
1426
  // 移除已经击中的子弹
1427
  for (let i = bulletHits.length - 1; i >= 0; i--) {
1428
  gameState.bullets.splice(bulletHits[i], 1);
 
1431
  });
1432
  gameState.obstacles = gameState.obstacles.filter(obstacle => obstacle.x + obstacle.width > 0 && !obstacle.hit);
1433
 
1434
+ // 更新敌人子弹
1435
+ gameState.enemyBullets.forEach(bullet => {
1436
+ bullet.x += Math.cos(bullet.angle) * bullet.speed * timeSlowFactor;
1437
+ bullet.y += Math.sin(bullet.angle) * bullet.speed * timeSlowFactor;
1438
+
1439
+ // 与玩家飞机碰撞
1440
+ const planeRect = {
1441
+ x: gameState.plane.x - gameState.plane.width / 2,
1442
+ y: gameState.plane.y - gameState.plane.height / 2,
1443
+ width: gameState.plane.width,
1444
+ height: gameState.plane.height
1445
+ };
1446
+ const bulletRect = {
1447
+ x: bullet.x - bullet.size / 2,
1448
+ y: bullet.y - bullet.size / 2,
1449
+ width: bullet.size,
1450
+ height: bullet.size
1451
+ };
1452
+ if (checkCollision(planeRect, bulletRect)) {
1453
+ // 如果没有护盾则受伤,否则仅销毁子弹
1454
+ if (!gameState.plane.hasShield || gameState.plane.shieldDuration < Date.now()) {
1455
+ gameState.lives -= bullet.damage;
1456
+ createExplosion(bullet.x, bullet.y);
1457
+ bullet.hit = true;
1458
+ if (gameState.lives <= 0) {
1459
+ endGame();
1460
+ } else {
1461
+ updateUI();
1462
+ }
1463
+ } else {
1464
+ createExplosion(bullet.x, bullet.y);
1465
+ bullet.hit = true;
1466
+ }
1467
+ }
1468
+
1469
+ // 子弹超出屏幕
1470
+ if (
1471
+ bullet.x < 0 ||
1472
+ bullet.x > canvas.width ||
1473
+ bullet.y < 0 ||
1474
+ bullet.y > canvas.height
1475
+ ) {
1476
+ bullet.hit = true;
1477
+ }
1478
+ });
1479
+ gameState.enemyBullets = gameState.enemyBullets.filter(b => !b.hit);
1480
+
1481
  // 更新爆炸效果
1482
  gameState.explosions.forEach(explosion => {
1483
  explosion.size += 2;
 
1511
  });
1512
  });
1513
 
1514
+ // 绘制粒子
1515
+ gameState.particles.forEach(particle => {
1516
+ ctx.beginPath();
1517
+ ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
1518
+ ctx.fillStyle = particle.color;
1519
+ ctx.globalAlpha = particle.life / 100;
1520
+ ctx.fill();
1521
+ ctx.globalAlpha = 1;
1522
+ });
1523
+
1524
  // 绘制碎片
1525
  gameState.debris.forEach(debris => {
1526
  ctx.save();
 
1546
  // 绘制闪光效果
1547
  ctx.beginPath();
1548
  ctx.arc(0, 0, powerup.size / 2, 0, Math.PI * 2);
1549
+ const gradP = ctx.createRadialGradient(0, 0, 0, 0, 0, powerup.size / 2);
1550
+ gradP.addColorStop(0, powerup.type.color);
1551
+ gradP.addColorStop(1, 'rgba(255,255,255,0)');
1552
+ ctx.fillStyle = gradP;
1553
  ctx.globalAlpha = 0.3;
1554
  ctx.fill();
1555
  ctx.globalAlpha = 1;
1556
 
1557
+ // 绘制道具图标背景
1558
  ctx.fillStyle = powerup.type.color;
1559
  ctx.beginPath();
1560
  ctx.arc(0, 0, powerup.size / 2 - 3, 0, Math.PI * 2);
1561
  ctx.fill();
1562
 
1563
+ // 边框
1564
  ctx.strokeStyle = 'white';
1565
  ctx.lineWidth = 2;
1566
  ctx.stroke();
 
1602
  ctx.restore();
1603
  });
1604
 
1605
+ // 绘制玩家子弹
1606
  gameState.bullets.forEach(bullet => {
1607
+ const grad = ctx.createRadialGradient(
1608
  bullet.x, bullet.y, 0,
1609
  bullet.x, bullet.y, bullet.size
1610
  );
1611
+ grad.addColorStop(0, '#ff0');
1612
+ grad.addColorStop(1, '#f80');
1613
 
1614
  ctx.beginPath();
1615
  ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2);
1616
+ ctx.fillStyle = grad;
1617
  ctx.fill();
1618
 
1619
+ // 子弹尾迹(简单绘制)
1620
  ctx.beginPath();
1621
  ctx.moveTo(bullet.x - bullet.speed, bullet.y);
1622
  ctx.lineTo(bullet.x, bullet.y);
 
1625
  ctx.stroke();
1626
  });
1627
 
1628
+ // 绘制敌人子弹
1629
+ gameState.enemyBullets.forEach(bullet => {
1630
+ ctx.save();
1631
+ ctx.translate(bullet.x, bullet.y);
1632
+ ctx.beginPath();
1633
+ ctx.arc(0, 0, bullet.size, 0, Math.PI * 2);
1634
+ ctx.fillStyle = 'rgba(255,0,0,0.8)';
1635
+ ctx.fill();
1636
+ ctx.restore();
1637
+ });
1638
+
1639
  // 绘制星星
1640
  gameState.stars.forEach(star => {
1641
  ctx.save();
 
1668
  }
1669
  ctx.closePath();
1670
 
1671
+ const gradStar = ctx.createRadialGradient(0, 0, 0, 0, 0, star.size / 2);
1672
+ gradStar.addColorStop(0, 'gold');
1673
+ gradStar.addColorStop(1, 'yellow');
1674
+ ctx.fillStyle = gradStar;
1675
  ctx.shadowColor = 'yellow';
1676
  ctx.shadowBlur = 10;
1677
  ctx.fill();
 
1679
  ctx.restore();
1680
  });
1681
 
1682
+ // 绘制障碍物(敌人)
1683
  gameState.obstacles.forEach(obstacle => {
1684
  ctx.save();
1685
  ctx.translate(obstacle.x, obstacle.y);
1686
 
1687
  if (obstacle.type === 'rectangle') {
1688
+ // 如果是Boss
1689
+ if (obstacle.isBoss) {
1690
+ // 画Boss的矩形身体
1691
+ // 绘制健康条
1692
+ if (obstacle.health < obstacle.maxHealth) {
1693
+ const healthBarWidth = 100;
1694
+ ctx.fillStyle = 'red';
1695
+ ctx.fillRect(
1696
+ -healthBarWidth / 2,
1697
+ -obstacle.height / 2 - 15,
1698
+ healthBarWidth,
1699
+ 5
1700
+ );
1701
+ ctx.fillStyle = 'purple';
1702
+ ctx.fillRect(
1703
+ -healthBarWidth / 2,
1704
+ -obstacle.height / 2 - 15,
1705
+ healthBarWidth * (obstacle.health / obstacle.maxHealth),
1706
+ 5
1707
+ );
1708
+ }
1709
+
1710
+ // 主体
1711
+ ctx.fillStyle = '#8B0000';
1712
+ ctx.fillRect(
1713
+ -obstacle.width / 2,
1714
+ -obstacle.height / 2,
1715
+ obstacle.width,
1716
+ obstacle.height
1717
+ );
1718
+
1719
+ ctx.fillStyle = '#600000';
1720
+ ctx.fillRect(
1721
+ -obstacle.width / 2 + 5,
1722
+ -obstacle.height / 2 + 5,
1723
+ obstacle.width - 10,
1724
+ obstacle.height - 10
1725
+ );
1726
+
1727
+ // 裂缝效果
1728
+ if (obstacle.health < obstacle.maxHealth) {
1729
+ ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)';
1730
+ ctx.lineWidth = 2;
1731
+ for (let i = 0; i < 10; i++) {
1732
+ ctx.beginPath();
1733
+ ctx.moveTo(
1734
+ -obstacle.width / 2 + Math.random() * obstacle.width,
1735
+ -obstacle.height / 2 + Math.random() * (obstacle.height / 3)
1736
+ );
1737
+ ctx.lineTo(
1738
+ -obstacle.width / 2 + Math.random() * obstacle.width,
1739
+ obstacle.height / 2 - Math.random() * (obstacle.height / 3)
1740
+ );
1741
+ ctx.stroke();
1742
+ }
1743
+ }
1744
+ // BOSS标记
1745
+ ctx.fillStyle = 'gold';
1746
+ ctx.font = 'bold 20px Arial';
1747
+ ctx.textAlign = 'center';
1748
+ ctx.textBaseline = 'middle';
1749
+ ctx.fillText('BOSS', 0, 0);
1750
+ }
1751
+ else {
1752
+ // 普通矩形敌人 -> 用小飞机造型来表示
1753
+ // 先画一个简化小飞机 (与玩家外观类似,但颜色不同)
1754
+ ctx.save();
1755
+ // 如果受损,可能需要画个小血条
1756
+ if (obstacle.health < obstacle.maxHealth) {
1757
+ const healthBarWidth = 20;
1758
+ ctx.fillStyle = 'red';
1759
+ ctx.fillRect(-healthBarWidth / 2, -obstacle.height / 2 - 10, healthBarWidth, 4);
1760
+ ctx.fillStyle = 'lime';
1761
+ ctx.fillRect(
1762
+ -healthBarWidth / 2,
1763
+ -obstacle.height / 2 - 10,
1764
+ healthBarWidth * (obstacle.health / obstacle.maxHealth),
1765
+ 4
1766
+ );
1767
+ }
1768
+
1769
+ // 敌机主体(蓝色机身)
1770
+ ctx.beginPath();
1771
+ ctx.moveTo(20, 0);
1772
+ ctx.lineTo(-15, -12);
1773
+ ctx.lineTo(-20, 0);
1774
+ ctx.lineTo(-15, 12);
1775
+ ctx.closePath();
1776
+ ctx.fillStyle = '#3498db';
1777
+ ctx.fill();
1778
+
1779
+ // 敌机窗户
1780
+ ctx.beginPath();
1781
+ ctx.arc(5, 0, 4, 0, Math.PI * 2);
1782
+ ctx.fillStyle = '#fff';
1783
+ ctx.fill();
1784
+
1785
+ // 敌机机翼
1786
+ ctx.beginPath();
1787
+ ctx.moveTo(3, 0);
1788
+ ctx.lineTo(-5, -15);
1789
+ ctx.lineTo(-12, -15);
1790
+ ctx.lineTo(-5, 0);
1791
+ ctx.closePath();
1792
+ ctx.fillStyle = '#2980b9';
1793
+ ctx.fill();
1794
+
1795
+ ctx.beginPath();
1796
+ ctx.moveTo(3, 0);
1797
+ ctx.lineTo(-5, 15);
1798
+ ctx.lineTo(-12, 15);
1799
+ ctx.lineTo(-5, 0);
1800
+ ctx.closePath();
1801
+ ctx.fillStyle = '#2980b9';
1802
+ ctx.fill();
1803
+
1804
+ // 尾翼
1805
+ ctx.beginPath();
1806
+ ctx.moveTo(-15, 0);
1807
+ ctx.lineTo(-20, -8);
1808
+ ctx.lineTo(-25, -8);
1809
+ ctx.lineTo(-20, 0);
1810
+ ctx.closePath();
1811
+ ctx.fillStyle = '#1c5982';
1812
+ ctx.fill();
1813
+
1814
+ ctx.beginPath();
1815
+ ctx.moveTo(-15, 0);
1816
+ ctx.lineTo(-20, 8);
1817
+ ctx.lineTo(-25, 8);
1818
+ ctx.lineTo(-20, 0);
1819
+ ctx.closePath();
1820
+ ctx.fillStyle = '#1c5982';
1821
+ ctx.fill();
1822
+
1823
+ ctx.restore();
1824
+ }
1825
+
1826
+ } else {
1827
+ // 圆形敌人 -> 绘制UFO形状
1828
+ // 小血条
1829
  if (obstacle.health < obstacle.maxHealth) {
1830
  const healthBarWidth = 20;
1831
  ctx.fillStyle = 'red';
1832
+ ctx.fillRect(-healthBarWidth / 2, -obstacle.height / 2 - 10, healthBarWidth, 4);
 
 
 
 
 
1833
  ctx.fillStyle = 'lime';
1834
  ctx.fillRect(
1835
  -healthBarWidth / 2,
1836
+ -obstacle.height / 2 - 10,
1837
  healthBarWidth * (obstacle.health / obstacle.maxHealth),
1838
+ 4
1839
  );
1840
  }
1841
 
1842
+ // UFO顶部圆 dome
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1843
  ctx.beginPath();
1844
+ ctx.ellipse(0, -obstacle.height / 6, obstacle.width / 3, obstacle.height / 4, 0, 0, Math.PI * 2);
1845
+ ctx.fillStyle = '#9b59b6';
1846
  ctx.fill();
1847
 
1848
+ // UFO主体
1849
  ctx.beginPath();
1850
+ ctx.ellipse(0, 0, obstacle.width / 2, obstacle.height / 3, 0, 0, Math.PI * 2);
1851
+ ctx.fillStyle = '#8e44ad';
1852
  ctx.fill();
1853
 
1854
+ // 一些灯
1855
+ for (let i = 0; i < 3; i++) {
1856
+ const angle = (i / 3) * Math.PI * 2;
1857
+ const rx = Math.cos(angle) * (obstacle.width / 2.2);
1858
+ const ry = Math.sin(angle) * (obstacle.height / 3.2);
1859
+ ctx.beginPath();
1860
+ ctx.arc(rx, ry, 3, 0, Math.PI * 2);
1861
+ ctx.fillStyle = '#ffdc00';
1862
+ ctx.fill();
 
 
 
 
 
 
 
1863
  }
1864
  }
1865
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1866
  ctx.restore();
1867
  });
1868
 
1869
+ // 绘制玩家飞机
1870
  ctx.save();
1871
  ctx.translate(gameState.plane.x, gameState.plane.y);
1872
  ctx.rotate(gameState.plane.rotation * Math.PI / 180);
 
1880
  ctx.stroke();
1881
 
1882
  // 保护罩光晕
1883
+ const gradS = ctx.createRadialGradient(0, 0, 0, 0, 0, 45);
1884
+ gradS.addColorStop(0, 'rgba(0, 204, 255, 0.2)');
1885
+ gradS.addColorStop(1, 'rgba(0, 204, 255, 0)');
1886
+ ctx.fillStyle = gradS;
1887
  ctx.fill();
1888
  }
1889
 
 
1948
  ctx.save();
1949
  ctx.translate(explosion.x, explosion.y);
1950
 
1951
+ const gradExp = ctx.createRadialGradient(
1952
  0, 0, 0,
1953
  0, 0, explosion.size
1954
  );
1955
+ gradExp.addColorStop(0, `rgba(255, 100, 0, ${explosion.alpha})`);
1956
+ gradExp.addColorStop(0.5, `rgba(255, 200, 0, ${explosion.alpha * 0.6})`);
1957
+ gradExp.addColorStop(1, `rgba(255, 255, 255, 0)`);
1958
 
1959
  ctx.beginPath();
1960
  ctx.arc(0, 0, explosion.size, 0, Math.PI * 2);
1961
+ ctx.fillStyle = gradExp;
1962
  ctx.fill();
1963
 
1964
  ctx.restore();
 
1970
  const progress = timePassed / effect.duration;
1971
 
1972
  ctx.save();
1973
+ ctx.translate(effect.x, effect.y - progress * 50); // 文字向上移动
1974
  ctx.globalAlpha = 1 - progress * 0.8;
 
1975
  ctx.font = 'bold 20px Arial';
1976
  ctx.fillStyle = effect.color;
1977
  ctx.textAlign = 'center';
1978
  ctx.textBaseline = 'middle';
1979
  ctx.fillText(effect.text, 0, 0);
 
1980
  ctx.restore();
1981
  });
1982
 
1983
+ // 绘制速度线(速度快时)
1984
  if (gameState.speed > 120) {
1985
  for (let i = 0; i < 10; i++) {
1986
  const x = Math.random() * canvas.width;
 
2021
  'fas fa-bolt': 'f0e7',
2022
  'fas fa-rocket': 'f135',
2023
  'fas fa-shield-alt': 'f3ed',
2024
+ 'fas fa-heart': 'f004',
2025
+ 'fas fa-clock': 'f017',
2026
+ 'fas fa-bomb': 'f1e2',
2027
+ 'fas fa-star': 'f005'
2028
  };
2029
  return icons[iconClass] || 'f128'; // 默认返回问号图标
2030
  }
2031
 
2032
+ // 虚拟摇杆控制
2033
+ function setupJoystick() {
2034
+ const joystickArea = joystick;
2035
+ const handle = joystickHandle;
2036
+ let active = false;
2037
+ let startX = 0;
2038
+ let startY = 0;
2039
+ let handleX = 0;
2040
+ let handleY = 0;
2041
+ const maxDistance = 40;
2042
+
2043
+ joystickArea.addEventListener('touchstart', (e) => {
2044
+ e.preventDefault();
2045
+ const touch = e.touches[0];
2046
+ const rect = joystickArea.getBoundingClientRect();
2047
+ startX = rect.left + rect.width / 2;
2048
+ startY = rect.top + rect.height / 2;
2049
+ handleX = touch.clientX - startX;
2050
+ handleY = touch.clientY - startY;
2051
+
2052
+ // 限制摇杆移动范围
2053
+ const distance = Math.sqrt(handleX * handleX + handleY * handleY);
2054
+ if (distance > maxDistance) {
2055
+ handleX = (handleX / distance) * maxDistance;
2056
+ handleY = (handleY / distance) * maxDistance;
2057
+ }
2058
+
2059
+ handle.style.transform = `translate(${handleX}px, ${handleY}px)`;
2060
+
2061
+ // 计算角度和距离
2062
+ gameState.joystickAngle = Math.atan2(handleY, handleX);
2063
+ gameState.joystickDistance = distance / maxDistance;
2064
+ gameState.joystickActive = true;
2065
+ active = true;
2066
+ });
2067
+
2068
+ joystickArea.addEventListener('touchmove', (e) => {
2069
+ if (!active) return;
2070
+ e.preventDefault();
2071
+ const touch = e.touches[0];
2072
+ handleX = touch.clientX - startX;
2073
+ handleY = touch.clientY - startY;
2074
+
2075
+ // 限制摇杆移动范围
2076
+ const distance = Math.sqrt(handleX * handleX + handleY * handleY);
2077
+ if (distance > maxDistance) {
2078
+ handleX = (handleX / distance) * maxDistance;
2079
+ handleY = (handleY / distance) * maxDistance;
2080
+ }
2081
+
2082
+ handle.style.transform = `translate(${handleX}px, ${handleY}px)`;
2083
+
2084
+ // 计算角度和距离
2085
+ gameState.joystickAngle = Math.atan2(handleY, handleX);
2086
+ gameState.joystickDistance = distance / maxDistance;
2087
+ });
2088
+
2089
+ joystickArea.addEventListener('touchend', (e) => {
2090
+ e.preventDefault();
2091
+ handle.style.transform = 'translate(0, 0)';
2092
+ gameState.joystickActive = false;
2093
+ active = false;
2094
+ });
2095
+ }
2096
+
2097
  // 事件监听
2098
  window.addEventListener('resize', resizeCanvas);
2099
 
 
2240
 
2241
  // 初始调整画布大小
2242
  resizeCanvas();
2243
+ setupJoystick();
2244
+
2245
+ // 显示最高分
2246
+ highScoreDisplay.textContent = gameState.highScore;
2247
  </script>
2248
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">
2249
+ Made with
2250
+ <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);">
2251
+ <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> -
2252
+ <a href="https://enzostvs-deepsite.hf.space?remix=zdwalter/plane-fighter-2" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a>
2253
+ </p>
2254
+ </body>
2255
+ </html>