ノート

JavaScriptとCanvasで作る2D戦車バトルゲーム

フリーのプロンプト百科事典 Wikiprompt より

Dan ⚡️

2025年8月7日

JavaScriptとCanvasで作る2D戦車バトルゲーム 包括地形、AI、カスタマイズの詳細な仕様を持つ、JavaScriptとHTML5 Canvasを使用した完全に機能する2D戦車バトルゲームを生成するための包括的なプロンプト。

プロンプト内容保存

🌐
const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); let gridSize = 36; let tileSize = 20; let player, enemies, bullets, walls, breakableWalls, waterTiles, mudTiles, specialAmmo, keys, touchStart, lastShot, score, lives, level, gameOver, gameWin, enemyCount, playerColor, highScores, gameStarted, menuScreen, colorSelect, countSelect; function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; tileSize = Math.floor(Math.min(canvas.width, canvas.height) / (gridSize + 2)); if (tileSize < 10) tileSize = 10; canvas.width = tileSize * (gridSize + 2); canvas.height = tileSize * (gridSize + 2); canvas.style.width = canvas.width + 'px'; canvas.style.height = canvas.height + 'px'; canvas.style.position = 'absolute'; canvas.style.left = '50%'; canvas.style.top = '50%'; canvas.style.transform = 'translate(-50%, -50%)'; } window.addEventListener('resize', resizeCanvas); resizeCanvas(); function initGame() { gridSize = 36; tileSize = Math.floor(Math.min(canvas.width, canvas.height) / (gridSize + 2)); if (tileSize < 10) tileSize = 10; player = { x: 1, y: 1, direction: 'right', color: playerColor || '#00ff00', lives: 3, cooldown: 0 }; enemies = []; bullets = []; walls = []; breakableWalls = []; waterTiles = []; mudTiles = []; specialAmmo = []; keys = {}; touchStart = null; lastShot = 0; score = 0; lives = 3; level = 1; gameOver = false; gameWin = false; enemyCount = enemyCount || 5; generateMap(); spawnEnemies(); gameStarted = true; menuScreen = false; colorSelect = false; countSelect = false; highScores = JSON.parse(localStorage.getItem('tankHighScores')) || []; } function generateMap() { for (let i = 0; i < gridSize; i++) { for (let j = 0; j < gridSize; j++) { if (i === 0 || j === 0 || i === gridSize - 1 || j === gridSize - 1) { walls.push({ x: i, y: j }); } else if (Math.random() < 0.1) { walls.push({ x: i, y: j }); } else if (Math.random() < 0.15) { breakableWalls.push({ x: i, y: j, health: 2 }); } else if (Math.random() < 0.05) { waterTiles.push({ x: i, y: j }); } else if (Math.random() < 0.08) { mudTiles.push({ x: i, y: j }); } } } for (let i = 0; i < 3; i++) { let x = Math.floor(Math.random() * (gridSize - 2)) + 1; let y = Math.floor(Math.random() * (gridSize - 2)) + 1; specialAmmo.push({ x: x, y: y }); } } function spawnEnemies() { for (let i = 0; i < enemyCount; i++) { let x, y; do { x = Math.floor(Math.random() * (gridSize - 2)) + 1; y = Math.floor(Math.random() * (gridSize - 2)) + 1; } while (isBlocked(x, y) || (Math.abs(x - player.x) < 5 && Math.abs(y - player.y) < 5)); let behavior = ['aggressive', 'defensive', 'patrolling'][Math.floor(Math.random() * 3)]; let color = behavior === 'aggressive' ? '#ff0000' : behavior === 'defensive' ? '#0000ff' : '#ffff00'; enemies.push({ x: x, y: y, direction: ['up', 'down', 'left', 'right'][Math.floor(Math.random() * 4)], color: color, behavior: behavior, cooldown: 0, speed: 0.5 + level * 0.1 }); } } function isBlocked(x, y) { if (x < 0 || y < 0 || x >= gridSize || y >= gridSize) return true; if (walls.some(w => w.x === x && w.y === y)) return true; if (waterTiles.some(w => w.x === x && w.y === y)) return true; if (breakableWalls.some(w => w.x === x && w.y === y)) return true; return false; } function update() { if (!gameStarted || gameOver || gameWin) return; let currentTime = Date.now(); let dt = (currentTime - lastShot) / 1000; lastShot = currentTime; if (keys['ArrowUp'] || keys['w']) movePlayer('up', dt); if (keys['ArrowDown'] || keys['s']) movePlayer('down', dt); if (keys['ArrowLeft'] || keys['a']) movePlayer('left', dt); if (keys['ArrowRight'] || keys['d']) movePlayer('right', dt); if (keys[' ']) shoot(); player.cooldown = Math.max(0, player.cooldown - dt); enemies.forEach(enemy => { enemy.cooldown = Math.max(0, enemy.cooldown - dt); moveEnemy(enemy, dt); if (Math.random() < 0.01 && enemy.cooldown === 0) { shootEnemy(enemy); } }); bullets.forEach(bullet => { bullet.x += bullet.vx * dt * 10; bullet.y += bullet.vy * dt * 10; if (bullet.x < 0 || bullet.y < 0 || bullet.x >= gridSize || bullet.y >= gridSize) { bullet.dead = true; return; } if (walls.some(w => w.x === Math.floor(bullet.x) && w.y === Math.floor(bullet.y))) { bullet.dead = true; return; } let bw = breakableWalls.find(w => w.x === Math.floor(bullet.x) && w.y === Math.floor(bullet.y)); if (bw) { bw.health--; if (bw.health <= 0) { breakableWalls = breakableWalls.filter(w => w !== bw); } bullet.dead = true; return; } if (bullet.fromPlayer) { enemies.forEach(enemy => { if (Math.floor(enemy.x) === Math.floor(bullet.x) && Math.floor(enemy.y) === Math.floor(bullet.y)) { enemy.dead = true; bullet.dead = true; score += 100; } }); } else { if (Math.floor(player.x) === Math.floor(bullet.x) && Math.floor(player.y) === Math.floor(bullet.y)) { player.lives--; bullet.dead = true; if (player.lives <= 0) { gameOver = true; saveHighScore(); } } } }); bullets = bullets.filter(b => !b.dead); enemies = enemies.filter(e => !e.dead); specialAmmo.forEach(ammo => { if (Math.floor(player.x) === ammo.x && Math.floor(player.y) === ammo.y) { player.cooldown = 0; player.specialAmmo = true; ammo.collected = true; setTimeout(() => { player.specialAmmo = false; }, 5000); } }); specialAmmo = specialAmmo.filter(a => !a.collected); if (enemies.length === 0) { gameWin = true; score += Math.max(0, 1000 - Math.floor((Date.now() - startTime) / 1000) * 10); saveHighScore(); } } function movePlayer(direction, dt) { let speed = 4; let newX = player.x; let newY = player.y; if (direction === 'up') newY -= speed * dt; if (direction === 'down') newY += speed * dt; if (direction === 'left') newX -= speed * dt; if (direction === 'right') newX += speed * dt; player.direction = direction; if (!isBlocked(Math.floor(newX), Math.floor(newY))) { player.x = newX; player.y = newY; } if (mudTiles.some(m => m.x === Math.floor(player.x) && m.y === Math.floor(player.y))) { player.x = player.x; player.y = player.y; } } function moveEnemy(enemy, dt) { let speed = enemy.speed; let newX = enemy.x; let newY = enemy.y; if (enemy.behavior === 'aggressive') { let dx = player.x - enemy.x; let dy = player.y - enemy.y; if (Math.abs(dx) > Math.abs(dy)) { enemy.direction = dx > 0 ? 'right' : 'left'; } else { enemy.direction = dy > 0 ? 'down' : 'up'; } } else if (enemy.behavior === 'defensive') { if (Math.random() < 0.1) { enemy.direction = ['up', 'down', 'left', 'right'][Math.floor(Math.random() * 4)]; } } else { if (Math.random() < 0.05) { enemy.direction = ['up', 'down', 'left', 'right'][Math.floor(Math.random() * 4)]; } } if (enemy.direction === 'up') newY -= speed * dt; if (enemy.direction === 'down') newY += speed * dt; if (enemy.direction === 'left') newX -= speed * dt; if (enemy.direction === 'right') newX += speed * dt; if (!isBlocked(Math.floor(newX), Math.floor(newY))) { enemy.x = newX; enemy.y = newY; } } function shoot() { if (player.cooldown > 0) return; player.cooldown = 2; let vx = 0, vy = 0; if (player.direction === 'up') vy = -1; if (player.direction === 'down') vy = 1; if (player.direction === 'left') vx = -1; if (player.direction === 'right') vx = 1; bullets.push({ x: player.x, y: player.y, vx: vx, vy: vy, fromPlayer: true, dead: false, blastRadius: player.specialAmmo ? 2 : 1 }); } function shootEnemy(enemy) { if (enemy.cooldown > 0) return; enemy.cooldown = 2; let vx = 0, vy = 0; if (enemy.direction === 'up') vy = -1; if (enemy.direction === 'down') vy = 1; if (enemy.direction === 'left') vx = -1; if (enemy.direction === 'right') vx = 1; bullets.push({ x: enemy.x, y: enemy.y, vx: vx, vy: vy, fromPlayer: false, dead: false, blastRadius: 1 }); } function draw() { ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, canvas.width, canvas.height); let offsetX = (canvas.width - gridSize * tileSize) / 2; let offsetY = (canvas.height - gridSize * tileSize) / 2; for (let i = 0; i < gridSize; i++) { for (let j = 0; j < gridSize; j++) { ctx.fillStyle = '#1a1a2e'; ctx.fillRect(offsetX + i * tileSize, offsetY + j * tileSize, tileSize, tileSize); } } walls.forEach(w => { ctx.fillStyle = '#555555'; ctx.fillRect(offsetX + w.x * tileSize, offsetY + w.y * tileSize, tileSize, tileSize); ctx.strokeStyle = '#333333'; ctx.strokeRect(offsetX + w.x * tileSize, offsetY + w.y * tileSize, tileSize, tileSize); }); breakableWalls.forEach(w => { ctx.fillStyle = '#8b4513'; ctx.fillRect(offsetX + w.x * tileSize, offsetY + w.y * tileSize, tileSize, tileSize); ctx.strokeStyle = '#5a2d0c'; ctx.strokeRect(offsetX + w.x * tileSize, offsetY + w.y * tileSize, tileSize, tileSize); }); waterTiles.forEach(w => { ctx.fillStyle = '#0000ff'; ctx.fillRect(offsetX + w.x * tileSize, offsetY + w.y * tileSize, tileSize, tileSize); }); mudTiles.forEach(m => { ctx.fillStyle = '#654321'; ctx.fillRect(offsetX + m.x * tileSize, offsetY + m.y * tileSize, tileSize, tileSize); }); specialAmmo.forEach(a => { ctx.fillStyle = '#ff00ff'; ctx.beginPath(); ctx.arc(offsetX + (a.x + 0.5) * tileSize, offsetY + (a.y + 0.5) * tileSize, tileSize * 0.3, 0, Math.PI * 2); ctx.fill(); }); drawTank(player, offsetX, offsetY); enemies.forEach(enemy => { drawTank(enemy, offsetX, offsetY); }); bullets.forEach(bullet => { ctx.fillStyle = bullet.fromPlayer ? '#ffff00' : '#ff6600'; ctx.beginPath(); ctx.arc(offsetX + bullet.x * tileSize, offsetY + bullet.y * tileSize, tileSize * 0.2, 0, Math.PI * 2); ctx.fill(); }); drawHUD(); } function drawTank(tank, offsetX, offsetY) { let x = offsetX + tank.x * tileSize; let y = offsetY + tank.y * tileSize; ctx.fillStyle = tank.color; ctx.fillRect(x + tileSize * 0.1, y + tileSize * 0.1, tileSize * 0.8, tileSize * 0.8); ctx.fillStyle = '#000000'; ctx.fillRect(x + tileSize * 0.3, y + tileSize * 0.3, tileSize * 0.4, tileSize * 0.4); ctx.fillStyle = tank.color; if (tank.direction === 'up') { ctx.fillRect(x + tileSize * 0.4, y - tileSize * 0.2, tileSize * 0.2, tileSize * 0.4); } else if (tank.direction === 'down') { ctx.fillRect(x + tileSize * 0.4, y + tileSize * 0.8, tileSize * 0.2, tileSize * 0.4); } else if (tank.direction === 'left') { ctx.fillRect(x - tileSize * 0.2, y + tileSize * 0.4, tileSize * 0.4, tileSize * 0.2); } else { ctx.fillRect(x + tileSize * 0.8, y + tileSize * 0.4, tileSize * 0.4, tileSize * 0.2); } } function drawHUD() { ctx.fillStyle = '#ffffff'; ctx.font = '16px monospace'; ctx.fillText('Score: ' + score, 10, 30); ctx.fillText('Lives: ' + player.lives, 10, 50); ctx.fillText('Level: ' + level, 10, 70); ctx.fillText('Enemies: ' + enemies.length, 10, 90); } function saveHighScore() { let finalScore = score; highScores.push(finalScore); highScores.sort((a, b) => b - a); highScores = highScores.slice(0, 5); localStorage.setItem('tankHighScores', JSON.stringify(highScores)); } function showMenu() { menuScreen = true; gameStarted = false; colorSelect = false; countSelect = false; } function showColorSelect() { menuScreen = false; colorSelect = true; } function showCountSelect() { menuScreen = false; countSelect = true; } function drawMenu() { ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#ffffff'; ctx.font = '32px monospace'; ctx.textAlign = 'center'; ctx.fillText('TANK BATTLE', canvas.width / 2, canvas.height / 2 - 100); ctx.font = '20px monospace'; ctx.fillText('Press 1 to Start', canvas.width / 2, canvas.height / 2 - 40); ctx.fillText('Press 2 for Color Select', canvas.width / 2, canvas.height / 2); ctx.fillText('Press 3 for Enemy Count', canvas.width / 2, canvas.height / 2 + 40); ctx.fillText('High Scores:', canvas.width / 2, canvas.height / 2 + 100); highScores.forEach((s, i) => { ctx.fillText((i + 1) + '. ' + s, canvas.width / 2, canvas.height / 2 + 130 + i * 30); }); } function drawColorSelect() { ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#ffffff'; ctx.font = '24px monospace'; ctx.textAlign = 'center'; ctx.fillText('Select Tank Color', canvas.width / 2, canvas.height / 2 - 100); let colors = ['#00ff00', '#ff0000', '#0000ff', '#ffff00', '#ff00ff', '#00ffff']; colors.forEach((c, i) => { ctx.fillStyle = c; ctx.fillRect(canvas.width / 2 - 150 + i * 60, canvas.height / 2 - 20, 40, 40); }); ctx.fillStyle = '#ffffff'; ctx.fillText('Press 1-6 to select', canvas.width / 2, canvas.height / 2 + 60); ctx.fillText('Press 0 to go back', canvas.width / 2, canvas.height / 2 + 100); } function drawCountSelect() { ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#ffffff'; ctx.font = '24px monospace'; ctx.textAlign = 'center'; ctx.fillText('Select Enemy Count', canvas.width / 2, canvas.height / 2 - 100); for (let i = 1; i <= 10; i++) { ctx.fillStyle = '#ffffff'; ctx.fillText(i, canvas.width / 2 - 200 + i * 40, canvas.height / 2); } ctx.fillText('Press 1-10 to select', canvas.width / 2, canvas.height / 2 + 60); ctx.fillText('Press 0 to go back', canvas.width / 2, canvas.height / 2 + 100); } document.addEventListener('keydown', (e) => { keys[e.key] = true; if (menuScreen) { if (e.key === '1') { initGame(); } else if (e.key === '2') { showColorSelect(); } else if (e.key === '3') { showCountSelect(); } } else if (colorSelect) { if (e.key >= '1' && e.key <= '6') { let colors = ['#00ff00', '#ff0000', '#0000ff', '#ffff00', '#ff00ff', '#00ffff']; playerColor = colors[parseInt(e.key) - 1]; showMenu(); } else if (e.key === '0') { showMenu(); } } else if (countSelect) { if (e.key >= '1' && e.key <= '9') { enemyCount = parseInt(e.key); showMenu(); } else if (e.key === '0') { showMenu(); } } if (e.key === ' ' && gameStarted && !gameOver && !gameWin) { shoot(); } }); document.addEventListener('keyup', (e) => { keys[e.key] = false; }); canvas.addEventListener('touchstart', (e) => { e.preventDefault(); let touch = e.touches[0]; touchStart = { x: touch.clientX, y: touch.clientY }; }); canvas.addEventListener('touchmove', (e) => { e.preventDefault(); if (!touchStart) return; let touch = e.touches[0]; let dx = touch.clientX - touchStart.x; let dy = touch.clientY - touchStart.y; if (Math.abs(dx) > Math.abs(dy)) { if (dx > 20) movePlayer('right', 0.1); if (dx < -20) movePlayer('left', 0.1); } else { if (dy > 20) movePlayer('down', 0.1); if (dy < -20) movePlayer('up', 0.1); } touchStart = { x: touch.clientX, y: touch.clientY }; }); canvas.addEventListener('touchend', (e) => { e.preventDefault(); if (touchStart) { shoot(); } touchStart = null; }); let startTime; function gameLoop() { if (gameStarted && !gameOver && !gameWin) { if (!startTime) startTime = Date.now(); update(); } draw(); if (menuScreen) drawMenu(); if (colorSelect) drawColorSelect(); if (countSelect) drawCountSelect(); if (gameOver) { ctx.fillStyle = '#ff0000'; ctx.font = '48px monospace'; ctx.textAlign = 'center'; ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2); ctx.font = '20px monospace'; ctx.fillText('Score: ' + score, canvas.width / 2, canvas.height / 2 + 50); ctx.fillText('Press R to restart', canvas.width / 2, canvas.height / 2 + 100); } if (gameWin) { ctx.fillStyle = '#00ff00'; ctx.font = '48px monospace'; ctx.textAlign = 'center'; ctx.fillText('VICTORY!', canvas.width / 2, canvas.height / 2); ctx.font = '20px monospace'; ctx.fillText('Score: ' + score, canvas.width / 2, canvas.height / 2 + 50); ctx.fillText('Press R to restart', canvas.width / 2, canvas.height / 2 + 100); } requestAnimationFrame(gameLoop); } document.addEventListener('keydown', (e) => { if (e.key === 'r' && (gameOver || gameWin)) { initGame(); } }); showMenu(); gameLoop();

ログインして完全なプロンプトを表示

次で続行:

ログインすると、次に同意したことになります: 利用規約 と プライバシーポリシー

使い方

このプロンプトは coding 向けに設計されています。上の内容をコピーして、お好みの AI ツールに貼り付けてください。

最良の結果を得るには、プレースホルダー(角括弧や大文字で示された部分)を具体的な要件に置き換えてください。

参考資料

カテゴリ:coding| twitter| javascript| canvas

ノート