zdwalter commited on
Commit
82c4765
·
verified ·
1 Parent(s): be1c803

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +188 -8
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Sky Adventure - 飞行游戏</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
@@ -98,6 +98,15 @@
98
  bottom: 30px;
99
  right: 30px;
100
  }
 
 
 
 
 
 
 
 
 
101
  </style>
102
  </head>
103
  <body class="bg-gray-900 text-white flex flex-col items-center justify-center h-screen">
@@ -108,11 +117,11 @@
108
  <div id="startScreen" class="game-overlay flex flex-col items-center justify-center bg-black bg-opacity-70">
109
  <h1 class="text-5xl font-bold mb-6 text-yellow-300">SKY ADVENTURE</h1>
110
  <div class="plane text-6xl mb-8">✈️</div>
111
- <p class="text-xl mb-8 text-center max-w-md px-4">控制飞机躲避障碍物<br>收集星星获得高分!</p>
112
  <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">
113
  开始游戏
114
  </button>
115
- <div class="mt-8 flex space-x-4">
116
  <div class="flex items-center">
117
  <i class="fas fa-arrow-up text-xl mr-2"></i>
118
  <span>上升</span>
@@ -126,6 +135,10 @@
126
  <i class="fas fa-arrow-right text-xl"></i>
127
  <span class="ml-2">转向</span>
128
  </div>
 
 
 
 
129
  </div>
130
  </div>
131
 
@@ -143,6 +156,12 @@
143
  <span id="livesDisplay" class="text-xl">3</span>
144
  </div>
145
  </div>
 
 
 
 
 
 
146
  <div class="absolute bottom-4 left-4 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
147
  <div class="flex items-center">
148
  <i class="fas fa-tachometer-alt text-blue-400 mr-2"></i>
@@ -164,6 +183,9 @@
164
  <div id="downBtn" class="control-btn hidden">
165
  <i class="fas fa-arrow-down"></i>
166
  </div>
 
 
 
167
  </div>
168
 
169
  <!-- 游戏结束界面 -->
@@ -185,6 +207,7 @@
185
  gameOver: false,
186
  score: 0,
187
  lives: 3,
 
188
  speed: 100,
189
  plane: {
190
  x: 0,
@@ -193,12 +216,14 @@
193
  height: 60,
194
  velocity: 0, // 左右方向速度
195
  verticalVelocity: 0, // 上下方向速度
196
- rotation: 0
 
197
  },
198
  stars: [],
199
  obstacles: [],
200
  clouds: [],
201
  explosions: [],
 
202
  lastStarTime: 0,
203
  lastObstacleTime: 0,
204
  lastCloudTime: 0,
@@ -206,7 +231,8 @@
206
  ArrowUp: false,
207
  ArrowDown: false,
208
  ArrowLeft: false,
209
- ArrowRight: false
 
210
  },
211
  isMobile: false
212
  };
@@ -221,12 +247,14 @@
221
  const restartButton = document.getElementById('restartButton');
222
  const scoreDisplay = document.getElementById('scoreDisplay');
223
  const livesDisplay = document.getElementById('livesDisplay');
 
224
  const speedDisplay = document.getElementById('speedDisplay');
225
  const finalScore = document.getElementById('finalScore');
226
  const leftBtn = document.getElementById('leftBtn');
227
  const rightBtn = document.getElementById('rightBtn');
228
  const upBtn = document.getElementById('upBtn');
229
  const downBtn = document.getElementById('downBtn');
 
230
 
231
  // 检测是否移动设备
232
  function detectMobile() {
@@ -251,6 +279,7 @@
251
  gameState.gameOver = false;
252
  gameState.score = 0;
253
  gameState.lives = 3;
 
254
  gameState.speed = 100;
255
  gameState.plane = {
256
  x: canvas.width / 2,
@@ -259,12 +288,14 @@
259
  height: 60,
260
  velocity: 0,
261
  verticalVelocity: 0,
262
- rotation: 0
 
263
  };
264
  gameState.stars = [];
265
  gameState.obstacles = [];
266
  gameState.clouds = [];
267
  gameState.explosions = [];
 
268
  gameState.lastStarTime = 0;
269
  gameState.lastObstacleTime = 0;
270
  gameState.lastCloudTime = 0;
@@ -279,6 +310,7 @@
279
  rightBtn.classList.remove('hidden');
280
  upBtn.classList.remove('hidden');
281
  downBtn.classList.remove('hidden');
 
282
  }
283
 
284
  updateUI();
@@ -338,6 +370,7 @@
338
  const y = Math.random() * (canvas.height - height);
339
  const speed = Math.random() * 2 + 2 + gameState.speed / 50;
340
  const type = Math.random() > 0.5 ? 'rectangle' : 'circle';
 
341
 
342
  gameState.obstacles.push({
343
  x,
@@ -345,8 +378,36 @@
345
  width,
346
  height,
347
  speed,
348
- type
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  });
 
 
 
 
 
 
 
 
 
350
  }
351
 
352
  // 创建爆炸效果
@@ -365,6 +426,7 @@
365
  scoreDisplay.textContent = gameState.score;
366
  livesDisplay.textContent = gameState.lives;
367
  speedDisplay.textContent = Math.floor(gameState.speed);
 
368
  }
369
 
370
  // 检测碰撞
@@ -389,6 +451,7 @@
389
  rightBtn.classList.add('hidden');
390
  upBtn.classList.add('hidden');
391
  downBtn.classList.add('hidden');
 
392
  }
393
 
394
  // 游戏主循环
@@ -410,6 +473,13 @@
410
 
411
  // 更新游戏状态
412
  function updateGame(timestamp) {
 
 
 
 
 
 
 
413
  // 更新飞机速度
414
  if (gameState.keys.ArrowUp || gameState.keys.ArrowDown) {
415
  gameState.speed = Math.max(50,
@@ -477,6 +547,12 @@
477
  });
478
  gameState.clouds = gameState.clouds.filter(cloud => cloud.x + cloud.size > 0);
479
 
 
 
 
 
 
 
480
  // 更新星星
481
  gameState.stars.forEach(star => {
482
  star.x -= star.speed;
@@ -509,7 +585,7 @@
509
  gameState.obstacles.forEach(obstacle => {
510
  obstacle.x -= obstacle.speed;
511
 
512
- // 检测碰撞
513
  const planeRect = {
514
  x: gameState.plane.x - gameState.plane.width / 2,
515
  y: gameState.plane.y - gameState.plane.height / 2,
@@ -539,6 +615,37 @@
539
  endGame();
540
  }
541
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
  });
543
  gameState.obstacles = gameState.obstacles.filter(obstacle => obstacle.x + obstacle.width > 0 && !obstacle.hit);
544
 
@@ -575,6 +682,29 @@
575
  });
576
  });
577
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
  // 绘制星星
579
  gameState.stars.forEach(star => {
580
  ctx.save();
@@ -624,6 +754,24 @@
624
  ctx.translate(obstacle.x, obstacle.y);
625
 
626
  if (obstacle.type === 'rectangle') {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
627
  ctx.fillStyle = '#555';
628
  ctx.fillRect(
629
  -obstacle.width / 2,
@@ -774,6 +922,11 @@
774
  gameState.keys[e.key] = true;
775
  e.preventDefault();
776
  }
 
 
 
 
 
777
  });
778
 
779
  document.addEventListener('keyup', (e) => {
@@ -781,6 +934,11 @@
781
  gameState.keys[e.key] = false;
782
  e.preventDefault();
783
  }
 
 
 
 
 
784
  });
785
 
786
  // 触摸控制按钮事件
@@ -820,6 +978,15 @@
820
  gameState.keys.ArrowDown = false;
821
  });
822
 
 
 
 
 
 
 
 
 
 
823
  // 鼠标控制按钮事件(用于桌面浏览器测试)
824
  leftBtn.addEventListener('mousedown', (e) => {
825
  e.preventDefault();
@@ -873,6 +1040,19 @@
873
  gameState.keys.ArrowDown = false;
874
  });
875
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876
  // 按钮事件
877
  startButton.addEventListener('click', initGame);
878
  restartButton.addEventListener('click', initGame);
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Sky Adventure - Plane Shooting Game</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
 
98
  bottom: 30px;
99
  right: 30px;
100
  }
101
+ #fireBtn {
102
+ bottom: 170px;
103
+ left: 30px;
104
+ }
105
+ .bullet {
106
+ position: absolute;
107
+ background: linear-gradient(to right, #ff0, #f80);
108
+ border-radius: 50%;
109
+ }
110
  </style>
111
  </head>
112
  <body class="bg-gray-900 text-white flex flex-col items-center justify-center h-screen">
 
117
  <div id="startScreen" class="game-overlay flex flex-col items-center justify-center bg-black bg-opacity-70">
118
  <h1 class="text-5xl font-bold mb-6 text-yellow-300">SKY ADVENTURE</h1>
119
  <div class="plane text-6xl mb-8">✈️</div>
120
+ <p class="text-xl mb-8 text-center max-w-md px-4">控制飞机躲避障碍物<br>收集星星获得高分!<br>按射击按钮消灭障碍物!</p>
121
  <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">
122
  开始游戏
123
  </button>
124
+ <div class="mt-8 flex flex-wrap justify-center gap-4">
125
  <div class="flex items-center">
126
  <i class="fas fa-arrow-up text-xl mr-2"></i>
127
  <span>上升</span>
 
135
  <i class="fas fa-arrow-right text-xl"></i>
136
  <span class="ml-2">转向</span>
137
  </div>
138
+ <div class="flex items-center">
139
+ <i class="fas fa-bolt text-xl mr-2 text-yellow-400"></i>
140
+ <span>射击</span>
141
+ </div>
142
  </div>
143
  </div>
144
 
 
156
  <span id="livesDisplay" class="text-xl">3</span>
157
  </div>
158
  </div>
159
+ <div class="absolute top-4 left-1/2 transform -translate-x-1/2 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
160
+ <div class="flex items-center">
161
+ <i class="fas fa-bolt text-yellow-400 mr-2"></i>
162
+ <span id="ammoDisplay" class="text-xl">∞</span>
163
+ </div>
164
+ </div>
165
  <div class="absolute bottom-4 left-4 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
166
  <div class="flex items-center">
167
  <i class="fas fa-tachometer-alt text-blue-400 mr-2"></i>
 
183
  <div id="downBtn" class="control-btn hidden">
184
  <i class="fas fa-arrow-down"></i>
185
  </div>
186
+ <div id="fireBtn" class="control-btn hidden">
187
+ <i class="fas fa-bolt text-yellow-400"></i>
188
+ </div>
189
  </div>
190
 
191
  <!-- 游戏结束界面 -->
 
207
  gameOver: false,
208
  score: 0,
209
  lives: 3,
210
+ ammo: Infinity,
211
  speed: 100,
212
  plane: {
213
  x: 0,
 
216
  height: 60,
217
  velocity: 0, // 左右方向速度
218
  verticalVelocity: 0, // 上下方向速度
219
+ rotation: 0,
220
+ lastFireTime: 0
221
  },
222
  stars: [],
223
  obstacles: [],
224
  clouds: [],
225
  explosions: [],
226
+ bullets: [],
227
  lastStarTime: 0,
228
  lastObstacleTime: 0,
229
  lastCloudTime: 0,
 
231
  ArrowUp: false,
232
  ArrowDown: false,
233
  ArrowLeft: false,
234
+ ArrowRight: false,
235
+ Space: false
236
  },
237
  isMobile: false
238
  };
 
247
  const restartButton = document.getElementById('restartButton');
248
  const scoreDisplay = document.getElementById('scoreDisplay');
249
  const livesDisplay = document.getElementById('livesDisplay');
250
+ const ammoDisplay = document.getElementById('ammoDisplay');
251
  const speedDisplay = document.getElementById('speedDisplay');
252
  const finalScore = document.getElementById('finalScore');
253
  const leftBtn = document.getElementById('leftBtn');
254
  const rightBtn = document.getElementById('rightBtn');
255
  const upBtn = document.getElementById('upBtn');
256
  const downBtn = document.getElementById('downBtn');
257
+ const fireBtn = document.getElementById('fireBtn');
258
 
259
  // 检测是否移动设备
260
  function detectMobile() {
 
279
  gameState.gameOver = false;
280
  gameState.score = 0;
281
  gameState.lives = 3;
282
+ gameState.ammo = Infinity;
283
  gameState.speed = 100;
284
  gameState.plane = {
285
  x: canvas.width / 2,
 
288
  height: 60,
289
  velocity: 0,
290
  verticalVelocity: 0,
291
+ rotation: 0,
292
+ lastFireTime: 0
293
  };
294
  gameState.stars = [];
295
  gameState.obstacles = [];
296
  gameState.clouds = [];
297
  gameState.explosions = [];
298
+ gameState.bullets = [];
299
  gameState.lastStarTime = 0;
300
  gameState.lastObstacleTime = 0;
301
  gameState.lastCloudTime = 0;
 
310
  rightBtn.classList.remove('hidden');
311
  upBtn.classList.remove('hidden');
312
  downBtn.classList.remove('hidden');
313
+ fireBtn.classList.remove('hidden');
314
  }
315
 
316
  updateUI();
 
370
  const y = Math.random() * (canvas.height - height);
371
  const speed = Math.random() * 2 + 2 + gameState.speed / 50;
372
  const type = Math.random() > 0.5 ? 'rectangle' : 'circle';
373
+ const health = type === 'rectangle' ? 2 : 1; // 矩形障碍物需要2发子弹
374
 
375
  gameState.obstacles.push({
376
  x,
 
378
  width,
379
  height,
380
  speed,
381
+ type,
382
+ health,
383
+ maxHealth: health
384
+ });
385
+ }
386
+
387
+ // 创建子弹
388
+ function createBullet() {
389
+ if (gameState.ammo <= 0) return false; // 没有弹药了
390
+
391
+ const speed = 15;
392
+ const size = 8;
393
+ const x = gameState.plane.x + 30; // 从飞机前端发射
394
+ const y = gameState.plane.y;
395
+
396
+ gameState.bullets.push({
397
+ x,
398
+ y,
399
+ size,
400
+ speed
401
  });
402
+
403
+ // 如果弹药不是无限的,减少弹药
404
+ if (gameState.ammo !== Infinity) {
405
+ gameState.ammo--;
406
+ }
407
+
408
+ updateUI();
409
+
410
+ return true;
411
  }
412
 
413
  // 创建爆炸效果
 
426
  scoreDisplay.textContent = gameState.score;
427
  livesDisplay.textContent = gameState.lives;
428
  speedDisplay.textContent = Math.floor(gameState.speed);
429
+ ammoDisplay.textContent = gameState.ammo === Infinity ? "∞" : gameState.ammo;
430
  }
431
 
432
  // 检测碰撞
 
451
  rightBtn.classList.add('hidden');
452
  upBtn.classList.add('hidden');
453
  downBtn.classList.add('hidden');
454
+ fireBtn.classList.add('hidden');
455
  }
456
 
457
  // 游戏主循环
 
473
 
474
  // 更新游戏状态
475
  function updateGame(timestamp) {
476
+ // 处理开火
477
+ if ((gameState.keys.Space || gameState.isFiring) &&
478
+ timestamp - gameState.plane.lastFireTime > 200) { // 射击冷却时间200ms
479
+ createBullet();
480
+ gameState.plane.lastFireTime = timestamp;
481
+ }
482
+
483
  // 更新飞机速度
484
  if (gameState.keys.ArrowUp || gameState.keys.ArrowDown) {
485
  gameState.speed = Math.max(50,
 
547
  });
548
  gameState.clouds = gameState.clouds.filter(cloud => cloud.x + cloud.size > 0);
549
 
550
+ // 更新子弹
551
+ gameState.bullets.forEach(bullet => {
552
+ bullet.x += bullet.speed;
553
+ });
554
+ gameState.bullets = gameState.bullets.filter(bullet => bullet.x < canvas.width);
555
+
556
  // 更新星星
557
  gameState.stars.forEach(star => {
558
  star.x -= star.speed;
 
585
  gameState.obstacles.forEach(obstacle => {
586
  obstacle.x -= obstacle.speed;
587
 
588
+ // 检测与飞机的碰撞
589
  const planeRect = {
590
  x: gameState.plane.x - gameState.plane.width / 2,
591
  y: gameState.plane.y - gameState.plane.height / 2,
 
615
  endGame();
616
  }
617
  }
618
+
619
+ // 检测与子弹的碰撞
620
+ if (!obstacle.hit) {
621
+ const bulletHits = [];
622
+
623
+ gameState.bullets.forEach((bullet, bulletIndex) => {
624
+ const bulletRect = {
625
+ x: bullet.x - bullet.size / 2,
626
+ y: bullet.y - bullet.size / 2,
627
+ width: bullet.size,
628
+ height: bullet.size
629
+ };
630
+
631
+ if (checkCollision(bulletRect, obstacleRect)) {
632
+ obstacle.health--;
633
+ bulletHits.push(bulletIndex);
634
+ createExplosion(bullet.x, bullet.y);
635
+
636
+ if (obstacle.health <= 0) {
637
+ obstacle.hit = true;
638
+ gameState.score += 15; // 击毁障碍物奖励
639
+ updateUI();
640
+ }
641
+ }
642
+ });
643
+
644
+ // 移除已经击中的子弹
645
+ for (let i = bulletHits.length - 1; i >= 0; i--) {
646
+ gameState.bullets.splice(bulletHits[i], 1);
647
+ }
648
+ }
649
  });
650
  gameState.obstacles = gameState.obstacles.filter(obstacle => obstacle.x + obstacle.width > 0 && !obstacle.hit);
651
 
 
682
  });
683
  });
684
 
685
+ // 绘制子弹
686
+ gameState.bullets.forEach(bullet => {
687
+ const gradient = ctx.createRadialGradient(
688
+ bullet.x, bullet.y, 0,
689
+ bullet.x, bullet.y, bullet.size
690
+ );
691
+ gradient.addColorStop(0, '#ff0');
692
+ gradient.addColorStop(1, '#f80');
693
+
694
+ ctx.beginPath();
695
+ ctx.arc(bullet.x, bullet.y, bullet.size, 0, Math.PI * 2);
696
+ ctx.fillStyle = gradient;
697
+ ctx.fill();
698
+
699
+ // 子弹尾迹
700
+ ctx.beginPath();
701
+ ctx.moveTo(bullet.x - bullet.speed, bullet.y);
702
+ ctx.lineTo(bullet.x, bullet.y);
703
+ ctx.strokeStyle = 'rgba(255, 200, 0, 0.8)';
704
+ ctx.lineWidth = bullet.size / 2;
705
+ ctx.stroke();
706
+ });
707
+
708
  // 绘制星星
709
  gameState.stars.forEach(star => {
710
  ctx.save();
 
754
  ctx.translate(obstacle.x, obstacle.y);
755
 
756
  if (obstacle.type === 'rectangle') {
757
+ // 绘制健康条 (如果是矩形障碍物)
758
+ if (obstacle.health < obstacle.maxHealth) {
759
+ ctx.fillStyle = 'red';
760
+ ctx.fillRect(
761
+ -obstacle.width / 2 - 2,
762
+ -obstacle.height / 2 - 10,
763
+ 5,
764
+ 5
765
+ );
766
+ ctx.fillStyle = 'lime';
767
+ ctx.fillRect(
768
+ -obstacle.width / 2 - 2 + 5,
769
+ -obstacle.height / 2 - 10,
770
+ 5,
771
+ 5
772
+ );
773
+ }
774
+
775
  ctx.fillStyle = '#555';
776
  ctx.fillRect(
777
  -obstacle.width / 2,
 
922
  gameState.keys[e.key] = true;
923
  e.preventDefault();
924
  }
925
+
926
+ if (e.key === ' ' || e.key === 'Spacebar') { // 空格键射击
927
+ gameState.keys.Space = true;
928
+ e.preventDefault();
929
+ }
930
  });
931
 
932
  document.addEventListener('keyup', (e) => {
 
934
  gameState.keys[e.key] = false;
935
  e.preventDefault();
936
  }
937
+
938
+ if (e.key === ' ' || e.key === 'Spacebar') {
939
+ gameState.keys.Space = false;
940
+ e.preventDefault();
941
+ }
942
  });
943
 
944
  // 触摸控制按钮事件
 
978
  gameState.keys.ArrowDown = false;
979
  });
980
 
981
+ fireBtn.addEventListener('touchstart', (e) => {
982
+ e.preventDefault();
983
+ gameState.isFiring = true;
984
+ });
985
+ fireBtn.addEventListener('touchend', (e) => {
986
+ e.preventDefault();
987
+ gameState.isFiring = false;
988
+ });
989
+
990
  // 鼠标控制按钮事件(用于桌面浏览器测试)
991
  leftBtn.addEventListener('mousedown', (e) => {
992
  e.preventDefault();
 
1040
  gameState.keys.ArrowDown = false;
1041
  });
1042
 
1043
+ fireBtn.addEventListener('mousedown', (e) => {
1044
+ e.preventDefault();
1045
+ gameState.isFiring = true;
1046
+ });
1047
+ fireBtn.addEventListener('mouseup', (e) => {
1048
+ e.preventDefault();
1049
+ gameState.isFiring = false;
1050
+ });
1051
+ fireBtn.addEventListener('mouseleave', (e) => {
1052
+ e.preventDefault();
1053
+ gameState.isFiring = false;
1054
+ });
1055
+
1056
  // 按钮事件
1057
  startButton.addEventListener('click', initGame);
1058
  restartButton.addEventListener('click', initGame);