Juego de Batalla de Tanques 2D en JavaScript con Canvas
De Wikiprompt, la enciclopedia libre de prompts
Juego de Batalla de Tanques 2D en JavaScript con Canvas Un prompt integral para generar un juego de batalla de tanques 2D totalmente funcional usando JavaScript y HTML5 Canvas, con especificaciones detalladas para jugabilidad, terreno, IA y personalización.
Contenido del PromptGuardar
🌐
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;
draw();
});
let gameState = 'menu';
let player = { x: 1, y: 1, dir: 'up', lives: 3, cooldown: 0, color: '#4CAF50', special: false };
let enemies = [];
let bullets = [];
let terrain = [];
let score = 0;
let level = 1;
let startTime = 0;
let highScore = localStorage.getItem('tankHighScore') || 0;
let enemyCount = 5;
let selectedColor = '#4CAF50';
let keys = {};
let touchStart = null;
const terrainTypes = {
solid: { color: '#555', shootable: false, passable: false },
breakable: { color: '#B8860B', shootable: true, passable: false, hp: 2 },
water: { color: '#1E90FF', shootable: true, passable: false },
mud: { color: '#8B4513', shootable: true, passable: true, speed: 0.5 },
ground: { color: '#2E8B57', shootable: true, passable: true }
};
function generateMap() {
terrain = [];
for (let y = 0; y < gridSize; y++) {
terrain[y] = [];
for (let x = 0; x < gridSize; x++) {
if (x === 0 || y === 0 || x === gridSize - 1 || y === gridSize - 1) {
terrain[y][x] = { type: 'solid', ...terrainTypes.solid };
} else if (Math.random() < 0.1) {
terrain[y][x] = { type: 'breakable', ...terrainTypes.breakable, hp: 2 };
} else if (Math.random() < 0.05) {
terrain[y][x] = { type: 'water', ...terrainTypes.water };
} else if (Math.random() < 0.05) {
terrain[y][x] = { type: 'mud', ...terrainTypes.mud };
} else {
terrain[y][x] = { type: 'ground', ...terrainTypes.ground };
}
}
}
}
function spawnEnemies(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 ((x === player.x && y === player.y) || terrain[y][x].type !== 'ground');
let behavior = Math.random() < 0.33 ? 'aggressive' : Math.random() < 0.5 ? 'defensive' : 'patrol';
enemies.push({
x, y, dir: 'up', cooldown: 0, behavior,
color: behavior === 'aggressive' ? '#FF4444' : behavior === 'defensive' ? '#FFA500' : '#FF69B4',
speed: 1 + level * 0.1,
special: false
});
}
}
function resetGame() {
player = { x: 1, y: 1, dir: 'down', lives: 3, cooldown: 0, color: selectedColor, special: false };
score = 0;
level = 1;
startTime = Date.now();
generateMap();
spawnEnemies(enemyCount);
gameState = 'playing';
}
function drawTank(x, y, dir, color, isPlayer) {
const cx = x * tileSize + tileSize / 2;
const cy = y * tileSize + tileSize / 2;
ctx.save();
ctx.translate(cx, cy);
ctx.rotate({ up: 0, right: Math.PI / 2, down: Math.PI, left: -Math.PI / 2 }[dir]);
ctx.fillStyle = color;
ctx.fillRect(-tileSize * 0.3, -tileSize * 0.4, tileSize * 0.6, tileSize * 0.8);
ctx.fillStyle = '#333';
ctx.fillRect(-tileSize * 0.1, -tileSize * 0.5, tileSize * 0.2, tileSize * 0.2);
ctx.fillStyle = '#222';
ctx.beginPath();
ctx.arc(0, 0, tileSize * 0.2, 0, Math.PI * 2);
ctx.fill();
if (isPlayer && player.special) {
ctx.fillStyle = '#FFD700';
ctx.beginPath();
ctx.arc(0, 0, tileSize * 0.1, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
}
function drawTerrain() {
for (let y = 0; y < gridSize; y++) {
for (let x = 0; x < gridSize; x++) {
const tile = terrain[y][x];
ctx.fillStyle = tile.color;
ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize);
if (tile.type === 'breakable' && tile.hp < 2) {
ctx.fillStyle = 'rgba(0,0,0,0.3)';
ctx.fillRect(x * tileSize, y * tileSize, tileSize * (1 - tile.hp / 2), tileSize);
}
}
}
}
function drawBullets() {
bullets.forEach(b => {
ctx.fillStyle = b.special ? '#FFD700' : '#FF0000';
ctx.beginPath();
ctx.arc(b.x * tileSize + tileSize / 2, b.y * tileSize + tileSize / 2, tileSize * 0.15, 0, Math.PI * 2);
ctx.fill();
});
}
function drawUI() {
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, 40);
ctx.fillStyle = '#FFF';
ctx.font = '16px monospace';
ctx.fillText(`Score: ${score}`, 10, 25);
ctx.fillText(`Lives: ${'♥'.repeat(player.lives)}`, 150, 25);
ctx.fillText(`Level: ${level}`, 300, 25);
ctx.fillText(`Time: ${Math.floor((Date.now() - startTime) / 1000)}s`, 450, 25);
ctx.fillText(`High: ${highScore}`, 600, 25);
}
function drawMenu() {
ctx.fillStyle = '#1a1a2e';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#FFF';
ctx.font = '48px monospace';
ctx.textAlign = 'center';
ctx.fillText('TANK BATTLE', canvas.width / 2, 100);
ctx.font = '20px monospace';
ctx.fillText('Enemies: ' + enemyCount, canvas.width / 2, 180);
ctx.fillText('Color: ' + selectedColor, canvas.width / 2, 220);
ctx.fillText('Press ENTER to start', canvas.width / 2, 300);
ctx.fillText('Use Arrow Keys / Swipe to move', canvas.width / 2, 340);
ctx.fillText('Space / Tap to shoot', canvas.width / 2, 380);
ctx.textAlign = 'left';
}
function drawGameOver() {
ctx.fillStyle = 'rgba(0,0,0,0.8)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#FFF';
ctx.font = '48px monospace';
ctx.textAlign = 'center';
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 50);
ctx.font = '24px monospace';
ctx.fillText(`Score: ${score}`, canvas.width / 2, canvas.height / 2 + 10);
ctx.fillText(`Time: ${Math.floor((Date.now() - startTime) / 1000)}s`, canvas.width / 2, canvas.height / 2 + 50);
ctx.fillText('Press ENTER to restart', canvas.width / 2, canvas.height / 2 + 100);
ctx.textAlign = 'left';
}
function drawVictory() {
ctx.fillStyle = 'rgba(0,0,0,0.8)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#FFD700';
ctx.font = '48px monospace';
ctx.textAlign = 'center';
ctx.fillText('VICTORY!', canvas.width / 2, canvas.height / 2 - 50);
ctx.fillStyle = '#FFF';
ctx.font = '24px monospace';
ctx.fillText(`Score: ${score}`, canvas.width / 2, canvas.height / 2 + 10);
ctx.fillText(`Time: ${Math.floor((Date.now() - startTime) / 1000)}s`, canvas.width / 2, canvas.height / 2 + 50);
ctx.fillText('Press ENTER for next level', canvas.width / 2, canvas.height / 2 + 100);
ctx.textAlign = 'left';
}
function update() {
if (gameState !== 'playing') return;
if (player.cooldown > 0) player.cooldown -= 1 / 60;
enemies.forEach(e => { if (e.cooldown > 0) e.cooldown -= 1 / 60; });
if (keys['ArrowUp'] || keys['w']) movePlayer(0, -1);
if (keys['ArrowDown'] || keys['s']) movePlayer(0, 1);
if (keys['ArrowLeft'] || keys['a']) movePlayer(-1, 0);
if (keys['ArrowRight'] || keys['d']) movePlayer(1, 0);
if (keys[' '] && player.cooldown <= 0) {
shoot(player, true);
player.cooldown = 2;
}
enemies.forEach(enemy => {
if (enemy.cooldown <= 0 && Math.random() < 0.02) {
shoot(enemy, false);
enemy.cooldown = 2;
}
moveEnemy(enemy);
});
bullets.forEach((bullet, i) => {
bullet.x += bullet.dx;
bullet.y += bullet.dy;
if (bullet.x < 0 || bullet.x >= gridSize || bullet.y < 0 || bullet.y >= gridSize) {
bullets.splice(i, 1);
return;
}
const tile = terrain[Math.floor(bullet.y)][Math.floor(bullet.x)];
if (tile.type === 'breakable') {
tile.hp--;
if (tile.hp <= 0) terrain[Math.floor(bullet.y)][Math.floor(bullet.x)] = { type: 'ground', ...terrainTypes.ground };
bullets.splice(i, 1);
return;
}
if (tile.type === 'solid') {
bullets.splice(i, 1);
return;
}
if (bullet.isPlayer) {
enemies.forEach((enemy, j) => {
if (Math.floor(bullet.x) === enemy.x && Math.floor(bullet.y) === enemy.y) {
enemies.splice(j, 1);
bullets.splice(i, 1);
score += 100;
if (bullet.special) {
const explosionRadius = 2;
for (let dx = -explosionRadius; dx <= explosionRadius; dx++) {
for (let dy = -explosionRadius; dy <= explosionRadius; dy++) {
const ex = enemy.x + dx;
const ey = enemy.y + dy;
if (ex >= 0 && ex < gridSize && ey >= 0 && ey < gridSize && terrain[ey][ex].type === 'breakable') {
terrain[ey][ex] = { type: 'ground', ...terrainTypes.ground };
}
}
}
}
}
});
} else {
if (Math.floor(bullet.x) === player.x && Math.floor(bullet.y) === player.y) {
player.lives--;
bullets.splice(i, 1);
if (player.lives <= 0) {
gameState = 'gameover';
if (score > highScore) {
highScore = score;
localStorage.setItem('tankHighScore', highScore);
}
}
}
}
});
if (enemies.length === 0) {
gameState = 'victory';
score += Math.max(0, 1000 - Math.floor((Date.now() - startTime) / 1000) * 10);
if (score > highScore) {
highScore = score;
localStorage.setItem('tankHighScore', highScore);
}
}
}
function movePlayer(dx, dy) {
const nx = player.x + dx;
const ny = player.y + dy;
if (nx >= 0 && nx < gridSize && ny >= 0 && ny < gridSize) {
const tile = terrain[ny][nx];
if (tile.passable) {
player.x = nx;
player.y = ny;
player.dir = dx !== 0 ? (dx > 0 ? 'right' : 'left') : (dy > 0 ? 'down' : 'up');
if (tile.type === 'mud') {
player.x += dx * 0.5;
player.y += dy * 0.5;
}
}
}
}
function moveEnemy(enemy) {
const dirs = ['up', 'down', 'left', 'right'];
let moved = false;
for (let i = 0; i < 4; i++) {
const dir = dirs[Math.floor(Math.random() * 4)];
const dx = dir === 'left' ? -1 : dir === 'right' ? 1 : 0;
const dy = dir === 'up' ? -1 : dir === 'down' ? 1 : 0;
const nx = enemy.x + dx;
const ny = enemy.y + dy;
if (nx >= 0 && nx < gridSize && ny >= 0 && ny < gridSize && terrain[ny][nx].passable) {
enemy.x = nx;
enemy.y = ny;
enemy.dir = dir;
moved = true;
break;
}
}
if (enemy.behavior === 'aggressive') {
const dx = player.x - enemy.x;
const dy = player.y - enemy.y;
if (Math.abs(dx) > Math.abs(dy)) {
const nx = enemy.x + Math.sign(dx);
if (terrain[enemy.y][nx].passable) enemy.x = nx;
} else {
const ny = enemy.y + Math.sign(dy);
if (terrain[ny][enemy.x].passable) enemy.y = ny;
}
}
}
function shoot(shooter, isPlayer) {
const dirs = { up: [0, -1], down: [0, 1], left: [-1, 0], right: [1, 0] };
const [dx, dy] = dirs[shooter.dir];
bullets.push({
x: shooter.x + dx,
y: shooter.y + dy,
dx, dy,
isPlayer,
special: shooter.special
});
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (gameState === 'menu') {
drawMenu();
} else if (gameState === 'gameover') {
drawGameOver();
} else if (gameState === 'victory') {
drawVictory();
} else {
drawTerrain();
drawBullets();
enemies.forEach(e => moveTank(e.x, e.y, e.dir, e.color, false));
drawTank(player.x, player.y, player.dir, player.color, true);
drawUI();
}
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
document.addEventListener('keydown', e => {
keys[e.key] = true;
if (e.key === 'Enter') {
if (gameState === 'menu') {
resetGame();
} else if (gameState === 'gameover' || gameState === 'victory') {
resetGame();
}
}
if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === ' ') {
e.preventDefault();
}
});
document.addEventListener('keyup', e => { keys[e.key] = false; });
canvas.addEventListener('touchstart', e => {
e.preventDefault();
const touch = e.touches[0];
selectedTouch = { x: touch.clientX, y: touch.clientY };
if (gameState === 'menu') {
resetGame();
}
});
canvas.addEventListener('touchmove', e => {
e.preventDefault();
const touch = e.touches[0];
const dx = touch.clientX - selectedTouch.x;
const dy = touch.clientY - selectedTouch.y;
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 10) movePlayer(1, 0);
else if (dx < -10) movePlayer(-1, 0);
} else {
if (dy > 10) movePlayer(0, 1);
else if (dy < -10) movePlayer(0, -1);
}
selectedTouch = { x: touch.clientX, y: touch.clientY };
});
canvas.addEventListener('touchend', e => {
e.preventDefault();
if (player.cooldown <= 0) {
shoot(player, true);
player.cooldown = 2;
}
});
gameState = 'menu';
gameLoop();
Iniciá sesión para ver el prompt completo
Continuar con:
Al iniciar sesión, aceptás nuestros Términos de uso y Política de privacidad
Uso
Este prompt está diseñado para usarse con coding. Copiá el contenido de arriba y pegalo en tu herramienta de IA preferida.
Para mejores resultados, personalizá los marcadores (indicados con corchetes o mayúsculas) con tus requisitos específicos.
Referencias
- Categoría: Prompts de coding
- Fuente: https://x.com/d4m1n/status/1953551805599580302
Discusión
0 comentarios