JavaScriptとCanvasで作る2D戦車バトルゲーム
フリーのプロンプト百科事典 Wikiprompt より
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 ツールに貼り付けてください。
最良の結果を得るには、プレースホルダー(角括弧や大文字で示された部分)を具体的な要件に置き換えてください。
ノート
0 件のコメント