讨论

使用JavaScript和Canvas的2D坦克大战游戏

来自 Wikiprompt,自由的提示词百科全书

Dan ⚡️

2025年8月7日

使用JavaScript和Canvas的2D坦克大战游戏 一个全面的提示,用于使用JavaScript和HTML5 Canvas生成一个功能完整的2D坦克对战游戏,包含对游戏玩法、地形、AI和自定义的详细规格说明。

提示词内容收藏

🌐
const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); let gridSize = 36; let tileSize = Math.floor(Math.min(window.innerWidth, window.innerHeight) / gridSize); canvas.width = tileSize * gridSize; canvas.height = tileSize * gridSize; window.addEventListener('resize', () => { tileSize = Math.floor(Math.min(window.innerWidth, window.innerHeight) / gridSize); canvas.width = tileSize * gridSize; canvas.height = tileSize * gridSize; drawMap(); }); let map = []; let player = {}; let enemies = []; let bullets = []; let explosions = []; let keys = {}; let touchStart = null; let lastShot = 0; let score = 0; let lives = 3; let level = 1; let gameOver = false; let gameWin = false; let enemyCount = 5; let playerColor = '#4CAF50'; let highScore = localStorage.getItem('tankHighScore') || 0; let gameStarted = false; let gamePaused = false; const terrainTypes = { solid: 0, breakable: 1, water: 2, mud: 3, ground: 4 }; const enemyColors = { aggressive: '#FF5722', defensive: '#2196F3', patrolling: '#FFC107' }; function generateMap() { map = []; for (let i = 0; i < gridSize; i++) { map[i] = []; for (let j = 0; j < gridSize; j++) { if (i === 0 || j === 0 || i === gridSize - 1 || j === gridSize - 1) { map[i][j] = terrainTypes.solid; } else if (Math.random() < 0.1) { map[i][j] = terrainTypes.breakable; } else if (Math.random() < 0.05) { map[i][j] = terrainTypes.water; } else if (Math.random() < 0.05) { map[i][j] = terrainTypes.mud; } else { map[i][j] = terrainTypes.ground; } } } } function drawMap() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i = 0; i < gridSize; i++) { for (let j = 0; j < gridSize; j++) { const x = j * tileSize; const y = i * tileSize; switch (map[i][j]) { case terrainTypes.solid: ctx.fillStyle = '#333'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#555'; ctx.strokeRect(x, y, tileSize, tileSize); break; case terrainTypes.breakable: ctx.fillStyle = '#8B4513'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#A0522D'; ctx.strokeRect(x, y, tileSize, tileSize); break; case terrainTypes.water: ctx.fillStyle = '#1E90FF'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#4682B4'; ctx.strokeRect(x, y, tileSize, tileSize); break; case terrainTypes.mud: ctx.fillStyle = '#8B7355'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#A0522D'; ctx.strokeRect(x, y, tileSize, tileSize); break; default: ctx.fillStyle = '#2E8B57'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#3CB371'; ctx.strokeRect(x, y, tileSize, tileSize); } } } } function createPlayer() { player = { x: Math.floor(gridSize / 2), y: gridSize - 2, direction: 'up', color: playerColor, cooldown: 0, speed: 1, lives: 3 }; } function createEnemies(count) { enemies = []; for (let i = 0; i < count; i++) { let x, y; do { x = Math.floor(Math.random() * (gridSize - 2)) + 1; y = Math.floor(Math.random() * (gridSize - 2)) + 1; } while (map[y][x] !== terrainTypes.ground || (Math.abs(x - player.x) < 5 && Math.abs(y - player.y) < 5)); const behavior = ['aggressive', 'defensive', 'patrolling'][Math.floor(Math.random() * 3)]; enemies.push({ x: x, y: y, direction: ['up', 'down', 'left', 'right'][Math.floor(Math.random() * 4)], color: enemyColors[behavior], behavior: behavior, cooldown: 0, speed: 0.5 + level * 0.1, alive: true }); } } function drawTank(tank, isPlayer) { const x = tank.x * tileSize; const y = tank.y * tileSize; ctx.fillStyle = tank.color; ctx.fillRect(x + tileSize * 0.2, y + tileSize * 0.2, tileSize * 0.6, tileSize * 0.6); ctx.fillStyle = '#000'; ctx.fillRect(x + tileSize * 0.4, y + tileSize * 0.4, tileSize * 0.2, tileSize * 0.2); ctx.fillStyle = '#FFF'; ctx.fillRect(x + tileSize * 0.45, y + tileSize * 0.45, tileSize * 0.1, tileSize * 0.1); ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.strokeRect(x + tileSize * 0.2, y + tileSize * 0.2, tileSize * 0.6, tileSize * 0.6); ctx.beginPath(); ctx.moveTo(x + tileSize / 2, y + tileSize / 2); switch (tank.direction) { case 'up': ctx.lineTo(x + tileSize / 2, y); break; case 'down': ctx.lineTo(x + tileSize / 2, y + tileSize); break; case 'left': ctx.lineTo(x, y + tileSize / 2); break; case 'right': ctx.lineTo(x + tileSize, y + tileSize / 2); break; } ctx.stroke(); } function drawBullets() { bullets.forEach(bullet => { ctx.fillStyle = '#FFD700'; ctx.beginPath(); ctx.arc(bullet.x * tileSize + tileSize / 2, bullet.y * tileSize + tileSize / 2, tileSize * 0.2, 0, Math.PI * 2); ctx.fill(); }); } function drawExplosions() { explosions.forEach(explosion => { ctx.fillStyle = `rgba(255, 165, 0, ${explosion.alpha})`; ctx.beginPath(); ctx.arc(explosion.x * tileSize + tileSize / 2, explosion.y * tileSize + tileSize / 2, explosion.radius * tileSize, 0, Math.PI * 2); ctx.fill(); }); } function updatePlayer() { if (gameOver || gameWin || !gameStarted || gamePaused) return; let dx = 0, dy = 0; if (keys['ArrowUp']) { dy = -1; player.direction = 'up'; } if (keys['ArrowDown']) { dy = 1; player.direction = 'down'; } if (keys['ArrowLeft']) { dx = -1; player.direction = 'left'; } if (keys['ArrowRight']) { dx = 1; player.direction = 'right'; } if (dx !== 0 || dy !== 0) { const newX = player.x + dx; const newY = player.y + dy; if (newX >= 0 && newX < gridSize && newY >= 0 && newY < gridSize && map[newY][newX] !== terrainTypes.solid && map[newY][newX] !== terrainTypes.water) { if (map[newY][newX] === terrainTypes.mud) { player.x += dx * 0.5; player.y += dy * 0.5; } else { player.x = newX; player.y = newY; } } } if (keys[' '] && Date.now() - lastShot > 2000) { shoot(player); lastShot = Date.now(); } } function updateEnemies() { if (gameOver || gameWin || !gameStarted || gamePaused) return; enemies.forEach(enemy => { if (!enemy.alive) return; let dx = 0, dy = 0; if (enemy.behavior === 'aggressive') { const diffX = player.x - enemy.x; const diffY = player.y - enemy.y; if (Math.abs(diffX) > Math.abs(diffY)) { dx = Math.sign(diffX); enemy.direction = dx > 0 ? 'right' : 'left'; } else { dy = Math.sign(diffY); enemy.direction = dy > 0 ? 'down' : 'up'; } } else if (enemy.behavior === 'defensive') { if (Math.random() < 0.3) { const dir = ['up', 'down', 'left', 'right'][Math.floor(Math.random() * 4)]; enemy.direction = dir; } switch (enemy.direction) { case 'up': dy = -1; break; case 'down': dy = 1; break; case 'left': dx = -1; break; case 'right': dx = 1; break; } } else { if (Math.random() < 0.1) { const dir = ['up', 'down', 'left', 'right'][Math.floor(Math.random() * 4)]; enemy.direction = dir; } switch (enemy.direction) { case 'up': dy = -1; break; case 'down': dy = 1; break; case 'left': dx = -1; break; case 'right': dx = 1; break; } } const newX = enemy.x + dx * enemy.speed; const newY = enemy.y + dy * enemy.speed; if (newX >= 0 && newX < gridSize && newY >= 0 && newY < gridSize && map[Math.floor(newY)][Math.floor(newX)] !== terrainTypes.solid && map[Math.floor(newY)][Math.floor(newX)] !== terrainTypes.water) { enemy.x = newX; enemy.y = newY; } if (Date.now() - enemy.cooldown > 2000) { shoot(enemy); enemy.cooldown = Date.now(); } }); } function shoot(tank) { let dx = 0, dy = 0; switch (tank.direction) { case 'up': dy = -1; break; case 'down': dy = 1; break; case 'left': dx = -1; break; case 'right': dx = 1; break; } bullets.push({ x: tank.x + dx, y: tank.y + dy, dx: dx, dy: dy, owner: tank === player ? 'player' : 'enemy' }); } function updateBullets() { bullets.forEach((bullet, index) => { bullet.x += bullet.dx; bullet.y += bullet.dy; if (bullet.x < 0 || bullet.x >= gridSize || bullet.y < 0 || bullet.y >= gridSize) { bullets.splice(index, 1); return; } if (map[Math.floor(bullet.y)][Math.floor(bullet.x)] === terrainTypes.breakable) { map[Math.floor(bullet.y)][Math.floor(bullet.x)] = terrainTypes.ground; bullets.splice(index, 1); return; } if (map[Math.floor(bullet.y)][Math.floor(bullet.x)] === terrainTypes.solid) { bullets.splice(index, 1); return; } if (bullet.owner === 'player') { enemies.forEach(enemy => { if (enemy.alive && Math.floor(enemy.x) === Math.floor(bullet.x) && Math.floor(enemy.y) === Math.floor(bullet.y)) { enemy.alive = false; score += 100; explosions.push({ x: bullet.x, y: bullet.y, radius: 1, alpha: 1 }); bullets.splice(index, 1); } }); } else { if (Math.floor(player.x) === Math.floor(bullet.x) && Math.floor(player.y) === Math.floor(bullet.y)) { lives--; player.lives = lives; explosions.push({ x: bullet.x, y: bullet.y, radius: 1, alpha: 1 }); bullets.splice(index, 1); if (lives <= 0) { gameOver = true; } } } }); } function updateExplosions() { explosions.forEach((explosion, index) => { explosion.alpha -= 0.05; explosion.radius += 0.1; if (explosion.alpha <= 0) { explosions.splice(index, 1); } }); } function checkWin() { if (enemies.every(enemy => !enemy.alive)) { gameWin = true; score += Math.max(0, 1000 - Math.floor(Date.now() / 1000)); if (score > highScore) { highScore = score; localStorage.setItem('tankHighScore', highScore); } } } function drawUI() { ctx.fillStyle = '#FFF'; ctx.font = '20px Arial'; ctx.fillText(`Score: ${score}`, 10, 30); ctx.fillText(`Lives: ${lives}`, 10, 60); ctx.fillText(`Level: ${level}`, 10, 90); ctx.fillText(`High Score: ${highScore}`, 10, 120); } function drawGameOver() { ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#FFF'; ctx.font = '40px Arial'; ctx.textAlign = 'center'; ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 20); ctx.font = '20px Arial'; ctx.fillText(`Score: ${score}`, canvas.width / 2, canvas.height / 2 + 30); ctx.fillText('Press R to restart', canvas.width / 2, canvas.height / 2 + 60); ctx.textAlign = 'left'; } function drawWin() { ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#FFF'; ctx.font = '40px Arial'; ctx.textAlign = 'center'; ctx.fillText('YOU WIN!', canvas.width / 2, canvas.height / 2 - 20); ctx.font = '20px Arial'; ctx.fillText(`Score: ${score}`, canvas.width / 2, canvas.height / 2 + 30); ctx.fillText('Press R to restart', canvas.width / 2, canvas.height / 2 + 60); ctx.textAlign = 'left'; } function drawStartScreen() { ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#FFF'; ctx.font = '40px Arial'; ctx.textAlign = 'center'; ctx.fillText('TANK BATTLE', canvas.width / 2, canvas.height / 2 - 100); ctx.font = '20px Arial'; ctx.fillText('Arrow Keys / Swipe to move', canvas.width / 2, canvas.height / 2 - 40); ctx.fillText('Space / Tap to shoot', canvas.width / 2, canvas.height / 2); ctx.fillText('Press Enter to start', canvas.width / 2, canvas.height / 2 + 40); ctx.fillText('Enemies: ' + enemyCount, canvas.width / 2, canvas.height / 2 + 80); ctx.fillText('Press +/- to change enemy count', canvas.width / 2, canvas.height / 2 + 110); ctx.fillText('Press C to change color', canvas.width / 2, canvas.height / 2 + 140); ctx.textAlign = 'left'; } function gameLoop() { drawMap(); updatePlayer(); updateEnemies(); updateBullets(); updateExplosions(); checkWin(); drawTank(player, true); enemies.forEach(enemy => { if (enemy.alive) drawTank(enemy, false); }); drawBullets(); drawExplosions(); drawUI(); if (gameOver) drawGameOver(); if (gameWin) drawWin(); if (!gameStarted) drawStartScreen(); requestAnimationFrame(gameLoop); } document.addEventListener('keydown', (e) => { keys[e.key] = true; if (e.key === 'Enter' && !gameStarted) { gameStarted = true; generateMap(); createPlayer(); createEnemies(enemyCount); } if (e.key === 'r' && (gameOver || gameWin)) { gameOver = false; gameWin = false; lives = 3; score = 0; level = 1; generateMap(); createPlayer(); createEnemies(enemyCount); } if (e.key === '+' && !gameStarted) { enemyCount = Math.min(10, enemyCount + 1); } if (e.key === '-' && !gameStarted) { enemyCount = Math.max(1, enemyCount - 1); } if (e.key === 'c' && !gameStarted) { const colors = ['#4CAF50', '#FF5722', '#2196F3', '#FFC107', '#9C27B0']; playerColor = colors[Math.floor(Math.random() * colors.length)]; } }); document.addEventListener('keyup', (e) => { keys[e.key] = false; }); canvas.addEventListener('touchstart', (e) => { e.preventDefault(); touchStart = { x: e.touches[0].clientX, y: e.touches[0].clientY }; if (!gameStarted) { gameStarted = true; generateMap(); createPlayer(); createEnemies(enemyCount); } }); canvas.addEventListener('touchmove', (e) => { e.preventDefault(); if (touchStart) { const dx = e.touches[0].clientX - touchStart.x; const dy = e.touches[0].clientY - touchStart.y; if (Math.abs(dx) > Math.abs(dy)) { keys['ArrowRight'] = dx > 0; keys['ArrowLeft'] = dx < 0; keys['ArrowUp'] = false; keys['ArrowDown'] = false; } else { keys['ArrowDown'] = dy > 0; keys['ArrowUp'] = dy < 0; keys['ArrowLeft'] = false; keys['ArrowRight'] = false; } touchStart = { x: e.touches[0].clientX, y: e.touches[0].clientY }; } }); canvas.addEventListener('touchend', (e) => { e.preventDefault(); keys['ArrowUp'] = false; keys['ArrowDown'] = false; keys['ArrowLeft'] = false; keys['ArrowRight'] = false; if (gameStarted && !gameOver && !gameWin) { shoot(player); } touchStart = null; }); generateMap(); createPlayer(); createEnemies(enemyCount); gameLoop();

登录以查看完整提示词

继续使用:

登录即表示你同意我们的 使用条款 和 隐私政策

用法

此提示词专为 coding 设计。复制上方内容并粘贴到你常用的 AI 工具中。

为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。

参考资料

分类:coding| twitter| javascript| canvas

讨论