zdwalter commited on
Commit
b530a8e
·
verified ·
1 Parent(s): fc65d71

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +2251 -37
index.html CHANGED
@@ -1,41 +1,2255 @@
1
  <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>My app</title>
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <meta charset="utf-8">
 
 
7
  <style>
8
- body {
9
- display: flex;
10
- justify-content: center;
11
- align-items: center;
12
- overflow: hidden;
13
- height: 100dvh;
14
- font-family: "Arial", sans-serif;
15
- text-align: center;
16
- }
17
- .arrow {
18
- position: absolute;
19
- bottom: 32px;
20
- left: 0px;
21
- width: 100px;
22
- transform: rotate(30deg);
23
- }
24
- h1 {
25
- font-size: 50px;
26
- }
27
- h1 span {
28
- color: #acacac;
29
- font-size: 32px;
30
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  </style>
32
- </head>
33
- <body>
34
- <h1>
35
- <span>I'm ready to work,</span><br />
36
- Ask me anything.
37
- </h1>
38
- <img src="https://enzostvs-deepsite.hf.space/arrow.svg" class="arrow" />
39
- <script></script>
40
- <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/debug" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  </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
+ margin: 0;
14
+ padding: 0;
15
+ }
16
+ #gameCanvas {
17
+ display: block;
18
+ background: linear-gradient(to bottom, #1e3c72 0%, #2a5298 100%);
19
+ }
20
+ .game-overlay {
21
+ position: absolute;
22
+ top: 0;
23
+ left: 0;
24
+ width: 100%;
25
+ height: 100%;
26
+ pointer-events: none;
27
+ }
28
+ .cloud {
29
+ position: absolute;
30
+ background-color: rgba(255, 255, 255, 0.8);
31
+ border-radius: 50%;
32
+ }
33
+ @keyframes float {
34
+ 0% { transform: translateY(0px); }
35
+ 50% { transform: translateY(-10px); }
36
+ 100% { transform: translateY(0px); }
37
+ }
38
+ .plane {
39
+ animation: float 2s ease-in-out infinite;
40
+ }
41
+ .star {
42
+ position: absolute;
43
+ color: gold;
44
+ text-shadow: 0 0 10px yellow;
45
+ animation: twinkle 1s ease-in-out infinite alternate;
46
+ }
47
+ @keyframes twinkle {
48
+ from { opacity: 0.7; transform: scale(0.9); }
49
+ to { opacity: 1; transform: scale(1.1); }
50
+ }
51
+ .obstacle {
52
+ position: absolute;
53
+ background-color: #555;
54
+ border-radius: 5px;
55
+ }
56
+ .explosion {
57
+ position: absolute;
58
+ width: 60px;
59
+ height: 60px;
60
+ background: radial-gradient(circle, rgba(255,100,0,0.8) 0%, rgba(255,200,0,0.6) 50%, rgba(255,255,255,0) 70%);
61
+ border-radius: 50%;
62
+ animation: explode 0.5s ease-out forwards;
63
+ }
64
+ @keyframes explode {
65
+ 0% { transform: scale(0); opacity: 1; }
66
+ 100% { transform: scale(2); opacity: 0; }
67
+ }
68
+ .control-btn {
69
+ position: absolute;
70
+ width: 60px;
71
+ height: 60px;
72
+ background: rgba(255, 255, 255, 0.2);
73
+ border-radius: 50%;
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ font-size: 24px;
78
+ color: white;
79
+ pointer-events: auto;
80
+ user-select: none;
81
+ -webkit-tap-highlight-color: transparent;
82
+ }
83
+ .control-btn:active {
84
+ background: rgba(255, 255, 255, 0.4);
85
+ transform: scale(0.95);
86
+ }
87
+ #leftBtn {
88
+ bottom: 30px;
89
+ left: 30px;
90
+ }
91
+ #rightBtn {
92
+ bottom: 30px;
93
+ left: 110px;
94
+ }
95
+ #upBtn {
96
+ bottom: 100px;
97
+ right: 30px;
98
+ }
99
+ #downBtn {
100
+ bottom: 30px;
101
+ right: 30px;
102
+ }
103
+ #fireBtn {
104
+ bottom: 170px;
105
+ left: 30px;
106
+ }
107
+ .bullet {
108
+ position: absolute;
109
+ background: linear-gradient(to right, #ff0, #f80);
110
+ border-radius: 50%;
111
+ }
112
+ .debris {
113
+ position: absolute;
114
+ background-color: #777;
115
+ border-radius: 2px;
116
+ }
117
+ .powerup {
118
+ position: absolute;
119
+ width: 40px;
120
+ height: 40px;
121
+ border-radius: 50%;
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ font-size: 20px;
126
+ text-shadow: 0 0 5px white;
127
+ }
128
+ .shield {
129
+ position: absolute;
130
+ border-radius: 50%;
131
+ border: 3px solid rgba(0, 204, 255, 0.6);
132
+ pointer-events: none;
133
+ }
134
+ .homing-missile {
135
+ position: absolute;
136
+ background: linear-gradient(to bottom, #ff5f5f, #ff0000);
137
+ border-radius: 50% 50% 0 0;
138
+ transform-origin: center bottom;
139
+ }
140
+ @keyframes pulse {
141
+ 0% { transform: scale(1); opacity: 0.9; }
142
+ 50% { transform: scale(1.1); opacity: 1; }
143
+ 100% { transform: scale(1); opacity: 0.9; }
144
+ }
145
+ .powerup-effect {
146
+ position: absolute;
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">
189
+ <div class="relative w-full h-full">
190
+ <canvas id="gameCanvas" class="w-full h-full"></canvas>
191
+
192
+ <!-- 开始界面 -->
193
+ <div id="startScreen" class="game-overlay flex flex-col items-center justify-center bg-black bg-opacity-70">
194
+ <h1 class="text-5xl font-bold mb-6 text-yellow-300">SKY ADVENTURE</h1>
195
+ <div class="plane text-6xl mb-8">✈️</div>
196
+ <p class="text-xl mb-8 text-center max-w-md px-4">控制飞机躲避障碍物<br>收集星星获得高分!<br>按射击按钮消灭障碍物!<br>收集道具获得特殊能力!</p>
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>
204
+ </div>
205
+ <div class="flex items-center">
206
+ <div class="powerup bg-purple-500 mr-2"><i class="fas fa-rocket"></i></div>
207
+ <span>跟踪导弹</span>
208
+ </div>
209
+ <div class="flex items-center">
210
+ <div class="powerup bg-blue-500 mr-2"><i class="fas fa-shield-alt"></i></div>
211
+ <span>保护罩</span>
212
+ </div>
213
+ <div class="flex items-center">
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
+
235
+ <!-- 游戏UI -->
236
+ <div id="gameUI" class="game-overlay hidden">
237
+ <div class="absolute top-4 left-4 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
238
+ <div class="flex items-center">
239
+ <i class="fas fa-star text-yellow-400 mr-2"></i>
240
+ <span id="scoreDisplay" class="text-xl">0</span>
241
+ </div>
242
+ </div>
243
+ <div class="absolute top-4 right-4 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
244
+ <div class="flex items-center">
245
+ <i class="fas fa-heart text-red-500 mr-2"></i>
246
+ <span id="livesDisplay" class="text-xl">3</span>
247
+ </div>
248
+ </div>
249
+ <div class="absolute top-4 left-1/2 transform -translate-x-1/2 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
250
+ <div class="flex items-center">
251
+ <i class="fas fa-bolt text-yellow-400 mr-2"></i>
252
+ <span id="ammoDisplay" class="text-xl">∞</span>
253
+ </div>
254
+ </div>
255
+ <div class="absolute bottom-4 left-4 bg-black bg-opacity-50 px-4 py-2 rounded-lg">
256
+ <div class="flex items-center">
257
+ <i class="fas fa-tachometer-alt text-blue-400 mr-2"></i>
258
+ <span id="speedDisplay" class="text-xl">100</span>
259
+ <span class="ml-1">km/h</span>
260
+ </div>
261
+ </div>
262
+
263
+ <!-- 主动技能图标 -->
264
+ <div id="powerupStatus" class="absolute bottom-24 right-4 flex gap-2">
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>
276
+ </div>
277
+ <div id="rightBtn" class="control-btn hidden">
278
+ <i class="fas fa-arrow-right"></i>
279
+ </div>
280
+ <div id="upBtn" class="control-btn hidden">
281
+ <i class="fas fa-arrow-up"></i>
282
+ </div>
283
+ <div id="downBtn" class="control-btn hidden">
284
+ <i class="fas fa-arrow-down"></i>
285
+ </div>
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
+ <!-- 游戏结束界面 -->
297
+ <div id="gameOverScreen" class="game-overlay hidden flex flex-col items-center justify-center bg-black bg-opacity-70">
298
+ <h1 class="text-5xl font-bold mb-6 text-red-500">GAME OVER</h1>
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 = {
319
+ started: false,
320
+ gameOver: false,
321
+ score: 0,
322
+ lives: 3,
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,
334
+ width: 60,
335
+ height: 60,
336
+ velocity: 0, // 左右方向速度
337
+ verticalVelocity: 0, // 上下方向速度
338
+ rotation: 0,
339
+ lastFireTime: 0,
340
+ fireRate: 200, // 射击间隔(ms)
341
+ bulletDamage: 1, // 子弹伤害
342
+ hasShield: false,
343
+ shieldDuration: 0,
344
+ powerups: {
345
+ rapidFire: 0, // 火力增强
346
+ homingMissiles: 0, // 跟踪导弹
347
+ }
348
+ },
349
+ stars: [],
350
+ obstacles: [],
351
+ clouds: [],
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,
368
+ ArrowLeft: 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
+ // 道具类型
386
+ const POWERUP_TYPES = {
387
+ RAPID_FIRE: {
388
+ id: 'rapidFire',
389
+ icon: 'fas fa-bolt',
390
+ color: 'red',
391
+ duration: 10000, // 10秒
392
+ effect: (game) => {
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: {
400
+ id: 'homingMissiles',
401
+ icon: 'fas fa-rocket',
402
+ color: 'purple',
403
+ duration: 10000, // 10秒
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: {
411
+ id: 'shield',
412
+ icon: 'fas fa-shield-alt',
413
+ color: 'blue',
414
+ duration: 8000, // 8秒
415
+ effect: (game) => {
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: {
423
+ id: 'health',
424
+ icon: 'fas fa-heart',
425
+ color: 'green',
426
+ effect: (game) => {
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
+ };
471
+
472
+ // 获取DOM元素
473
+ const canvas = document.getElementById('gameCanvas');
474
+ const ctx = canvas.getContext('2d');
475
+ const startScreen = document.getElementById('startScreen');
476
+ const gameUI = document.getElementById('gameUI');
477
+ const gameOverScreen = document.getElementById('gameOverScreen');
478
+ const startButton = document.getElementById('startButton');
479
+ const restartButton = document.getElementById('restartButton');
480
+ const scoreDisplay = document.getElementById('scoreDisplay');
481
+ const livesDisplay = document.getElementById('livesDisplay');
482
+ const ammoDisplay = document.getElementById('ammoDisplay');
483
+ const speedDisplay = document.getElementById('speedDisplay');
484
+ const finalScore = document.getElementById('finalScore');
485
+ const powerupStatus = document.getElementById('powerupStatus');
486
+ const leftBtn = document.getElementById('leftBtn');
487
+ const rightBtn = document.getElementById('rightBtn');
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() {
518
+ return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
519
+ }
520
+
521
+ // 设置画布大小
522
+ function resizeCanvas() {
523
+ canvas.width = window.innerWidth;
524
+ canvas.height = window.innerHeight;
525
+ if (gameState.started && !gameState.gameOver) {
526
+ gameState.plane.x = canvas.width / 2;
527
+ gameState.plane.y = canvas.height / 2;
528
+ }
529
+ }
530
+
531
+ // 创建文字特效
532
+ function createEffect(text, color, duration) {
533
+ gameState.effects.push({
534
+ text,
535
+ color,
536
+ x: gameState.plane.x,
537
+ y: gameState.plane.y - 50,
538
+ alpha: 1,
539
+ duration,
540
+ startTime: Date.now()
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 = '';
569
+
570
+ if (gameState.plane.powerups.rapidFire > Date.now()) {
571
+ const timeLeft = Math.ceil((gameState.plane.powerups.rapidFire - Date.now()) / 1000);
572
+ powerupStatus.innerHTML += `
573
+ <div class="bg-black bg-opacity-50 px-3 py-1 rounded-lg flex items-center" title="火力增强 (${timeLeft}s)">
574
+ <i class="fas fa-bolt text-red-500 mr-2"></i>
575
+ </div>
576
+ `;
577
+ }
578
+
579
+ if (gameState.plane.powerups.homingMissiles > Date.now()) {
580
+ const timeLeft = Math.ceil((gameState.plane.powerups.homingMissiles - Date.now()) / 1000);
581
+ powerupStatus.innerHTML += `
582
+ <div class="bg-black bg-opacity-50 px-3 py-1 rounded-lg flex items-center" title="跟踪导弹 (${timeLeft}s)">
583
+ <i class="fas fa-rocket text-purple-500 mr-2"></i>
584
+ </div>
585
+ `;
586
+ }
587
+
588
+ if (gameState.plane.hasShield && gameState.plane.shieldDuration > Date.now()) {
589
+ const timeLeft = Math.ceil((gameState.plane.shieldDuration - Date.now()) / 1000);
590
+ powerupStatus.innerHTML += `
591
+ <div class="bg-black bg-opacity-50 px-3 py-1 rounded-lg flex items-center" title="保护罩 (${timeLeft}s)">
592
+ <i class="fas fa-shield-alt text-blue-500 mr-2"></i>
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
+ // 初始化游戏
625
+ function initGame() {
626
+ gameState.isMobile = detectMobile();
627
+ resizeCanvas();
628
+ gameState.started = true;
629
+ gameState.gameOver = false;
630
+ gameState.score = 0;
631
+ gameState.lives = 3;
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,
643
+ width: 60,
644
+ height: 60,
645
+ velocity: 0,
646
+ verticalVelocity: 0,
647
+ rotation: 0,
648
+ lastFireTime: 0,
649
+ fireRate: 200,
650
+ bulletDamage: 1,
651
+ hasShield: false,
652
+ shieldDuration: 0,
653
+ powerups: {
654
+ rapidFire: 0,
655
+ homingMissiles: 0,
656
+ }
657
+ };
658
+ gameState.stars = [];
659
+ gameState.obstacles = [];
660
+ gameState.clouds = [];
661
+ gameState.explosions = [];
662
+ gameState.bullets = [];
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');
683
+ gameUI.classList.remove('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
+
701
+ // 创建初始云朵
702
+ function createInitialClouds() {
703
+ for (let i = 0; i < 10; i++) {
704
+ createCloud(true);
705
+ }
706
+ }
707
+
708
+ // 创建云朵
709
+ function createCloud(initial = false) {
710
+ const size = Math.random() * 60 + 40;
711
+ const x = initial ? Math.random() * canvas.width : canvas.width + size;
712
+ const y = Math.random() * canvas.height;
713
+ const speed = Math.random() * 1 + 0.5;
714
+
715
+ gameState.clouds.push({
716
+ x,
717
+ y,
718
+ size,
719
+ speed,
720
+ parts: Array(3).fill().map(() => ({
721
+ size: size * (Math.random() * 0.3 + 0.7),
722
+ offsetX: (Math.random() - 0.5) * size * 0.6,
723
+ offsetY: (Math.random() - 0.5) * size * 0.6
724
+ }))
725
+ });
726
+ }
727
+
728
+ // 创建星星
729
+ function createStar() {
730
+ const size = Math.random() * 20 + 15;
731
+ const x = canvas.width + size;
732
+ const y = Math.random() * (canvas.height - size * 2) + size;
733
+ const speed = Math.random() * 3 + 3 + gameState.speed / 50;
734
+
735
+ gameState.stars.push({
736
+ x,
737
+ y,
738
+ size,
739
+ speed,
740
+ rotation: 0,
741
+ rotationSpeed: Math.random() * 0.1 - 0.05
742
+ });
743
+ }
744
+
745
+ // 创建障碍物(敌人)
746
+ function createObstacle() {
747
+ const width = Math.random() * 80 + 40;
748
+ const height = Math.random() * 80 + 40;
749
+ const x = canvas.width + width;
750
+ const y = Math.random() * (canvas.height - height);
751
+ const speed = Math.random() * 2 + 2 + gameState.speed / 50;
752
+ const type = Math.random() > 0.5 ? 'rectangle' : 'circle';
753
+ const health = type === 'rectangle' ? (width > 80 ? 3 : 2) : 1;
754
+
755
+ // 碰撞体积比实际显示大20%
756
+ const collisionWidth = width * 1.2;
757
+ const collisionHeight = height * 1.2;
758
+
759
+ gameState.obstacles.push({
760
+ x,
761
+ y,
762
+ width,
763
+ height,
764
+ collisionWidth,
765
+ collisionHeight,
766
+ speed,
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;
812
+ const x = canvas.width + size;
813
+ const y = Math.random() * (canvas.height - size * 2) + size;
814
+ const speed = Math.random() * 2 + 1;
815
+
816
+ // 随机选择一种道具类型
817
+ const powerupKeys = Object.keys(POWERUP_TYPES);
818
+ const randomPowerup = POWERUP_TYPES[powerupKeys[Math.floor(Math.random() * powerupKeys.length)]];
819
+
820
+ gameState.powerups.push({
821
+ x,
822
+ y,
823
+ size,
824
+ speed,
825
+ type: randomPowerup
826
+ });
827
+ }
828
+
829
+ // 创建跟踪导弹
830
+ function createHomingMissile() {
831
+ if (gameState.obstacles.length === 0) return; // 没有目标时不开火
832
+
833
+ // 找到最近的障碍物作为目标
834
+ let closestObstacle = null;
835
+ let minDistance = Infinity;
836
+
837
+ gameState.obstacles.forEach(obstacle => {
838
+ const dx = obstacle.x - gameState.plane.x;
839
+ const dy = obstacle.y - gameState.plane.y;
840
+ const distance = Math.sqrt(dx * dx + dy * dy);
841
+
842
+ if (distance < minDistance) {
843
+ minDistance = distance;
844
+ closestObstacle = obstacle;
845
+ }
846
+ });
847
+
848
+ if (!closestObstacle) return;
849
+
850
+ const size = 12;
851
+ gameState.homingMissiles.push({
852
+ x: gameState.plane.x,
853
+ y: gameState.plane.y,
854
+ size,
855
+ speed: 8,
856
+ target: closestObstacle,
857
+ angle: Math.atan2(
858
+ closestObstacle.y - gameState.plane.y,
859
+ closestObstacle.x - gameState.plane.x
860
+ )
861
+ });
862
+ }
863
+
864
+ // 创建碎片
865
+ function createDebris(obstacle, count = 5) {
866
+ for (let i = 0; i < count; i++) {
867
+ gameState.debris.push({
868
+ x: obstacle.x,
869
+ y: obstacle.y,
870
+ width: obstacle.width / 3,
871
+ height: obstacle.height / 3,
872
+ speedX: (Math.random() - 0.5) * 4,
873
+ speedY: (Math.random() - 0.5) * 4,
874
+ rotation: 0,
875
+ rotationSpeed: (Math.random() - 0.5) * 0.2,
876
+ opacity: 1
877
+ });
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
+
891
+ gameState.bullets.push({
892
+ x,
893
+ y,
894
+ size,
895
+ speed,
896
+ damage
897
+ });
898
+
899
+ // 如果有跟踪导弹能力且冷却结束,随机几率触发
900
+ if (gameState.plane.powerups.homingMissiles > Date.now() &&
901
+ Math.random() > 0.7) { // 70%概率发射跟踪导弹
902
+ requestAnimationFrame(createHomingMissile);
903
+ }
904
+
905
+ // 如果弹药不是无限的,减少弹药
906
+ if (gameState.ammo !== Infinity) {
907
+ gameState.ammo--;
908
+ }
909
+
910
+ playSound('shootSound');
911
+ updateUI();
912
+
913
+ return true;
914
+ }
915
+
916
+ // 创建爆炸效果
917
+ function createExplosion(x, y) {
918
+ gameState.explosions.push({
919
+ x,
920
+ y,
921
+ size: 0,
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 &&
948
+ rect1.x + rect1.width > rect2.x &&
949
+ rect1.y < rect2.y + rect2.height &&
950
+ rect1.y + rect1.height > rect2.y
951
+ );
952
+ }
953
+
954
+ // 游戏结束
955
+ function endGame() {
956
+ gameState.gameOver = true;
957
+ gameUI.classList.add('hidden');
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
+ // 游戏主循环
994
+ function gameLoop(timestamp) {
995
+ if (!gameState.started || gameState.gameOver) return;
996
+
997
+ // 清除画布
998
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
999
+
1000
+ // 更新游戏状态
1001
+ updateGame(timestamp);
1002
+
1003
+ // 绘制游戏元素
1004
+ drawGame();
1005
+
1006
+ // 继续循环
1007
+ requestAnimationFrame(gameLoop);
1008
+ }
1009
+
1010
+ // 更新游戏状态
1011
+ function updateGame(timestamp) {
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; // 恢复默认射击速度
1039
+ createEffect('火力增强结束', 'red', 1500);
1040
+ }
1041
+
1042
+ if (gameState.plane.powerups.homingMissiles > 0 && gameState.plane.powerups.homingMissiles < Date.now()) {
1043
+ gameState.plane.powerups.homingMissiles = 0;
1044
+ createEffect('跟踪导弹结束', 'purple', 1500);
1045
+ }
1046
+
1047
+ if (gameState.plane.hasShield && gameState.plane.shieldDuration < Date.now()) {
1048
+ gameState.plane.hasShield = false;
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 {
1078
+ // 如果没有按左右键,飞机逐渐回正
1079
+ gameState.plane.rotation *= 0.95;
1080
+ gameState.plane.velocity *= 0.95;
1081
+ if (Math.abs(gameState.plane.rotation) < 0.5) gameState.plane.rotation = 0;
1082
+ if (Math.abs(gameState.plane.velocity) < 0.5) gameState.plane.velocity = 0;
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
+ // 如果没有按上下键,垂直速度逐渐归零
1092
+ gameState.plane.verticalVelocity *= 0.95;
1093
+ if (Math.abs(gameState.plane.verticalVelocity) < 0.5) gameState.plane.verticalVelocity = 0;
1094
+ }
1095
+
1096
+ // 更新飞机位置
1097
+ gameState.plane.x += gameState.plane.velocity;
1098
+ gameState.plane.y += gameState.plane.verticalVelocity;
1099
+
1100
+ // 限制飞机在屏幕内
1101
+ gameState.plane.x = Math.max(gameState.plane.width / 2, Math.min(gameState.plane.x, canvas.width - gameState.plane.width / 2));
1102
+ gameState.plane.y = Math.max(gameState.plane.height / 2, Math.min(gameState.plane.y, canvas.height - gameState.plane.height / 2));
1103
+
1104
+ // 生成新星星
1105
+ if (timestamp - gameState.lastStarTime > 2000 / gameState.difficulty) {
1106
+ createStar();
1107
+ gameState.lastStarTime = timestamp;
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
1139
+ );
1140
+
1141
+ // 更新碎片
1142
+ gameState.debris.forEach(debris => {
1143
+ debris.x += debris.speedX;
1144
+ debris.y += debris.speedY;
1145
+ debris.rotation += debris.rotationSpeed;
1146
+ debris.opacity -= 0.02;
1147
+ });
1148
+ gameState.debris = gameState.debris.filter(debris => debris.opacity > 0);
1149
+
1150
+ // 更新跟踪导弹
1151
+ gameState.homingMissiles.forEach(missile => {
1152
+ if (!missile.target || missile.target.hit) {
1153
+ // 如果没有目标或目标已被击中,则直线飞行
1154
+ missile.x += Math.cos(missile.angle) * missile.speed;
1155
+ missile.y += Math.sin(missile.angle) * missile.speed;
1156
+ } else {
1157
+ // 计算新的角度以跟踪目标
1158
+ const dx = missile.target.x - missile.x;
1159
+ const dy = missile.target.y - missile.y;
1160
+ const targetAngle = Math.atan2(dy, dx);
1161
+
1162
+ // 平滑转向
1163
+ let angleDiff = targetAngle - missile.angle;
1164
+ if (angleDiff > Math.PI) angleDiff -= Math.PI * 2;
1165
+ if (angleDiff < -Math.PI) angleDiff += Math.PI * 2;
1166
+
1167
+ missile.angle += angleDiff * 0.1;
1168
+ missile.x += Math.cos(missile.angle) * missile.speed;
1169
+ missile.y += Math.sin(missile.angle) * missile.speed;
1170
+
1171
+ // 检测碰撞
1172
+ const missileRect = {
1173
+ x: missile.x - missile.size / 2,
1174
+ y: missile.y - missile.size / 2,
1175
+ width: missile.size,
1176
+ height: missile.size
1177
+ };
1178
+
1179
+ const targetRect = {
1180
+ x: missile.target.x - missile.target.collisionWidth / 2,
1181
+ y: missile.target.y - missile.target.collisionHeight / 2,
1182
+ width: missile.target.collisionWidth,
1183
+ height: missile.target.collisionHeight
1184
+ };
1185
+
1186
+ if (checkCollision(missileRect, targetRect)) {
1187
+ missile.hit = true;
1188
+ missile.target.health -= 3; // 导弹伤害更高
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,
1201
+ y: missile.target.y + (Math.random() - 0.5) * 50,
1202
+ width: missile.target.width / 2,
1203
+ height: missile.target.height / 2,
1204
+ collisionWidth: missile.target.collisionWidth / 2,
1205
+ collisionHeight: missile.target.collisionHeight / 2,
1206
+ speed: missile.target.speed * 1.2,
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
+ // 创建爆炸效果
1228
+ createExplosion(missile.x, missile.y);
1229
+ }
1230
+ }
1231
+ });
1232
+ gameState.homingMissiles = gameState.homingMissiles.filter(missile =>
1233
+ missile.x < canvas.width && missile.x > 0 &&
1234
+ missile.y < canvas.height && missile.y > 0 &&
1235
+ !missile.hit
1236
+ );
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 = {
1250
+ x: gameState.plane.x - gameState.plane.width / 2,
1251
+ y: gameState.plane.y - gameState.plane.height / 2,
1252
+ width: gameState.plane.width,
1253
+ height: gameState.plane.height
1254
+ };
1255
+
1256
+ const powerupRect = {
1257
+ x: powerup.x - powerup.size / 2,
1258
+ y: powerup.y - powerup.size / 2,
1259
+ width: powerup.size,
1260
+ height: powerup.size
1261
+ };
1262
+
1263
+ if (checkCollision(planeRect, powerupRect) && !gameState.gameOver) {
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,
1289
+ width: gameState.plane.width,
1290
+ height: gameState.plane.height
1291
+ };
1292
+
1293
+ const starRect = {
1294
+ x: star.x - star.size / 2,
1295
+ y: star.y - star.size / 2,
1296
+ width: star.size,
1297
+ height: star.size
1298
+ };
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 = {
1325
+ x: gameState.plane.x - gameState.plane.width / 2,
1326
+ y: gameState.plane.y - gameState.plane.height / 2,
1327
+ width: gameState.plane.width,
1328
+ height: gameState.plane.height
1329
+ };
1330
+
1331
+ const obstacleRect = {
1332
+ x: obstacle.x - obstacle.collisionWidth / 2,
1333
+ y: obstacle.y - obstacle.collisionHeight / 2,
1334
+ width: obstacle.collisionWidth,
1335
+ height: obstacle.collisionHeight
1336
+ };
1337
+
1338
+ if (checkCollision(planeRect, obstacleRect) && !gameState.gameOver) {
1339
+ // 如果有保护罩则不会受伤
1340
+ if (!gameState.plane.hasShield || gameState.plane.shieldDuration < Date.now()) {
1341
+ obstacle.hit = true;
1342
+ gameState.lives--;
1343
+ updateUI();
1344
+ createExplosion(gameState.plane.x, gameState.plane.y);
1345
+
1346
+ if (gameState.lives <= 0) {
1347
+ endGame();
1348
+ }
1349
+ } else {
1350
+ // 保护罩被击中
1351
+ obstacle.hit = true;
1352
+ createExplosion(obstacle.x, obstacle.y);
1353
+ createDebris(obstacle, 4);
1354
+ }
1355
+ }
1356
+
1357
+ // 检测与玩家子弹的碰撞
1358
+ if (!obstacle.hit) {
1359
+ const bulletHits = [];
1360
+
1361
+ gameState.bullets.forEach((bullet, bulletIndex) => {
1362
+ const bulletRect = {
1363
+ x: bullet.x - bullet.size / 2,
1364
+ y: bullet.y - bullet.size / 2,
1365
+ width: bullet.size,
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,
1386
+ y: obstacle.y + (Math.random() - 0.5) * 50,
1387
+ width: obstacle.width / 2,
1388
+ height: obstacle.height / 2,
1389
+ collisionWidth: obstacle.collisionWidth / 2,
1390
+ collisionHeight: obstacle.collisionHeight / 2,
1391
+ speed: obstacle.speed * 1.2,
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);
1429
+ }
1430
+ }
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;
1484
+ explosion.alpha -= 0.02;
1485
+ });
1486
+ gameState.explosions = gameState.explosions.filter(explosion => explosion.alpha > 0);
1487
+ }
1488
+
1489
+ // 绘制游戏元素
1490
+ function drawGame() {
1491
+ // 绘制背景渐变
1492
+ const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
1493
+ gradient.addColorStop(0, '#1e3c72');
1494
+ gradient.addColorStop(1, '#2a5298');
1495
+ ctx.fillStyle = gradient;
1496
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
1497
+
1498
+ // 绘制云朵
1499
+ gameState.clouds.forEach(cloud => {
1500
+ cloud.parts.forEach(part => {
1501
+ ctx.beginPath();
1502
+ ctx.arc(
1503
+ cloud.x + part.offsetX,
1504
+ cloud.y + part.offsetY,
1505
+ part.size / 2,
1506
+ 0,
1507
+ Math.PI * 2
1508
+ );
1509
+ ctx.fillStyle = `rgba(255, 255, 255, ${0.7 + Math.random() * 0.3})`;
1510
+ ctx.fill();
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();
1527
+ ctx.translate(debris.x, debris.y);
1528
+ ctx.rotate(debris.rotation);
1529
+
1530
+ ctx.fillStyle = `rgba(100, 100, 100, ${debris.opacity})`;
1531
+ ctx.fillRect(
1532
+ -debris.width / 2,
1533
+ -debris.height / 2,
1534
+ debris.width,
1535
+ debris.height
1536
+ );
1537
+
1538
+ ctx.restore();
1539
+ });
1540
+
1541
+ // 绘制道具
1542
+ gameState.powerups.forEach(powerup => {
1543
+ ctx.save();
1544
+ ctx.translate(powerup.x, powerup.y);
1545
+
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();
1567
+
1568
+ // 绘制道具图标
1569
+ ctx.fillStyle = 'white';
1570
+ ctx.font = '20px FontAwesome';
1571
+ ctx.textAlign = 'center';
1572
+ ctx.textBaseline = 'middle';
1573
+ ctx.fillText(String.fromCharCode(parseInt(getIconCode(powerup.type.icon), 16)), 0, 1);
1574
+
1575
+ ctx.restore();
1576
+ });
1577
+
1578
+ // 绘制跟踪导弹
1579
+ gameState.homingMissiles.forEach(missile => {
1580
+ ctx.save();
1581
+ ctx.translate(missile.x, missile.y);
1582
+ ctx.rotate(missile.angle);
1583
+
1584
+ // 导弹主体
1585
+ ctx.fillStyle = 'red';
1586
+ ctx.beginPath();
1587
+ ctx.moveTo(missile.size / 2, 0);
1588
+ ctx.lineTo(-missile.size / 2, -missile.size / 3);
1589
+ ctx.lineTo(-missile.size / 2, missile.size / 3);
1590
+ ctx.closePath();
1591
+ ctx.fill();
1592
+
1593
+ // 火焰效果
1594
+ ctx.fillStyle = 'orange';
1595
+ ctx.beginPath();
1596
+ ctx.moveTo(-missile.size / 2, -missile.size / 4);
1597
+ ctx.lineTo(-missile.size, 0);
1598
+ ctx.lineTo(-missile.size / 2, missile.size / 4);
1599
+ ctx.closePath();
1600
+ ctx.fill();
1601
+
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);
1623
+ ctx.strokeStyle = 'rgba(255, 200, 0, 0.8)';
1624
+ ctx.lineWidth = bullet.size / 2;
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();
1642
+ ctx.translate(star.x, star.y);
1643
+ ctx.rotate(star.rotation);
1644
+
1645
+ ctx.beginPath();
1646
+ for (let i = 0; i < 5; i++) {
1647
+ const angle = (i * 2 * Math.PI / 5) - Math.PI / 2;
1648
+ const innerAngle = angle + Math.PI / 5;
1649
+ const outerRadius = star.size / 2;
1650
+ const innerRadius = star.size / 4;
1651
+
1652
+ if (i === 0) {
1653
+ ctx.moveTo(
1654
+ Math.cos(angle) * outerRadius,
1655
+ Math.sin(angle) * outerRadius
1656
+ );
1657
+ } else {
1658
+ ctx.lineTo(
1659
+ Math.cos(angle) * outerRadius,
1660
+ Math.sin(angle) * outerRadius
1661
+ );
1662
+ }
1663
+
1664
+ ctx.lineTo(
1665
+ Math.cos(innerAngle) * innerRadius,
1666
+ Math.sin(innerAngle) * innerRadius
1667
+ );
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();
1678
+
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);
1873
+
1874
+ // 绘制保护罩
1875
+ if (gameState.plane.hasShield && gameState.plane.shieldDuration > Date.now()) {
1876
+ ctx.beginPath();
1877
+ ctx.arc(0, 0, 45, 0, Math.PI * 2);
1878
+ ctx.strokeStyle = `rgba(0, 204, 255, ${0.3 + Math.sin(Date.now() / 200) * 0.3})`;
1879
+ ctx.lineWidth = 3;
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
+
1890
+ // 飞机主体
1891
+ ctx.beginPath();
1892
+ ctx.moveTo(30, 0);
1893
+ ctx.lineTo(-20, -15);
1894
+ ctx.lineTo(-25, 0);
1895
+ ctx.lineTo(-20, 15);
1896
+ ctx.closePath();
1897
+ ctx.fillStyle = '#e74c3c';
1898
+ ctx.fill();
1899
+
1900
+ // 飞机窗户
1901
+ ctx.beginPath();
1902
+ ctx.arc(10, 0, 5, 0, Math.PI * 2);
1903
+ ctx.fillStyle = '#3498db';
1904
+ ctx.fill();
1905
+
1906
+ // 飞机机翼
1907
+ ctx.beginPath();
1908
+ ctx.moveTo(5, 0);
1909
+ ctx.lineTo(-5, -20);
1910
+ ctx.lineTo(-15, -20);
1911
+ ctx.lineTo(-5, 0);
1912
+ ctx.closePath();
1913
+ ctx.fillStyle = '#c0392b';
1914
+ ctx.fill();
1915
+
1916
+ ctx.beginPath();
1917
+ ctx.moveTo(5, 0);
1918
+ ctx.lineTo(-5, 20);
1919
+ ctx.lineTo(-15, 20);
1920
+ ctx.lineTo(-5, 0);
1921
+ ctx.closePath();
1922
+ ctx.fillStyle = '#c0392b';
1923
+ ctx.fill();
1924
+
1925
+ // 飞机尾翼
1926
+ ctx.beginPath();
1927
+ ctx.moveTo(-20, 0);
1928
+ ctx.lineTo(-25, -10);
1929
+ ctx.lineTo(-30, -10);
1930
+ ctx.lineTo(-25, 0);
1931
+ ctx.closePath();
1932
+ ctx.fillStyle = '#a5281b';
1933
+ ctx.fill();
1934
+
1935
+ ctx.beginPath();
1936
+ ctx.moveTo(-20, 0);
1937
+ ctx.lineTo(-25, 10);
1938
+ ctx.lineTo(-30, 10);
1939
+ ctx.lineTo(-25, 0);
1940
+ ctx.closePath();
1941
+ ctx.fillStyle = '#a5281b';
1942
+ ctx.fill();
1943
+
1944
+ ctx.restore();
1945
+
1946
+ // 绘制爆炸效果
1947
+ gameState.explosions.forEach(explosion => {
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();
1965
+ });
1966
+
1967
+ // 绘制特效文字
1968
+ gameState.effects.forEach(effect => {
1969
+ const timePassed = Date.now() - effect.startTime;
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;
1987
+ const y = Math.random() * canvas.height;
1988
+ const length = Math.random() * 20 + 10;
1989
+ const angle = Math.atan2(
1990
+ gameState.plane.y - y,
1991
+ gameState.plane.x - x
1992
+ );
1993
+
1994
+ ctx.save();
1995
+ ctx.translate(x, y);
1996
+ ctx.rotate(angle);
1997
+
1998
+ ctx.beginPath();
1999
+ ctx.moveTo(0, 0);
2000
+ ctx.lineTo(length, 0);
2001
+ ctx.strokeStyle = `rgba(255, 255, 255, ${Math.random() * 0.5 + 0.3})`;
2002
+ ctx.lineWidth = 1;
2003
+ ctx.stroke();
2004
+
2005
+ ctx.restore();
2006
+ }
2007
+ }
2008
+
2009
+ // 绘制难度提示
2010
+ if (gameState.difficulty > 1.5) {
2011
+ ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';
2012
+ ctx.font = '20px Arial';
2013
+ ctx.textAlign = 'right';
2014
+ ctx.fillText(`难度: ${gameState.difficulty.toFixed(1)}x`, canvas.width - 20, 30);
2015
+ }
2016
+ }
2017
+
2018
+ // 辅助函数: 获取FontAwesome图标的Unicode
2019
+ function getIconCode(iconClass) {
2020
+ const icons = {
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
+
2100
+ // 键盘控制
2101
+ document.addEventListener('keydown', (e) => {
2102
+ if (gameState.keys.hasOwnProperty(e.key)) {
2103
+ gameState.keys[e.key] = true;
2104
+ e.preventDefault();
2105
+ }
2106
+
2107
+ if (e.key === ' ' || e.key === 'Spacebar') { // 空格键射击
2108
+ gameState.keys.Space = true;
2109
+ e.preventDefault();
2110
+ }
2111
+ });
2112
+
2113
+ document.addEventListener('keyup', (e) => {
2114
+ if (gameState.keys.hasOwnProperty(e.key)) {
2115
+ gameState.keys[e.key] = false;
2116
+ e.preventDefault();
2117
+ }
2118
+
2119
+ if (e.key === ' ' || e.key === 'Spacebar') {
2120
+ gameState.keys.Space = false;
2121
+ e.preventDefault();
2122
+ }
2123
+ });
2124
+
2125
+ // 触摸控制按钮事件
2126
+ leftBtn.addEventListener('touchstart', (e) => {
2127
+ e.preventDefault();
2128
+ gameState.keys.ArrowLeft = true;
2129
+ });
2130
+ leftBtn.addEventListener('touchend', (e) => {
2131
+ e.preventDefault();
2132
+ gameState.keys.ArrowLeft = false;
2133
+ });
2134
+
2135
+ rightBtn.addEventListener('touchstart', (e) => {
2136
+ e.preventDefault();
2137
+ gameState.keys.ArrowRight = true;
2138
+ });
2139
+ rightBtn.addEventListener('touchend', (e) => {
2140
+ e.preventDefault();
2141
+ gameState.keys.ArrowRight = false;
2142
+ });
2143
+
2144
+ upBtn.addEventListener('touchstart', (e) => {
2145
+ e.preventDefault();
2146
+ gameState.keys.ArrowUp = true;
2147
+ });
2148
+ upBtn.addEventListener('touchend', (e) => {
2149
+ e.preventDefault();
2150
+ gameState.keys.ArrowUp = false;
2151
+ });
2152
+
2153
+ downBtn.addEventListener('touchstart', (e) => {
2154
+ e.preventDefault();
2155
+ gameState.keys.ArrowDown = true;
2156
+ });
2157
+ downBtn.addEventListener('touchend', (e) => {
2158
+ e.preventDefault();
2159
+ gameState.keys.ArrowDown = false;
2160
+ });
2161
+
2162
+ fireBtn.addEventListener('touchstart', (e) => {
2163
+ e.preventDefault();
2164
+ gameState.isFiring = true;
2165
+ });
2166
+ fireBtn.addEventListener('touchend', (e) => {
2167
+ e.preventDefault();
2168
+ gameState.isFiring = false;
2169
+ });
2170
+
2171
+ // 鼠标控制按钮事件(用于桌面浏览器测试)
2172
+ leftBtn.addEventListener('mousedown', (e) => {
2173
+ e.preventDefault();
2174
+ gameState.keys.ArrowLeft = true;
2175
+ });
2176
+ leftBtn.addEventListener('mouseup', (e) => {
2177
+ e.preventDefault();
2178
+ gameState.keys.ArrowLeft = false;
2179
+ });
2180
+ leftBtn.addEventListener('mouseleave', (e) => {
2181
+ e.preventDefault();
2182
+ gameState.keys.ArrowLeft = false;
2183
+ });
2184
+
2185
+ rightBtn.addEventListener('mousedown', (e) => {
2186
+ e.preventDefault();
2187
+ gameState.keys.ArrowRight = true;
2188
+ });
2189
+ rightBtn.addEventListener('mouseup', (e) => {
2190
+ e.preventDefault();
2191
+ gameState.keys.ArrowRight = false;
2192
+ });
2193
+ rightBtn.addEventListener('mouseleave', (e) => {
2194
+ e.preventDefault();
2195
+ gameState.keys.ArrowRight = false;
2196
+ });
2197
+
2198
+ upBtn.addEventListener('mousedown', (e) => {
2199
+ e.preventDefault();
2200
+ gameState.keys.ArrowUp = true;
2201
+ });
2202
+ upBtn.addEventListener('mouseup', (e) => {
2203
+ e.preventDefault();
2204
+ gameState.keys.ArrowUp = false;
2205
+ });
2206
+ upBtn.addEventListener('mouseleave', (e) => {
2207
+ e.preventDefault();
2208
+ gameState.keys.ArrowUp = false;
2209
+ });
2210
+
2211
+ downBtn.addEventListener('mousedown', (e) => {
2212
+ e.preventDefault();
2213
+ gameState.keys.ArrowDown = true;
2214
+ });
2215
+ downBtn.addEventListener('mouseup', (e) => {
2216
+ e.preventDefault();
2217
+ gameState.keys.ArrowDown = false;
2218
+ });
2219
+ downBtn.addEventListener('mouseleave', (e) => {
2220
+ e.preventDefault();
2221
+ gameState.keys.ArrowDown = false;
2222
+ });
2223
+
2224
+ fireBtn.addEventListener('mousedown', (e) => {
2225
+ e.preventDefault();
2226
+ gameState.isFiring = true;
2227
+ });
2228
+ fireBtn.addEventListener('mouseup', (e) => {
2229
+ e.preventDefault();
2230
+ gameState.isFiring = false;
2231
+ });
2232
+ fireBtn.addEventListener('mouseleave', (e) => {
2233
+ e.preventDefault();
2234
+ gameState.isFiring = false;
2235
+ });
2236
+
2237
+ // 按钮事件
2238
+ startButton.addEventListener('click', initGame);
2239
+ restartButton.addEventListener('click', initGame);
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>