|
13 | 13 | #gameCanvas { |
14 | 14 | display: block; |
15 | 15 | background-color: #000; |
| 16 | + margin: 0 auto; |
16 | 17 | } |
17 | 18 | #miniMap { |
18 | 19 | position: absolute; |
|
29 | 30 | left: 10px; |
30 | 31 | color: white; |
31 | 32 | font-size: 16px; |
| 33 | + background-color: rgba(0, 0, 0, 0.7); |
| 34 | + padding: 10px; |
| 35 | + border-radius: 5px; |
| 36 | + } |
| 37 | + #gameOver { |
| 38 | + position: absolute; |
| 39 | + top: 50%; |
| 40 | + left: 50%; |
| 41 | + transform: translate(-50%, -50%); |
| 42 | + color: white; |
| 43 | + font-size: 48px; |
| 44 | + display: none; |
32 | 45 | } |
33 | 46 | </style> |
34 | 47 | </head> |
35 | 48 | <body> |
36 | 49 | <canvas id="gameCanvas"></canvas> |
37 | 50 | <canvas id="miniMap"></canvas> |
38 | 51 | <div id="gameInfo"> |
39 | | - <div id="lives">Lives: 3</div> |
40 | | - <div id="score">Score: 0</div> |
| 52 | + <div>生命: <span id="lives">3</span></div> |
| 53 | + <div>分数: <span id="score">0</span></div> |
41 | 54 | </div> |
| 55 | + <div id="gameOver">游戏结束,请刷新页面</div> |
42 | 56 |
|
43 | 57 | <script> |
44 | 58 | // 游戏主画布 |
|
55 | 69 | let gameRunning = true; |
56 | 70 | let playerLives = 3; |
57 | 71 | let score = 0; |
58 | | - const livesDisplay = document.getElementById('lives'); |
59 | | - const scoreDisplay = document.getElementById('score'); |
60 | 72 |
|
61 | 73 | // 玩家坦克 |
62 | 74 | const playerTank = { |
|
66 | 78 | height: 30, |
67 | 79 | color: 'blue', |
68 | 80 | speed: 3, |
69 | | - direction: 0, |
| 81 | + direction: 0, // 0:上, 1:右, 2:下, 3:左 |
70 | 82 | bullets: [], |
71 | 83 | lastShot: 0, |
72 | 84 | shootDelay: 500, |
73 | 85 | isPlayer: true |
74 | 86 | }; |
75 | 87 |
|
76 | | - // 我方AI坦克(修复了显示问题) |
| 88 | + // 友军AI坦克 |
77 | 89 | const allyTank = { |
78 | 90 | x: 200, |
79 | 91 | y: 200, |
|
88 | 100 | isPlayer: false, |
89 | 101 | isAlly: true, |
90 | 102 | aiTimer: 0, |
91 | | - aiInterval: 2000, |
92 | | - alive: true // 添加了alive状态 |
| 103 | + aiInterval: 2000 |
93 | 104 | }; |
94 | 105 |
|
95 | | - // 敌方AI坦克 |
| 106 | + // 敌军AI坦克 |
96 | 107 | let enemyTank = { |
97 | 108 | x: 600, |
98 | 109 | y: 400, |
|
108 | 119 | isAlly: false, |
109 | 120 | aiTimer: 0, |
110 | 121 | aiInterval: 3000, |
111 | | - alive: true, |
112 | 122 | respawnTimer: 0 |
113 | 123 | }; |
114 | 124 |
|
|
159 | 169 |
|
160 | 170 | // 我方AI行为 |
161 | 171 | function allyAI(tank, deltaTime) { |
162 | | - if (!tank.alive) return; |
163 | | - |
164 | 172 | tank.aiTimer += deltaTime; |
165 | 173 | if (tank.aiTimer >= tank.aiInterval) { |
166 | 174 | tank.aiTimer = 0; |
167 | 175 | tank.direction = Math.floor(Math.random() * 4); |
168 | | - |
169 | | - if (Math.random() < 0.7) { |
| 176 | + if (Math.random() < 0.3) { |
170 | 177 | shoot(tank); |
171 | 178 | } |
172 | 179 | } |
173 | | - |
174 | 180 | moveTank(tank); |
175 | | - avoidObstacles(tank); |
176 | 181 | } |
177 | 182 |
|
178 | 183 | // 敌方AI行为 |
179 | 184 | function enemyAI(tank, deltaTime) { |
180 | 185 | if (!tank.alive) { |
181 | 186 | tank.respawnTimer += deltaTime; |
182 | 187 | if (tank.respawnTimer >= 3000) { |
183 | | - respawnEnemy(); |
| 188 | + tank.alive = true; |
| 189 | + tank.x = Math.random() > 0.5 ? 100 : 700; |
| 190 | + tank.y = Math.random() > 0.5 ? 100 : 500; |
| 191 | + tank.respawnTimer = 0; |
184 | 192 | } |
185 | 193 | return; |
186 | 194 | } |
187 | 195 |
|
188 | 196 | tank.aiTimer += deltaTime; |
189 | 197 | if (tank.aiTimer >= tank.aiInterval) { |
190 | 198 | tank.aiTimer = 0; |
191 | | - |
192 | 199 | const dx = playerTank.x - tank.x; |
193 | 200 | const dy = playerTank.y - tank.y; |
194 | 201 |
|
|
202 | 209 | shoot(tank); |
203 | 210 | } |
204 | 211 | } |
205 | | - |
206 | 212 | moveTank(tank); |
207 | | - avoidObstacles(tank); |
208 | | - } |
209 | | - |
210 | | - // 重生敌方坦克 |
211 | | - function respawnEnemy() { |
212 | | - enemyTank = { |
213 | | - x: Math.random() > 0.5 ? 100 : 700, |
214 | | - y: Math.random() > 0.5 ? 100 : 500, |
215 | | - width: 30, |
216 | | - height: 30, |
217 | | - color: 'red', |
218 | | - speed: 2, |
219 | | - direction: Math.floor(Math.random() * 4), |
220 | | - bullets: [], |
221 | | - lastShot: 0, |
222 | | - shootDelay: 1500, |
223 | | - isPlayer: false, |
224 | | - isAlly: false, |
225 | | - aiTimer: 0, |
226 | | - aiInterval: 3000, |
227 | | - alive: true, |
228 | | - respawnTimer: 0 |
229 | | - }; |
230 | 213 | } |
231 | 214 |
|
232 | 215 | // 移动坦克 |
233 | 216 | function moveTank(tank) { |
234 | | - if (!tank.alive) return; |
235 | | - |
236 | 217 | let newX = tank.x; |
237 | 218 | let newY = tank.y; |
238 | 219 |
|
|
243 | 224 | case 3: newX -= tank.speed; break; |
244 | 225 | } |
245 | 226 |
|
| 227 | + // 边界检测 |
246 | 228 | if (newX >= 0 && newX + tank.width <= canvas.width) { |
247 | 229 | tank.x = newX; |
248 | 230 | } |
|
251 | 233 | } |
252 | 234 | } |
253 | 235 |
|
254 | | - // 避免障碍物 |
255 | | - function avoidObstacles(tank) { |
256 | | - if (!tank.alive) return; |
257 | | - |
258 | | - for (const obstacle of obstacles) { |
259 | | - if (checkCollision(tank, obstacle)) { |
260 | | - switch(tank.direction) { |
261 | | - case 0: tank.y += tank.speed; break; |
262 | | - case 1: tank.x -= tank.speed; break; |
263 | | - case 2: tank.y -= tank.speed; break; |
264 | | - case 3: tank.x += tank.speed; break; |
265 | | - } |
266 | | - tank.direction = Math.floor(Math.random() * 4); |
267 | | - break; |
268 | | - } |
269 | | - } |
270 | | - } |
271 | | - |
272 | 236 | // 碰撞检测 |
273 | 237 | function checkCollision(obj1, obj2) { |
274 | 238 | return obj1.x < obj2.x + obj2.width && |
|
289 | 253 | case 3: bullet.x -= bullet.speed; break; |
290 | 254 | } |
291 | 255 |
|
| 256 | + // 边界检测 |
292 | 257 | if (bullet.x < 0 || bullet.x > canvas.width || |
293 | 258 | bullet.y < 0 || bullet.y > canvas.height) { |
294 | 259 | tank.bullets.splice(i, 1); |
295 | 260 | continue; |
296 | 261 | } |
297 | 262 |
|
| 263 | + // 障碍物碰撞 |
298 | 264 | for (const obstacle of obstacles) { |
299 | 265 | if (checkCollision(bullet, obstacle)) { |
300 | 266 | tank.bullets.splice(i, 1); |
301 | 267 | break; |
302 | 268 | } |
303 | 269 | } |
304 | 270 |
|
305 | | - if (bullet.owner !== playerTank && checkCollision(bullet, playerTank)) { |
| 271 | + // 敌方子弹击中玩家 |
| 272 | + if (bullet.owner === enemyTank && checkCollision(bullet, playerTank)) { |
306 | 273 | tank.bullets.splice(i, 1); |
307 | 274 | playerHit(); |
308 | 275 | continue; |
309 | 276 | } |
310 | 277 |
|
311 | | - if (bullet.owner !== allyTank && bullet.owner !== playerTank && checkCollision(bullet, allyTank)) { |
312 | | - tank.bullets.splice(i, 1); |
313 | | - continue; |
314 | | - } |
315 | | - |
316 | | - if (bullet.owner !== enemyTank && checkCollision(bullet, enemyTank) && enemyTank.alive) { |
| 278 | + // 玩家子弹击中敌方 |
| 279 | + if (bullet.owner === playerTank && checkCollision(bullet, enemyTank) && enemyTank.alive) { |
317 | 280 | tank.bullets.splice(i, 1); |
318 | 281 | enemyHit(); |
319 | 282 | continue; |
|
324 | 287 | // 玩家被击中 |
325 | 288 | function playerHit() { |
326 | 289 | playerLives--; |
327 | | - livesDisplay.textContent = `Lives: ${playerLives}`; |
| 290 | + document.getElementById('lives').textContent = playerLives; |
328 | 291 |
|
329 | 292 | if (playerLives <= 0) { |
330 | 293 | gameOver(); |
331 | 294 | } else { |
| 295 | + // 重置玩家位置 |
332 | 296 | playerTank.x = 400; |
333 | 297 | playerTank.y = 300; |
| 298 | + playerTank.direction = 0; |
334 | 299 | } |
335 | 300 | } |
336 | 301 |
|
|
339 | 304 | enemyTank.alive = false; |
340 | 305 | enemyTank.respawnTimer = 0; |
341 | 306 | score += 100; |
342 | | - scoreDisplay.textContent = `Score: ${score}`; |
| 307 | + document.getElementById('score').textContent = score; |
343 | 308 | } |
344 | 309 |
|
345 | 310 | // 游戏结束 |
346 | 311 | function gameOver() { |
347 | 312 | gameRunning = false; |
348 | | - ctx.fillStyle = 'white'; |
349 | | - ctx.font = '48px Arial'; |
350 | | - ctx.textAlign = 'center'; |
351 | | - ctx.fillText('GAME OVER', canvas.width/2, canvas.height/2); |
| 313 | + document.getElementById('gameOver').style.display = 'block'; |
352 | 314 | } |
353 | 315 |
|
354 | | - // 绘制坦克(修复了显示问题) |
| 316 | + // 绘制坦克 |
355 | 317 | function drawTank(tank) { |
356 | | - if (!tank.alive) return; |
357 | | - |
358 | 318 | ctx.fillStyle = tank.color; |
359 | 319 | ctx.fillRect(tank.x, tank.y, tank.width, tank.height); |
360 | 320 |
|
|
374 | 334 |
|
375 | 335 | // 绘制子弹 |
376 | 336 | function drawBullets(tank) { |
377 | | - ctx.fillStyle = tank.bullets[0]?.color || 'yellow'; |
| 337 | + ctx.fillStyle = 'yellow'; |
378 | 338 | for (const bullet of tank.bullets) { |
379 | 339 | ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height); |
380 | 340 | } |
|
395 | 355 | const scaleX = miniMapCanvas.width / canvas.width; |
396 | 356 | const scaleY = miniMapCanvas.height / canvas.height; |
397 | 357 |
|
| 358 | + // 绘制障碍物 |
398 | 359 | miniMapCtx.fillStyle = 'gray'; |
399 | 360 | for (const obstacle of obstacles) { |
400 | 361 | miniMapCtx.fillRect( |
|
405 | 366 | ); |
406 | 367 | } |
407 | 368 |
|
| 369 | + // 绘制玩家坦克 |
408 | 370 | miniMapCtx.fillStyle = playerTank.color; |
409 | 371 | miniMapCtx.fillRect( |
410 | 372 | playerTank.x * scaleX, |
|
413 | 375 | playerTank.height * scaleY |
414 | 376 | ); |
415 | 377 |
|
| 378 | + // 绘制友军坦克 |
416 | 379 | miniMapCtx.fillStyle = allyTank.color; |
417 | 380 | miniMapCtx.fillRect( |
418 | 381 | allyTank.x * scaleX, |
|
421 | 384 | allyTank.height * scaleY |
422 | 385 | ); |
423 | 386 |
|
| 387 | + // 绘制敌军坦克 |
424 | 388 | if (enemyTank.alive) { |
425 | 389 | miniMapCtx.fillStyle = enemyTank.color; |
426 | 390 | miniMapCtx.fillRect( |
|
476 | 440 | // 绘制游戏元素 |
477 | 441 | drawObstacles(); |
478 | 442 | drawTank(playerTank); |
479 | | - drawTank(allyTank); // 确保调用绘制我方坦克 |
480 | | - drawTank(enemyTank); |
| 443 | + drawTank(allyTank); |
| 444 | + if (enemyTank.alive) drawTank(enemyTank); |
481 | 445 | drawBullets(playerTank); |
482 | 446 | drawBullets(allyTank); |
483 | 447 | drawBullets(enemyTank); |
|
488 | 452 | requestAnimationFrame(gameLoop); |
489 | 453 | } |
490 | 454 |
|
491 | | - // 启动游戏 |
| 455 | + // 初始化游戏 |
492 | 456 | requestAnimationFrame(gameLoop); |
493 | 457 | </script> |
494 | 458 | </body> |
|
0 commit comments