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