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

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +1684 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Plane Fighter 2
3
- emoji: 👀
4
- colorFrom: gray
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: plane-fighter-2
3
+ emoji: 🐳
4
+ colorFrom: purple
5
+ colorTo: yellow
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1684 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
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>
10
+ body {
11
+ overflow: hidden;
12
+ touch-action: none;
13
+ }
14
+ #gameCanvas {
15
+ display: block;
16
+ background: linear-gradient(to bottom, #1e3c72 0%, #2a5298 100%);
17
+ }
18
+ .game-overlay {
19
+ position: absolute;
20
+ top: 0;
21
+ left: 0;
22
+ width: 100%;
23
+ height: 100%;
24
+ pointer-events: none;
25
+ }
26
+ .cloud {
27
+ position: absolute;
28
+ background-color: rgba(255, 255, 255, 0.8);
29
+ border-radius: 50%;
30
+ }
31
+ @keyframes float {
32
+ 0% { transform: translateY(0px); }
33
+ 50% { transform: translateY(-10px); }
34
+ 100% { transform: translateY(0px); }
35
+ }
36
+ .plane {
37
+ animation: float 2s ease-in-out infinite;
38
+ }
39
+ .star {
40
+ position: absolute;
41
+ color: gold;
42
+ text-shadow: 0 0 10px yellow;
43
+ animation: twinkle 1s ease-in-out infinite alternate;
44
+ }
45
+ @keyframes twinkle {
46
+ from { opacity: 0.7; transform: scale(0.9); }
47
+ to { opacity: 1; transform: scale(1.1); }
48
+ }
49
+ .obstacle {
50
+ position: absolute;
51
+ background-color: #555;
52
+ border-radius: 5px;
53
+ }
54
+ .explosion {
55
+ position: absolute;
56
+ width: 60px;
57
+ height: 60px;
58
+ background: radial-gradient(circle, rgba(255,100,0,0.8) 0%, rgba(255,200,0,0.6) 50%, rgba(255,255,255,0) 70%);
59
+ border-radius: 50%;
60
+ animation: explode 0.5s ease-out forwards;
61
+ }
62
+ @keyframes explode {
63
+ 0% { transform: scale(0); opacity: 1; }
64
+ 100% { transform: scale(2); opacity: 0; }
65
+ }
66
+ .control-btn {
67
+ position: absolute;
68
+ width: 60px;
69
+ height: 60px;
70
+ background: rgba(255, 255, 255, 0.2);
71
+ border-radius: 50%;
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ font-size: 24px;
76
+ color: white;
77
+ pointer-events: auto;
78
+ user-select: none;
79
+ -webkit-tap-highlight-color: transparent;
80
+ }
81
+ .control-btn:active {
82
+ background: rgba(255, 255, 255, 0.4);
83
+ transform: scale(0.95);
84
+ }
85
+ #leftBtn {
86
+ bottom: 30px;
87
+ left: 30px;
88
+ }
89
+ #rightBtn {
90
+ bottom: 30px;
91
+ left: 110px;
92
+ }
93
+ #upBtn {
94
+ bottom: 100px;
95
+ right: 30px;
96
+ }
97
+ #downBtn {
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
+ .debris {
111
+ position: absolute;
112
+ background-color: #777;
113
+ border-radius: 2px;
114
+ }
115
+ .powerup {
116
+ position: absolute;
117
+ width: 40px;
118
+ height: 40px;
119
+ border-radius: 50%;
120
+ display: flex;
121
+ align-items: center;
122
+ justify-content: center;
123
+ font-size: 20px;
124
+ text-shadow: 0 0 5px white;
125
+ }
126
+ .shield {
127
+ position: absolute;
128
+ border-radius: 50%;
129
+ border: 3px solid rgba(0, 204, 255, 0.6);
130
+ pointer-events: none;
131
+ }
132
+ .homing-missile {
133
+ position: absolute;
134
+ background: linear-gradient(to bottom, #ff5f5f, #ff0000);
135
+ border-radius: 50% 50% 0 0;
136
+ transform-origin: center bottom;
137
+ }
138
+ @keyframes pulse {
139
+ 0% { transform: scale(1); opacity: 0.9; }
140
+ 50% { transform: scale(1.1); opacity: 1; }
141
+ 100% { transform: scale(1); opacity: 0.9; }
142
+ }
143
+ .powerup-effect {
144
+ position: absolute;
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">
151
+ <div class="relative w-full h-full">
152
+ <canvas id="gameCanvas" class="w-full h-full"></canvas>
153
+
154
+ <!-- 开始界面 -->
155
+ <div id="startScreen" class="game-overlay flex flex-col items-center justify-center bg-black bg-opacity-70">
156
+ <h1 class="text-5xl font-bold mb-6 text-yellow-300">SKY ADVENTURE</h1>
157
+ <div class="plane text-6xl mb-8">✈️</div>
158
+ <p class="text-xl mb-8 text-center max-w-md px-4">控制飞机躲避障碍物<br>收集星星获得高分!<br>按射击按钮消灭障碍物!<br>收集道具获得特殊能力!</p>
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>
166
+ </div>
167
+ <div class="flex items-center">
168
+ <div class="powerup bg-purple-500 mr-2"><i class="fas fa-rocket"></i></div>
169
+ <span>跟踪导弹</span>
170
+ </div>
171
+ <div class="flex items-center">
172
+ <div class="powerup bg-blue-500 mr-2"><i class="fas fa-shield-alt"></i></div>
173
+ <span>保护罩</span>
174
+ </div>
175
+ <div class="flex items-center">
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
+
182
+ <!-- 游戏UI -->
183
+ <div id="gameUI" class="game-overlay hidden">
184
+ <div class="absolute top-4 left-4 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
185
+ <div class="flex items-center">
186
+ <i class="fas fa-star text-yellow-400 mr-2"></i>
187
+ <span id="scoreDisplay" class="text-xl">0</span>
188
+ </div>
189
+ </div>
190
+ <div class="absolute top-4 right-4 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
191
+ <div class="flex items-center">
192
+ <i class="fas fa-heart text-red-500 mr-2"></i>
193
+ <span id="livesDisplay" class="text-xl">3</span>
194
+ </div>
195
+ </div>
196
+ <div class="absolute top-4 left-1/2 transform -translate-x-1/2 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
197
+ <div class="flex items-center">
198
+ <i class="fas fa-bolt text-yellow-400 mr-2"></i>
199
+ <span id="ammoDisplay" class="text-xl">∞</span>
200
+ </div>
201
+ </div>
202
+ <div class="absolute bottom-4 left-4 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
203
+ <div class="flex items-center">
204
+ <i class="fas fa-tachometer-alt text-blue-400 mr-2"></i>
205
+ <span id="speedDisplay" class="text-xl">100</span>
206
+ <span class="ml-1">km/h</span>
207
+ </div>
208
+ </div>
209
+
210
+ <!-- 主动技能图标 -->
211
+ <div id="powerupStatus" class="absolute bottom-24 right-4 flex gap-2">
212
+ <!-- 这里会被JavaScript动态填充 -->
213
+ </div>
214
+
215
+ <!-- 触摸控制按钮 -->
216
+ <div id="leftBtn" class="control-btn hidden">
217
+ <i class="fas fa-arrow-left"></i>
218
+ </div>
219
+ <div id="rightBtn" class="control-btn hidden">
220
+ <i class="fas fa-arrow-right"></i>
221
+ </div>
222
+ <div id="upBtn" class="control-btn hidden">
223
+ <i class="fas fa-arrow-up"></i>
224
+ </div>
225
+ <div id="downBtn" class="control-btn hidden">
226
+ <i class="fas fa-arrow-down"></i>
227
+ </div>
228
+ <div id="fireBtn" class="control-btn hidden">
229
+ <i class="fas fa-bolt text-yellow-400"></i>
230
+ </div>
231
+ </div>
232
+
233
+ <!-- 游戏结束界面 -->
234
+ <div id="gameOverScreen" class="game-overlay hidden flex flex-col items-center justify-center bg-black bg-opacity-70">
235
+ <h1 class="text-5xl font-bold mb-6 text-red-500">GAME OVER</h1>
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 = {
248
+ started: false,
249
+ gameOver: false,
250
+ score: 0,
251
+ lives: 3,
252
+ ammo: Infinity,
253
+ speed: 100,
254
+ difficulty: 1,
255
+ plane: {
256
+ x: 0,
257
+ y: 0,
258
+ width: 60,
259
+ height: 60,
260
+ velocity: 0, // 左右方向速度
261
+ verticalVelocity: 0, // 上下方向速度
262
+ rotation: 0,
263
+ lastFireTime: 0,
264
+ fireRate: 200, // 射击间隔(ms)
265
+ bulletDamage: 1, // 子弹伤害
266
+ hasShield: false,
267
+ shieldDuration: 0,
268
+ powerups: {
269
+ rapidFire: 0, // 火力增强
270
+ homingMissiles: 0, // 跟踪导弹
271
+ }
272
+ },
273
+ stars: [],
274
+ obstacles: [],
275
+ clouds: [],
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,
289
+ ArrowLeft: false,
290
+ ArrowRight: false,
291
+ Space: false
292
+ },
293
+ isMobile: false
294
+ };
295
+
296
+ // 道具类型
297
+ const POWERUP_TYPES = {
298
+ RAPID_FIRE: {
299
+ id: 'rapidFire',
300
+ icon: 'fas fa-bolt',
301
+ color: 'red',
302
+ duration: 10000, // 10秒
303
+ effect: (game) => {
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: {
310
+ id: 'homingMissiles',
311
+ icon: 'fas fa-rocket',
312
+ color: 'purple',
313
+ duration: 10000, // 10秒
314
+ effect: (game) => {
315
+ game.plane.powerups.homingMissiles = Date.now() + POWERUP_TYPES.HOMING_MISSILE.duration;
316
+ createEffect('跟踪导弹已激活!', 'purple', 1500);
317
+ }
318
+ },
319
+ SHIELD: {
320
+ id: 'shield',
321
+ icon: 'fas fa-shield-alt',
322
+ color: 'blue',
323
+ duration: 8000, // 8秒
324
+ effect: (game) => {
325
+ game.plane.hasShield = true;
326
+ game.plane.shieldDuration = Date.now() + POWERUP_TYPES.SHIELD.duration;
327
+ createEffect('保护罩已启用!', 'blue', 1500);
328
+ }
329
+ },
330
+ HEALTH: {
331
+ id: 'health',
332
+ icon: 'fas fa-heart',
333
+ color: 'green',
334
+ effect: (game) => {
335
+ game.lives = Math.min(game.lives + 1, 5); // 最多5条命
336
+ updateUI();
337
+ createEffect('生命值恢复!', 'green', 1500);
338
+ }
339
+ }
340
+ };
341
+
342
+ // 获取DOM元素
343
+ const canvas = document.getElementById('gameCanvas');
344
+ const ctx = canvas.getContext('2d');
345
+ const startScreen = document.getElementById('startScreen');
346
+ const gameUI = document.getElementById('gameUI');
347
+ const gameOverScreen = document.getElementById('gameOverScreen');
348
+ const startButton = document.getElementById('startButton');
349
+ const restartButton = document.getElementById('restartButton');
350
+ const scoreDisplay = document.getElementById('scoreDisplay');
351
+ const livesDisplay = document.getElementById('livesDisplay');
352
+ const ammoDisplay = document.getElementById('ammoDisplay');
353
+ const speedDisplay = document.getElementById('speedDisplay');
354
+ const finalScore = document.getElementById('finalScore');
355
+ const powerupStatus = document.getElementById('powerupStatus');
356
+ const leftBtn = document.getElementById('leftBtn');
357
+ const rightBtn = document.getElementById('rightBtn');
358
+ const upBtn = document.getElementById('upBtn');
359
+ const downBtn = document.getElementById('downBtn');
360
+ const fireBtn = document.getElementById('fireBtn');
361
+
362
+ // 检测是否移动设备
363
+ function detectMobile() {
364
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
365
+ }
366
+
367
+ // 设置画布大小
368
+ function resizeCanvas() {
369
+ canvas.width = window.innerWidth;
370
+ canvas.height = window.innerHeight;
371
+ if (gameState.started && !gameState.gameOver) {
372
+ gameState.plane.x = canvas.width / 2;
373
+ gameState.plane.y = canvas.height / 2;
374
+ }
375
+ }
376
+
377
+ // 创建文字特效
378
+ function createEffect(text, color, duration) {
379
+ gameState.effects.push({
380
+ text,
381
+ color,
382
+ x: gameState.plane.x,
383
+ y: gameState.plane.y - 50,
384
+ alpha: 1,
385
+ duration,
386
+ startTime: Date.now()
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 = '';
399
+
400
+ if (gameState.plane.powerups.rapidFire > Date.now()) {
401
+ const timeLeft = Math.ceil((gameState.plane.powerups.rapidFire - Date.now()) / 1000);
402
+ powerupStatus.innerHTML += `
403
+ <div class="bg-black bg-opacity-50 px-3 py-1 rounded-lg flex items-center" title="火力增强 (${timeLeft}s)">
404
+ <i class="fas fa-bolt text-red-500 mr-2"></i>
405
+ </div>
406
+ `;
407
+ }
408
+
409
+ if (gameState.plane.powerups.homingMissiles > Date.now()) {
410
+ const timeLeft = Math.ceil((gameState.plane.powerups.homingMissiles - Date.now()) / 1000);
411
+ powerupStatus.innerHTML += `
412
+ <div class="bg-black bg-opacity-50 px-3 py-1 rounded-lg flex items-center" title="跟踪导弹 (${timeLeft}s)">
413
+ <i class="fas fa-rocket text-purple-500 mr-2"></i>
414
+ </div>
415
+ `;
416
+ }
417
+
418
+ if (gameState.plane.hasShield && gameState.plane.shieldDuration > Date.now()) {
419
+ const timeLeft = Math.ceil((gameState.plane.shieldDuration - Date.now()) / 1000);
420
+ powerupStatus.innerHTML += `
421
+ <div class="bg-black bg-opacity-50 px-3 py-1 rounded-lg flex items-center" title="保护罩 (${timeLeft}s)">
422
+ <i class="fas fa-shield-alt text-blue-500 mr-2"></i>
423
+ </div>
424
+ `;
425
+ }
426
+ }
427
+
428
+ // 初始化游戏
429
+ function initGame() {
430
+ gameState.isMobile = detectMobile();
431
+ resizeCanvas();
432
+ gameState.started = true;
433
+ gameState.gameOver = false;
434
+ gameState.score = 0;
435
+ gameState.lives = 3;
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,
442
+ width: 60,
443
+ height: 60,
444
+ velocity: 0,
445
+ verticalVelocity: 0,
446
+ rotation: 0,
447
+ lastFireTime: 0,
448
+ fireRate: 200,
449
+ bulletDamage: 1,
450
+ hasShield: false,
451
+ shieldDuration: 0,
452
+ powerups: {
453
+ rapidFire: 0,
454
+ homingMissiles: 0,
455
+ }
456
+ };
457
+ gameState.stars = [];
458
+ gameState.obstacles = [];
459
+ gameState.clouds = [];
460
+ gameState.explosions = [];
461
+ gameState.bullets = [];
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');
473
+ gameUI.classList.remove('hidden');
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
+
489
+ // 创建初始云朵
490
+ function createInitialClouds() {
491
+ for (let i = 0; i < 10; i++) {
492
+ createCloud(true);
493
+ }
494
+ }
495
+
496
+ // 创建云朵
497
+ function createCloud(initial = false) {
498
+ const size = Math.random() * 60 + 40;
499
+ const x = initial ? Math.random() * canvas.width : canvas.width + size;
500
+ const y = Math.random() * canvas.height;
501
+ const speed = Math.random() * 1 + 0.5;
502
+
503
+ gameState.clouds.push({
504
+ x,
505
+ y,
506
+ size,
507
+ speed,
508
+ parts: Array(3).fill().map(() => ({
509
+ size: size * (Math.random() * 0.3 + 0.7),
510
+ offsetX: (Math.random() - 0.5) * size * 0.6,
511
+ offsetY: (Math.random() - 0.5) * size * 0.6
512
+ }))
513
+ });
514
+ }
515
+
516
+ // 创建星星
517
+ function createStar() {
518
+ const size = Math.random() * 20 + 15;
519
+ const x = canvas.width + size;
520
+ const y = Math.random() * (canvas.height - size * 2) + size;
521
+ const speed = Math.random() * 3 + 3 + gameState.speed / 50;
522
+
523
+ gameState.stars.push({
524
+ x,
525
+ y,
526
+ size,
527
+ speed,
528
+ rotation: 0,
529
+ rotationSpeed: Math.random() * 0.1 - 0.05
530
+ });
531
+ }
532
+
533
+ // 创建障碍物
534
+ function createObstacle() {
535
+ const width = Math.random() * 80 + 40;
536
+ const height = Math.random() * 80 + 40;
537
+ const x = canvas.width + width;
538
+ const y = Math.random() * (canvas.height - height);
539
+ const speed = Math.random() * 2 + 2 + gameState.speed / 50;
540
+ const type = Math.random() > 0.5 ? 'rectangle' : 'circle';
541
+ const health = type === 'rectangle' ? (width > 80 ? 3 : 2) : 1;
542
+
543
+ // 碰撞体积比实际显示大20%
544
+ const collisionWidth = width * 1.2;
545
+ const collisionHeight = height * 1.2;
546
+
547
+ gameState.obstacles.push({
548
+ x,
549
+ y,
550
+ width,
551
+ height,
552
+ collisionWidth,
553
+ collisionHeight,
554
+ speed,
555
+ type,
556
+ health,
557
+ maxHealth: health,
558
+ isLarge: width > 80
559
+ });
560
+ }
561
+
562
+ // 创建道具
563
+ function createPowerup() {
564
+ const size = 40;
565
+ const x = canvas.width + size;
566
+ const y = Math.random() * (canvas.height - size * 2) + size;
567
+ const speed = Math.random() * 2 + 1;
568
+
569
+ // 随机选择一种道具类型
570
+ const powerupKeys = Object.keys(POWERUP_TYPES);
571
+ const randomPowerup = POWERUP_TYPES[powerupKeys[Math.floor(Math.random() * powerupKeys.length)]];
572
+
573
+ gameState.powerups.push({
574
+ x,
575
+ y,
576
+ size,
577
+ speed,
578
+ type: randomPowerup
579
+ });
580
+ }
581
+
582
+ // 创建跟踪导弹
583
+ function createHomingMissile() {
584
+ if (gameState.obstacles.length === 0) return; // 没有目标时不开火
585
+
586
+ // 找到最近的障碍物作为目标
587
+ let closestObstacle = null;
588
+ let minDistance = Infinity;
589
+
590
+ gameState.obstacles.forEach(obstacle => {
591
+ const dx = obstacle.x - gameState.plane.x;
592
+ const dy = obstacle.y - gameState.plane.y;
593
+ const distance = Math.sqrt(dx * dx + dy * dy);
594
+
595
+ if (distance < minDistance) {
596
+ minDistance = distance;
597
+ closestObstacle = obstacle;
598
+ }
599
+ });
600
+
601
+ if (!closestObstacle) return;
602
+
603
+ const size = 12;
604
+ gameState.homingMissiles.push({
605
+ x: gameState.plane.x,
606
+ y: gameState.plane.y,
607
+ size,
608
+ speed: 8,
609
+ target: closestObstacle,
610
+ angle: Math.atan2(
611
+ closestObstacle.y - gameState.plane.y,
612
+ closestObstacle.x - gameState.plane.x
613
+ )
614
+ });
615
+ }
616
+
617
+ // 创建碎片
618
+ function createDebris(obstacle, count = 5) {
619
+ for (let i = 0; i < count; i++) {
620
+ gameState.debris.push({
621
+ x: obstacle.x,
622
+ y: obstacle.y,
623
+ width: obstacle.width / 3,
624
+ height: obstacle.height / 3,
625
+ speedX: (Math.random() - 0.5) * 4,
626
+ speedY: (Math.random() - 0.5) * 4,
627
+ rotation: 0,
628
+ rotationSpeed: (Math.random() - 0.5) * 0.2,
629
+ opacity: 1
630
+ });
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
+
644
+ gameState.bullets.push({
645
+ x,
646
+ y,
647
+ size,
648
+ speed,
649
+ damage
650
+ });
651
+
652
+ // 如果有跟踪导弹能力且冷却结束
653
+ if (gameState.plane.powerups.homingMissiles > Date.now() &&
654
+ Math.random() > 0.7) { // 70%概率发射跟踪导弹
655
+ requestAnimationFrame(createHomingMissile);
656
+ }
657
+
658
+ // 如果弹药不是无限的,减少弹药
659
+ if (gameState.ammo !== Infinity) {
660
+ gameState.ammo--;
661
+ }
662
+
663
+ updateUI();
664
+
665
+ return true;
666
+ }
667
+
668
+ // 创建爆炸效果
669
+ function createExplosion(x, y) {
670
+ gameState.explosions.push({
671
+ x,
672
+ y,
673
+ size: 0,
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 &&
683
+ rect1.x + rect1.width > rect2.x &&
684
+ rect1.y < rect2.y + rect2.height &&
685
+ rect1.y + rect1.height > rect2.y
686
+ );
687
+ }
688
+
689
+ // 游戏结束
690
+ function endGame() {
691
+ gameState.gameOver = true;
692
+ gameUI.classList.add('hidden');
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
+ // 游戏主循环
705
+ function gameLoop(timestamp) {
706
+ if (!gameState.started || gameState.gameOver) return;
707
+
708
+ // 清除画布
709
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
710
+
711
+ // 更新游戏状态
712
+ updateGame(timestamp);
713
+
714
+ // 绘制游戏元素
715
+ drawGame();
716
+
717
+ // 继续循环
718
+ requestAnimationFrame(gameLoop);
719
+ }
720
+
721
+ // 更新游戏状态
722
+ function updateGame(timestamp) {
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; // 恢复默认射击速度
737
+ createEffect('火力增强结束', 'red', 1500);
738
+ }
739
+
740
+ if (gameState.plane.powerups.homingMissiles > 0 && gameState.plane.powerups.homingMissiles < Date.now()) {
741
+ gameState.plane.powerups.homingMissiles = 0;
742
+ createEffect('跟踪导弹结束', 'purple', 1500);
743
+ }
744
+
745
+ if (gameState.plane.hasShield && gameState.plane.shieldDuration < Date.now()) {
746
+ gameState.plane.hasShield = false;
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 {
767
+ // 如果没有按左右键,飞机逐渐回正
768
+ gameState.plane.rotation *= 0.95;
769
+ gameState.plane.velocity *= 0.95;
770
+ if (Math.abs(gameState.plane.rotation) < 0.5) gameState.plane.rotation = 0;
771
+ if (Math.abs(gameState.plane.velocity) < 0.5) gameState.plane.velocity = 0;
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
+ // 如果没有按上下键,垂直速度逐渐归零
781
+ gameState.plane.verticalVelocity *= 0.95;
782
+ if (Math.abs(gameState.plane.verticalVelocity) < 0.5) gameState.plane.verticalVelocity = 0;
783
+ }
784
+
785
+ // 更新飞机位置
786
+ gameState.plane.x += gameState.plane.velocity;
787
+ gameState.plane.y += gameState.plane.verticalVelocity;
788
+
789
+ // 限制飞机在屏幕内
790
+ gameState.plane.x = Math.max(gameState.plane.width / 2, Math.min(gameState.plane.x, canvas.width - gameState.plane.width / 2));
791
+ gameState.plane.y = Math.max(gameState.plane.height / 2, Math.min(gameState.plane.y, canvas.height - gameState.plane.height / 2));
792
+
793
+ // 生成新星星
794
+ if (timestamp - gameState.lastStarTime > 2000 / gameState.difficulty) {
795
+ createStar();
796
+ gameState.lastStarTime = timestamp;
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
820
+ );
821
+
822
+ // 更新碎片
823
+ gameState.debris.forEach(debris => {
824
+ debris.x += debris.speedX;
825
+ debris.y += debris.speedY;
826
+ debris.rotation += debris.rotationSpeed;
827
+ debris.opacity -= 0.02;
828
+ });
829
+ gameState.debris = gameState.debris.filter(debris => debris.opacity > 0);
830
+
831
+ // 更新跟踪导弹
832
+ gameState.homingMissiles.forEach(missile => {
833
+ if (!missile.target || missile.target.hit) {
834
+ // 如果没有目标或目标已被击中,则直线飞行
835
+ missile.x += Math.cos(missile.angle) * missile.speed;
836
+ missile.y += Math.sin(missile.angle) * missile.speed;
837
+ } else {
838
+ // 计算新的角度以跟踪目标
839
+ const dx = missile.target.x - missile.x;
840
+ const dy = missile.target.y - missile.y;
841
+ const targetAngle = Math.atan2(dy, dx);
842
+
843
+ // 平滑转向
844
+ let angleDiff = targetAngle - missile.angle;
845
+ if (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
846
+ if (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
847
+
848
+ missile.angle += angleDiff * 0.1;
849
+ missile.x += Math.cos(missile.angle) * missile.speed;
850
+ missile.y += Math.sin(missile.angle) * missile.speed;
851
+
852
+ // 检测碰撞
853
+ const missileRect = {
854
+ x: missile.x - missile.size / 2,
855
+ y: missile.y - missile.size / 2,
856
+ width: missile.size,
857
+ height: missile.size
858
+ };
859
+
860
+ const targetRect = {
861
+ x: missile.target.x - missile.target.collisionWidth / 2,
862
+ y: missile.target.y - missile.target.collisionHeight / 2,
863
+ width: missile.target.collisionWidth,
864
+ height: missile.target.collisionHeight
865
+ };
866
+
867
+ if (checkCollision(missileRect, targetRect)) {
868
+ missile.hit = true;
869
+ missile.target.health -= 3; // 导弹伤害更高
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,
881
+ y: missile.target.y + (Math.random() - 0.5) * 50,
882
+ width: missile.target.width / 2,
883
+ height: missile.target.height / 2,
884
+ collisionWidth: missile.target.collisionWidth / 2,
885
+ collisionHeight: missile.target.collisionHeight / 2,
886
+ speed: missile.target.speed * 1.2,
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
+ // 创建爆炸效果
900
+ createExplosion(missile.x, missile.y);
901
+ }
902
+ }
903
+ });
904
+ gameState.homingMissiles = gameState.homingMissiles.filter(missile =>
905
+ missile.x < canvas.width && missile.x > 0 &&
906
+ missile.y < canvas.height && missile.y > 0 &&
907
+ !missile.hit
908
+ );
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 = {
922
+ x: gameState.plane.x - gameState.plane.width / 2,
923
+ y: gameState.plane.y - gameState.plane.height / 2,
924
+ width: gameState.plane.width,
925
+ height: gameState.plane.height
926
+ };
927
+
928
+ const powerupRect = {
929
+ x: powerup.x - powerup.size / 2,
930
+ y: powerup.y - powerup.size / 2,
931
+ width: powerup.size,
932
+ height: powerup.size
933
+ };
934
+
935
+ if (checkCollision(planeRect, powerupRect) && !gameState.gameOver) {
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,
960
+ width: gameState.plane.width,
961
+ height: gameState.plane.height
962
+ };
963
+
964
+ const starRect = {
965
+ x: star.x - star.size / 2,
966
+ y: star.y - star.size / 2,
967
+ width: star.size,
968
+ height: star.size
969
+ };
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 = {
985
+ x: gameState.plane.x - gameState.plane.width / 2,
986
+ y: gameState.plane.y - gameState.plane.height / 2,
987
+ width: gameState.plane.width,
988
+ height: gameState.plane.height
989
+ };
990
+
991
+ const obstacleRect = {
992
+ x: obstacle.x - obstacle.collisionWidth / 2,
993
+ y: obstacle.y - obstacle.collisionHeight / 2,
994
+ width: obstacle.collisionWidth,
995
+ height: obstacle.collisionHeight
996
+ };
997
+
998
+ if (checkCollision(planeRect, obstacleRect) && !gameState.gameOver) {
999
+ // 如果有保护罩则不会受伤
1000
+ if (!gameState.plane.hasShield || gameState.plane.shieldDuration < Date.now()) {
1001
+ obstacle.hit = true;
1002
+ gameState.lives--;
1003
+ updateUI();
1004
+ createExplosion(gameState.plane.x, gameState.plane.y);
1005
+
1006
+ if (gameState.lives <= 0) {
1007
+ endGame();
1008
+ }
1009
+ } else {
1010
+ // 保护罩被击中
1011
+ obstacle.hit = true;
1012
+ createExplosion(obstacle.x, obstacle.y);
1013
+ createDebris(obstacle, 4);
1014
+ }
1015
+ }
1016
+
1017
+ // 检测与子弹的碰撞
1018
+ if (!obstacle.hit) {
1019
+ const bulletHits = [];
1020
+
1021
+ gameState.bullets.forEach((bullet, bulletIndex) => {
1022
+ const bulletRect = {
1023
+ x: bullet.x - bullet.size / 2,
1024
+ y: bullet.y - bullet.size / 2,
1025
+ width: bullet.size,
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,
1051
+ y: obstacle.y + (Math.random() - 0.5) * 50,
1052
+ width: obstacle.width / 2,
1053
+ height: obstacle.height / 2,
1054
+ collisionWidth: obstacle.collisionWidth / 2,
1055
+ collisionHeight: obstacle.collisionHeight / 2,
1056
+ speed: obstacle.speed * 1.2,
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);
1074
+ }
1075
+ }
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;
1082
+ explosion.alpha -= 0.02;
1083
+ });
1084
+ gameState.explosions = gameState.explosions.filter(explosion => explosion.alpha > 0);
1085
+ }
1086
+
1087
+ // 绘制游戏元素
1088
+ function drawGame() {
1089
+ // 绘制背景渐变
1090
+ const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
1091
+ gradient.addColorStop(0, '#1e3c72');
1092
+ gradient.addColorStop(1, '#2a5298');
1093
+ ctx.fillStyle = gradient;
1094
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
1095
+
1096
+ // 绘制云朵
1097
+ gameState.clouds.forEach(cloud => {
1098
+ cloud.parts.forEach(part => {
1099
+ ctx.beginPath();
1100
+ ctx.arc(
1101
+ cloud.x + part.offsetX,
1102
+ cloud.y + part.offsetY,
1103
+ part.size / 2,
1104
+ 0,
1105
+ Math.PI * 2
1106
+ );
1107
+ ctx.fillStyle = `rgba(255, 255, 255, ${0.7 + Math.random() * 0.3})`;
1108
+ ctx.fill();
1109
+ });
1110
+ });
1111
+
1112
+ // 绘制碎片
1113
+ gameState.debris.forEach(debris => {
1114
+ ctx.save();
1115
+ ctx.translate(debris.x, debris.y);
1116
+ ctx.rotate(debris.rotation);
1117
+
1118
+ ctx.fillStyle = `rgba(100, 100, 100, ${debris.opacity})`;
1119
+ ctx.fillRect(
1120
+ -debris.width / 2,
1121
+ -debris.height / 2,
1122
+ debris.width,
1123
+ debris.height
1124
+ );
1125
+
1126
+ ctx.restore();
1127
+ });
1128
+
1129
+ // 绘制道具
1130
+ gameState.powerups.forEach(powerup => {
1131
+ ctx.save();
1132
+ ctx.translate(powerup.x, powerup.y);
1133
+
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();
1155
+
1156
+ // 绘制道具图标
1157
+ ctx.fillStyle = 'white';
1158
+ ctx.font = '20px FontAwesome';
1159
+ ctx.textAlign = 'center';
1160
+ ctx.textBaseline = 'middle';
1161
+ ctx.fillText(String.fromCharCode(parseInt(getIconCode(powerup.type.icon), 16)), 0, 1);
1162
+
1163
+ ctx.restore();
1164
+ });
1165
+
1166
+ // 绘制跟踪导弹
1167
+ gameState.homingMissiles.forEach(missile => {
1168
+ ctx.save();
1169
+ ctx.translate(missile.x, missile.y);
1170
+ ctx.rotate(missile.angle);
1171
+
1172
+ // 导弹主体
1173
+ ctx.fillStyle = 'red';
1174
+ ctx.beginPath();
1175
+ ctx.moveTo(missile.size / 2, 0);
1176
+ ctx.lineTo(-missile.size / 2, -missile.size / 3);
1177
+ ctx.lineTo(-missile.size / 2, missile.size / 3);
1178
+ ctx.closePath();
1179
+ ctx.fill();
1180
+
1181
+ // 火焰效果
1182
+ ctx.fillStyle = 'orange';
1183
+ ctx.beginPath();
1184
+ ctx.moveTo(-missile.size / 2, -missile.size / 4);
1185
+ ctx.lineTo(-missile.size, 0);
1186
+ ctx.lineTo(-missile.size / 2, missile.size / 4);
1187
+ ctx.closePath();
1188
+ ctx.fill();
1189
+
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);
1211
+ ctx.strokeStyle = 'rgba(255, 200, 0, 0.8)';
1212
+ ctx.lineWidth = bullet.size / 2;
1213
+ ctx.stroke();
1214
+ });
1215
+
1216
+ // 绘制星星
1217
+ gameState.stars.forEach(star => {
1218
+ ctx.save();
1219
+ ctx.translate(star.x, star.y);
1220
+ ctx.rotate(star.rotation);
1221
+
1222
+ ctx.beginPath();
1223
+ for (let i = 0; i < 5; i++) {
1224
+ const angle = (i * 2 * Math.PI / 5) - Math.PI / 2;
1225
+ const innerAngle = angle + Math.PI / 5;
1226
+ const outerRadius = star.size / 2;
1227
+ const innerRadius = star.size / 4;
1228
+
1229
+ if (i === 0) {
1230
+ ctx.moveTo(
1231
+ Math.cos(angle) * outerRadius,
1232
+ Math.sin(angle) * outerRadius
1233
+ );
1234
+ } else {
1235
+ ctx.lineTo(
1236
+ Math.cos(angle) * outerRadius,
1237
+ Math.sin(angle) * outerRadius
1238
+ );
1239
+ }
1240
+
1241
+ ctx.lineTo(
1242
+ Math.cos(innerAngle) * innerRadius,
1243
+ Math.sin(innerAngle) * innerRadius
1244
+ );
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();
1255
+
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);
1378
+
1379
+ // 绘制保护罩
1380
+ if (gameState.plane.hasShield && gameState.plane.shieldDuration > Date.now()) {
1381
+ ctx.beginPath();
1382
+ ctx.arc(0, 0, 45, 0, Math.PI * 2);
1383
+ ctx.strokeStyle = `rgba(0, 204, 255, ${0.3 + Math.sin(Date.now() / 200) * 0.3})`;
1384
+ ctx.lineWidth = 3;
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
+
1395
+ // 飞机主体
1396
+ ctx.beginPath();
1397
+ ctx.moveTo(30, 0);
1398
+ ctx.lineTo(-20, -15);
1399
+ ctx.lineTo(-25, 0);
1400
+ ctx.lineTo(-20, 15);
1401
+ ctx.closePath();
1402
+ ctx.fillStyle = '#e74c3c';
1403
+ ctx.fill();
1404
+
1405
+ // 飞机窗户
1406
+ ctx.beginPath();
1407
+ ctx.arc(10, 0, 5, 0, Math.PI * 2);
1408
+ ctx.fillStyle = '#3498db';
1409
+ ctx.fill();
1410
+
1411
+ // 飞机机翼
1412
+ ctx.beginPath();
1413
+ ctx.moveTo(5, 0);
1414
+ ctx.lineTo(-5, -20);
1415
+ ctx.lineTo(-15, -20);
1416
+ ctx.lineTo(-5, 0);
1417
+ ctx.closePath();
1418
+ ctx.fillStyle = '#c0392b';
1419
+ ctx.fill();
1420
+
1421
+ ctx.beginPath();
1422
+ ctx.moveTo(5, 0);
1423
+ ctx.lineTo(-5, 20);
1424
+ ctx.lineTo(-15, 20);
1425
+ ctx.lineTo(-5, 0);
1426
+ ctx.closePath();
1427
+ ctx.fillStyle = '#c0392b';
1428
+ ctx.fill();
1429
+
1430
+ // 飞机尾翼
1431
+ ctx.beginPath();
1432
+ ctx.moveTo(-20, 0);
1433
+ ctx.lineTo(-25, -10);
1434
+ ctx.lineTo(-30, -10);
1435
+ ctx.lineTo(-25, 0);
1436
+ ctx.closePath();
1437
+ ctx.fillStyle = '#a5281b';
1438
+ ctx.fill();
1439
+
1440
+ ctx.beginPath();
1441
+ ctx.moveTo(-20, 0);
1442
+ ctx.lineTo(-25, 10);
1443
+ ctx.lineTo(-30, 10);
1444
+ ctx.lineTo(-25, 0);
1445
+ ctx.closePath();
1446
+ ctx.fillStyle = '#a5281b';
1447
+ ctx.fill();
1448
+
1449
+ ctx.restore();
1450
+
1451
+ // 绘制爆炸效果
1452
+ gameState.explosions.forEach(explosion => {
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();
1470
+ });
1471
+
1472
+ // 绘制特效文字
1473
+ gameState.effects.forEach(effect => {
1474
+ const timePassed = Date.now() - effect.startTime;
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;
1494
+ const y = Math.random() * canvas.height;
1495
+ const length = Math.random() * 20 + 10;
1496
+ const angle = Math.atan2(
1497
+ gameState.plane.y - y,
1498
+ gameState.plane.x - x
1499
+ );
1500
+
1501
+ ctx.save();
1502
+ ctx.translate(x, y);
1503
+ ctx.rotate(angle);
1504
+
1505
+ ctx.beginPath();
1506
+ ctx.moveTo(0, 0);
1507
+ ctx.lineTo(length, 0);
1508
+ ctx.strokeStyle = `rgba(255, 255, 255, ${Math.random() * 0.5 + 0.3})`;
1509
+ ctx.lineWidth = 1;
1510
+ ctx.stroke();
1511
+
1512
+ ctx.restore();
1513
+ }
1514
+ }
1515
+
1516
+ // 绘制难度提示
1517
+ if (gameState.difficulty > 1.5) {
1518
+ ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
1519
+ ctx.font = '20px Arial';
1520
+ ctx.textAlign = 'right';
1521
+ ctx.fillText(`难度: ${gameState.difficulty.toFixed(1)}x`, canvas.width - 20, 30);
1522
+ }
1523
+ }
1524
+
1525
+ // 辅助函数: 获取FontAwesome图标的Unicode
1526
+ function getIconCode(iconClass) {
1527
+ const icons = {
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
+
1539
+ // 键盘控制
1540
+ document.addEventListener('keydown', (e) => {
1541
+ if (gameState.keys.hasOwnProperty(e.key)) {
1542
+ gameState.keys[e.key] = true;
1543
+ e.preventDefault();
1544
+ }
1545
+
1546
+ if (e.key === ' ' || e.key === 'Spacebar') { // 空格键射击
1547
+ gameState.keys.Space = true;
1548
+ e.preventDefault();
1549
+ }
1550
+ });
1551
+
1552
+ document.addEventListener('keyup', (e) => {
1553
+ if (gameState.keys.hasOwnProperty(e.key)) {
1554
+ gameState.keys[e.key] = false;
1555
+ e.preventDefault();
1556
+ }
1557
+
1558
+ if (e.key === ' ' || e.key === 'Spacebar') {
1559
+ gameState.keys.Space = false;
1560
+ e.preventDefault();
1561
+ }
1562
+ });
1563
+
1564
+ // 触摸控制按钮事件
1565
+ leftBtn.addEventListener('touchstart', (e) => {
1566
+ e.preventDefault();
1567
+ gameState.keys.ArrowLeft = true;
1568
+ });
1569
+ leftBtn.addEventListener('touchend', (e) => {
1570
+ e.preventDefault();
1571
+ gameState.keys.ArrowLeft = false;
1572
+ });
1573
+
1574
+ rightBtn.addEventListener('touchstart', (e) => {
1575
+ e.preventDefault();
1576
+ gameState.keys.ArrowRight = true;
1577
+ });
1578
+ rightBtn.addEventListener('touchend', (e) => {
1579
+ e.preventDefault();
1580
+ gameState.keys.ArrowRight = false;
1581
+ });
1582
+
1583
+ upBtn.addEventListener('touchstart', (e) => {
1584
+ e.preventDefault();
1585
+ gameState.keys.ArrowUp = true;
1586
+ });
1587
+ upBtn.addEventListener('touchend', (e) => {
1588
+ e.preventDefault();
1589
+ gameState.keys.ArrowUp = false;
1590
+ });
1591
+
1592
+ downBtn.addEventListener('touchstart', (e) => {
1593
+ e.preventDefault();
1594
+ gameState.keys.ArrowDown = true;
1595
+ });
1596
+ downBtn.addEventListener('touchend', (e) => {
1597
+ e.preventDefault();
1598
+ gameState.keys.ArrowDown = false;
1599
+ });
1600
+
1601
+ fireBtn.addEventListener('touchstart', (e) => {
1602
+ e.preventDefault();
1603
+ gameState.isFiring = true;
1604
+ });
1605
+ fireBtn.addEventListener('touchend', (e) => {
1606
+ e.preventDefault();
1607
+ gameState.isFiring = false;
1608
+ });
1609
+
1610
+ // 鼠标控制按钮事件(用于桌面浏览器测试)
1611
+ leftBtn.addEventListener('mousedown', (e) => {
1612
+ e.preventDefault();
1613
+ gameState.keys.ArrowLeft = true;
1614
+ });
1615
+ leftBtn.addEventListener('mouseup', (e) => {
1616
+ e.preventDefault();
1617
+ gameState.keys.ArrowLeft = false;
1618
+ });
1619
+ leftBtn.addEventListener('mouseleave', (e) => {
1620
+ e.preventDefault();
1621
+ gameState.keys.ArrowLeft = false;
1622
+ });
1623
+
1624
+ rightBtn.addEventListener('mousedown', (e) => {
1625
+ e.preventDefault();
1626
+ gameState.keys.ArrowRight = true;
1627
+ });
1628
+ rightBtn.addEventListener('mouseup', (e) => {
1629
+ e.preventDefault();
1630
+ gameState.keys.ArrowRight = false;
1631
+ });
1632
+ rightBtn.addEventListener('mouseleave', (e) => {
1633
+ e.preventDefault();
1634
+ gameState.keys.ArrowRight = false;
1635
+ });
1636
+
1637
+ upBtn.addEventListener('mousedown', (e) => {
1638
+ e.preventDefault();
1639
+ gameState.keys.ArrowUp = true;
1640
+ });
1641
+ upBtn.addEventListener('mouseup', (e) => {
1642
+ e.preventDefault();
1643
+ gameState.keys.ArrowUp = false;
1644
+ });
1645
+ upBtn.addEventListener('mouseleave', (e) => {
1646
+ e.preventDefault();
1647
+ gameState.keys.ArrowUp = false;
1648
+ });
1649
+
1650
+ downBtn.addEventListener('mousedown', (e) => {
1651
+ e.preventDefault();
1652
+ gameState.keys.ArrowDown = true;
1653
+ });
1654
+ downBtn.addEventListener('mouseup', (e) => {
1655
+ e.preventDefault();
1656
+ gameState.keys.ArrowDown = false;
1657
+ });
1658
+ downBtn.addEventListener('mouseleave', (e) => {
1659
+ e.preventDefault();
1660
+ gameState.keys.ArrowDown = false;
1661
+ });
1662
+
1663
+ fireBtn.addEventListener('mousedown', (e) => {
1664
+ e.preventDefault();
1665
+ gameState.isFiring = true;
1666
+ });
1667
+ fireBtn.addEventListener('mouseup', (e) => {
1668
+ e.preventDefault();
1669
+ gameState.isFiring = false;
1670
+ });
1671
+ fireBtn.addEventListener('mouseleave', (e) => {
1672
+ e.preventDefault();
1673
+ gameState.isFiring = false;
1674
+ });
1675
+
1676
+ // 按钮事件
1677
+ startButton.addEventListener('click', initGame);
1678
+ restartButton.addEventListener('click', initGame);
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>