Discussion

Jeu de bataille de chars 2D en JavaScript avec Canvas

De Wikiprompt, l’encyclopédie libre de prompts

Dan ⚡️
Contribué parDan ⚡️XSource

7 août 2025

Jeu de bataille de chars 2D en JavaScript avec Canvas Une invite complète pour générer un jeu de bataille de chars 2D entièrement fonctionnel en utilisant JavaScript et HTML5 Canvas, avec des spécifications détaillées pour le gameplay, le terrain, l'IA et la personnalisation.

Contenu du PromptEnregistrer

🌐
const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const GRID_SIZE = 36; const TILE_SIZE = 20; let canvasWidth, canvasHeight, tileSize, offsetX, offsetY; let gameState = 'menu'; let player, enemies, bullets, explosions, terrain, powerups; let score = 0, lives = 3, level = 1, startTime, elapsedTime; let keys = {}, touchStart = null, touchEnd = null; let lastShotTime = 0; let selectedEnemyCount = 5; let selectedTankColor = '#4CAF50'; let highScores = JSON.parse(localStorage.getItem('tankBattleHighScores')) || []; function resizeCanvas() { canvasWidth = window.innerWidth; canvasHeight = window.innerHeight; canvas.width = canvasWidth; canvas.height = canvasHeight; tileSize = Math.min(canvasWidth, canvasHeight) / (GRID_SIZE + 2); offsetY = (canvasHeight - tileSize * GRID_SIZE) / 2; offsetX = (canvasWidth - tileSize * GRID_SIZE) / 2; } window.addEventListener('resize', resizeCanvas); resizeCanvas(); function initGame() { player = { x: 1, y: 1, direction: 'right', lives: 3, cooldown: 0, color: selectedTankColor, speed: 1, hasPowerup: false }; enemies = []; bullets = []; explosions = []; terrain = []; powerups = []; score = 0; level = 1; startTime = Date.now(); lastShotTime = 0; generateTerrain(); spawnEnemies(selectedEnemyCount); gameState = 'playing'; } function generateTerrain() { terrain = Array(GRID_SIZE).fill(null).map(() => Array(GRID_SIZE).fill('open')); for (let i = 0; i < GRID_SIZE; i++) { for (let j = 0; j < GRID_SIZE; j++) { if (i === 0 || i === GRID_SIZE - 1 || j === 0 || j === GRID_SIZE - 1) { terrain[i][j] = 'solid'; } else if (Math.random() < 0.1) { terrain[i][j] = 'solid'; } else if (Math.random() < 0.15) { terrain[i][j] = 'breakable'; } else if (Math.random() < 0.05) { terrain[i][j] = 'water'; } else if (Math.random() < 0.05) { terrain[i][j] = 'mud'; } } } terrain[1][1] = 'open'; terrain[GRID_SIZE - 2][GRID_SIZE - 2] = 'open'; } function spawnEnemies(count) { for (let i = 0; i < count; i++) { let x, y; do { x = Math.floor(Math.random() * (GRID_SIZE - 2)) + 1; y = Math.floor(Math.random() * (GRID_SIZE - 2)) + 1; } while ((x === 1 && y === 1) || (x === GRID_SIZE - 2 && y === GRID_SIZE - 2) || terrain[x][y] !== 'open'); const behavior = ['aggressive', 'defensive', 'patrolling'][Math.floor(Math.random() * 3)]; enemies.push({ x, y, direction: ['up', 'down', 'left', 'right'][Math.floor(Math.random() * 4)], color: behavior === 'aggressive' ? '#FF5722' : behavior === 'defensive' ? '#2196F3' : '#FFC107', behavior, cooldown: 0, speed: 0.5 + level * 0.1, fireRate: 2000, lastShot: 0 }); } } function update(deltaTime) { if (gameState !== 'playing') return; elapsedTime = (Date.now() - startTime) / 1000; handlePlayerMovement(deltaTime); handlePlayerShooting(); updateEnemies(deltaTime); updateBullets(deltaTime); updateExplosions(deltaTime); updatePowerups(deltaTime); checkCollisions(); checkWinLose(); } function handlePlayerMovement(deltaTime) { let dx = 0, dy = 0; if (keys['ArrowUp'] || keys['w']) { dy = -1; player.direction = 'up'; } if (keys['ArrowDown'] || keys['s']) { dy = 1; player.direction = 'down'; } if (keys['ArrowLeft'] || keys['a']) { dx = -1; player.direction = 'left'; } if (keys['ArrowRight'] || keys['d']) { dx = 1; player.direction = 'right'; } if (dx !== 0 || dy !== 0) { let newX = player.x + dx * player.speed * deltaTime; let newY = player.y + dy * player.speed * deltaTime; if (canMove(newX, newY)) { player.x = newX; player.y = newY; } } } function canMove(x, y) { let gridX = Math.floor(x); let gridY = Math.floor(y); if (gridX < 0 || gridX >= GRID_SIZE || gridY < 0 || gridY >= GRID_SIZE) return false; let tile = terrain[gridX][gridY]; return tile === 'open' || tile === 'mud'; } function handlePlayerShooting() { if (Date.now() - lastShotTime > 2000) { let bullet = createBullet(player.x + 0.5, player.y + 0.5, player.direction, player.color, player.firePowerup); bullets.push(bullet); lastShotTime = Date.now(); } } function createBullet(x, y, direction, color, isPowerup) { return { x, y, direction, color, speed: 10, isPowerup: isPowerup || false, blastRadius: isPowerup ? 2 : 1 }; } function updateEnemies(deltaTime) { for (let enemy of enemies) { 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'; } let moveX = enemy.x + (enemy.direction === 'right' ? 1 : enemy.direction === 'left' ? -1 : 0) * enemy.speed * deltaTime; let moveY = enemy.y + (enemy.direction === 'down' ? 1 : enemy.direction === 'up' ? -1 : 0) * enemy.speed * deltaTime; if (canMove(moveX, moveY)) { enemy.x = moveX; enemy.y = moveY; } } else if (enemy.behavior === 'defensive') { if (Math.abs(player.x - enemy.x) < 5 && Math.abs(player.y - enemy.y) < 5) { 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 (Math.random() < 0.01) { enemy.direction = ['up', 'down', 'left', 'right'][Math.floor(Math.random() * 4)]; } let newX = enemy.x + (enemy.direction === 'right' ? 1 : enemy.direction === 'left' ? -1 : 0) * enemy.speed * deltaTime; let newY = enemy.y + (enemy.direction === 'down' ? 1 : enemy.direction === 'up' ? -1 : 0) * enemy.speed * deltaTime; if (canMove(newX, newY)) { enemy.x = newX; enemy.y = newY; } } if (Date.now() - enemy.lastShot > enemy.maxRate) { let bullet = createBullet(enemy.x + 0.5, enemy.y + 0.5, enemy.direction, enemy.color, false); bullets.push(bullet); enemy.lastShot = Date.now(); } } } function updateBullets(deltaTime) { for (let i = bullets.length - 1; i >= 0; i--) { let bullet = bullets[i]; let dx = bullet.direction === 'right' ? 1 : bullet.direction === 'left' ? -1 : 0; let dy = bullet.direction === 'down' ? 1 : bullet.direction === 'up' ? -1 : 0; bullet.x += dx * bullet.speed * deltaTime; bullet.y += dy * bullet.speed * deltaTime; let gridX = Math.floor(bullet.x); let gridY = Math.floor(bullet.y); if (gridX < 0 || gridX >= GRID_SIZE || gridY < 0 || gridY >= GRID_SIZE) { bullets.splice(i, 1); continue; } let tile = terrain[gridX][gridY]; if (tile === 'solid') { bullets.splice(i, 1); } else if (tile === 'breakable') { terrain[gridX][gridY] = 'open'; createExplosion(gridX, gridY, bullet.blastRadius); bullets.splice(i, 1); } } } function updateExplosions(deltaTime) { for (let i = explosions.length - 1; i >= 0; i--) { explosions[i].life -= deltaTime; if (explosions[i].life <= 0) { explosions.splice(i, 1); } } } function updatePowerups(deltaTime) { for (let i = powerups.length - 1; i >= 0; i--) { let powerup = powerups[i]; let dist = Math.sqrt((player.x - powerup.x) ** 2 + (player.y - powerup.y) ** 2); if (dist < 1) { player.firePowerUp = true; powerups.splice(i, 1); } } } function checkCollisions() { for (let i = bullets.length - 1; i >= 0; i--) { let bullet = bullets[i]; if (bullet.isPlayer) { for (let j = enemies.length - 1; j >= 0; j--) { let enemy = enemies[j]; if (Math.abs(bullet.x - enemy.x) < 0.5 && Math.abs(bullet.y - enemy.y) < 0.5) { enemies.splice(j, 1); score += 100; bullets.splice(i, 1); createExplosion(enemy.x, enemy.y, bullet.blastRadius); if (Math.random() < 0.2) { powerups.push({ x: enemy.x, y: enemy.y, type: 'powerup' }); } break; } } } else { if (Math.abs(bullet.x - player.x) < 0.5 && Math.abs(bullet.y - player.y) < 0.5) { player.lives--; bullets.splice(i, 1); createExplosion(player.x, player.y, 1); if (player.lives <= 0) { gameState = 'gameover'; } } } } } function createExplosion(x, y, radius) { explosions.push({ x, y, radius, life: 0.5 }); } function checkWinLose() { if (enemies.length === 0) { level++; score += Math.max(0, 1000 - Math.floor(elapsedTime)); if (level > 3) { gameState = 'victory'; saveHighScore(); } else { spawnEnemies(selectedEnemyCount + level); startTime = Date.now(); } } } function saveHighScore() { let totalScore = score + player.lives * 500; highScores.push({ score: totalScore, time: Math.floor(elapsedTime), date: new Date().toLocaleDateString() }); highScores.sort((a, b) => b.score - a.score); highScores = highScores.slice(0, 5); localStorage.setItem('tankBattleHighScores', JSON.stringify(highScores)); } function draw() { ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.fillStyle = '#1a1a2e'; ctx.fillRect(0, 0, canvasWidth, canvasHeight); drawTerrain(); drawPowerups(); drawTanks(); drawBullets(); drawExplosions(); drawUI(); if (gameState === 'menu') drawMenu(); if (gameState === 'gameover') drawGameOver(); if (gameState === 'victory') drawVictory(); } function drawTerrain() { for (let i = 0; i < GRID_SIZE; i++) { for (let j = 0; j < GRID_SIZE; j++) { let x = offsetX + i * tileSize; let y = offsetY + j * tileSize; let tile = terrain[i][j]; if (tile === 'solid') { ctx.fillStyle = '#555'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#333'; ctx.strokeRect(x, y, tileSize, tileSize); } else if (tile === 'breakable') { ctx.fillStyle = '#8B4513'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#654321'; ctx.strokeRect(x, y, tileSize, tileSize); } else if (tile === 'water') { ctx.fillStyle = '#2196F3'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#1976D2'; ctx.strokeRect(x, y, tileSize, tileSize); } else if (tile === 'mud') { ctx.fillStyle = '#795548'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#5D4037'; ctx.strokeRect(x, y, tileSize, tileSize); } else { ctx.fillStyle = '#2d2d44'; ctx.fillRect(x, y, tileSize, tileSize); ctx.strokeStyle = '#1a1a2e'; ctx.strokeRect(x, y, tileSize, tileSize); } } } } function drawTanks() { drawTank(player.x, player.y, player.direction, player.color); for (let enemy of enemies) { drawTank(enemy.x, enemy.y, enemy.direction, enemy.color); } } function drawTank(x, y, direction, color) { let px = offsetX + x * tileSize; let py = offsetY + y * tileSize; ctx.save(); ctx.translate(px + tileSize / 2, py + tileSize / 2); ctx.rotate(direction === 'right' ? 0 : direction === 'down' ? Math.PI / 2 : direction === 'left' ? Math.PI : -Math.PI / 2); ctx.fillStyle = color; ctx.fillRect(-tileSize * 0.3, -tileSize * 0.3, tileSize * 0.6, tileSize * 0.6); ctx.fillStyle = '#333'; ctx.fillRect(-tileSize * 0.1, -tileSize * 0.5, tileSize * 0.2, tileSize * 0.3); ctx.fillStyle = '#666'; ctx.fillRect(-tileSize * 0.25, -tileSize * 0.25, tileSize * 0.5, tileSize * 0.1); ctx.fillRect(-tileSize * 0.25, tileSize * 0.15, tileSize * 0.5, tileSize * 0.1); ctx.restore(); } function drawBullets() { for (let bullet of bullets) { let px = offsetX + bullet.x * tileSize; let py = offsetY + bullet.y * tileSize; ctx.fillStyle = bullet.color; ctx.beginPath(); ctx.arc(px, py, tileSize * 0.15, 0, Math.PI * 2); ctx.fill(); } } function drawExplosions() { for (let explosion of explosions) { let px = offsetX + explosion.x * tileSize; let py = offsetY + explosion.y * tileSize; let radius = explosion.radius * tileSize * (1 - explosion.life); ctx.fillStyle = `rgba(255, 165, 0, ${explosion.life})`; ctx.beginPath(); ctx.arc(px, py, radius, 0, Math.PI * 2); ctx.fill(); } } function drawPowerups() { for (let powerup of powerups) { let px = offsetX + powerup.x * tileSize; let py = offsetY + powerup.y * tileSize; ctx.fillStyle = '#FFD700'; ctx.beginPath(); ctx.arc(px, py, tileSize * 0.3, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = '#FF0000'; ctx.font = `${tileSize * 0.4}px Arial`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText('P', px, py); } } function drawUI() { ctx.fillStyle = '#FFF'; ctx.font = '20px Arial'; ctx.textAlign = 'left'; ctx.fillText(`Score: ${score}`, 10, 30); ctx.fillText(`Lives: ${'❤'.repeat(player.lives)}`, 10, 60); ctx.fillText(`Level: ${level}`, 10, 90); ctx.fillText(`Time: ${Math.floor(elapsedTime)}s`, 10, 120); } function drawMenu() { ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.fillRect(0, 0, canvasWidth, canvasHeight); ctx.fillStyle = '#FFF'; ctx.font = '48px Arial'; ctx.textAlign = 'center'; ctx.fillText('TANK BATTLE', canvasWidth / 2, canvasHeight / 2 - 100); ctx.font = '24px Arial'; ctx.fillText('Enemy Count:', canvasWidth / 2 - 100, canvasHeight / 2 - 20); ctx.fillText(selectedEnemyCount, canvasWidth / 2 + 100, canvasHeight / 2 - 20); ctx.fillText('Tank Color:', canvasWidth / 2 - 100, canvasHeight / 2 + 30); ctx.fillStyle = selectedTankColor; ctx.fillRect(canvasWidth / 2 + 50, canvasHeight / 2 + 15, 30, 30); ctx.fillStyle = '#FFF'; ctx.font = '20px Arial'; ctx.fillText('Press SPACE to Start', canvasWidth / 2, canvasHeight / 2 + 100); ctx.fillText('Use Arrow Keys to Move, SPACE to Shoot', canvasWidth / 2, canvasHeight / 2 + 140); ctx.fillText('Swipe to Move, Tap to Shoot on Mobile', canvasWidth / 2, canvasHeight / 2 + 170); ctx.font = '16px Arial'; ctx.fillText('High Scores:', canvasWidth / 2, canvasHeight / 2 + 220); for (let i = 0; i < highScores.length; i++) { ctx.fillText(`${i + 1}. ${highScores[i].score} - ${highScores[i].date}`, canvasWidth / 2, canvasHeight / 2 + 250 + i * 30); } } function drawGameOver() { ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.fillRect(0, 0, canvasWidth, canvasHeight); ctx.fillStyle = '#FF0000'; ctx.font = '48px Arial'; ctx.textAlign = 'center'; ctx.fillText('GAME OVER', canvasWidth / 2, canvasHeight / 2 - 50); ctx.fillStyle = '#FFF'; ctx.font = '24px Arial'; ctx.fillText(`Score: ${score}`, canvasWidth / 2, canvasHeight / 2 + 20); ctx.fillText('Press SPACE to Restart', canvasWidth / 2, canvasHeight / 2 + 80); } function drawVictory() { ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; ctx.fillRect(0, 0, canvasWidth, canvasHeight); ctx.fillStyle = '#FFD700'; ctx.font = '48px Arial'; ctx.textAlign = 'center'; ctx.fillText('VICTORY!', canvasWidth / 2, canvasHeight / 2 - 50); ctx.fillStyle = '#FFF'; ctx.font = '24px Arial'; ctx.fillText(`Final Score: ${score + player.lives * 100}`, canvasWidth / 2, canvasHeight / 2 + 20); ctx.fillText('Press SPACE to Play Again', canvasWidth / 2, canvasHeight / 2 + 80); } function gameLoop(timestamp) { let deltaTime = Math.min((timestamp - lastTime) / 1000, 0.1); lastTime = timestamp; update(deltaTime); draw(); requestAnimationFrame(gameLoop); } let lastTime = 0; requestAnimationFrame(gameLoop); window.addEventListener('keydown', (e) => { keys[e.key] = true; if (e.key === ' ') { if (gameState === 'menu') { initGame(); } else if (gameState === 'gameover' || gameState === 'victory') { gameState = 'menu'; } else { handlePlayerShooting(); } } if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') { e.preventDefault(); } }); window.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 }; if (gameState === 'playing') { handlePlayerShooting(); } }); canvas.addEventListener('touchmove', (e) => { e.preventDefault(); let touch = e.touches[0]; touchEnd = { x: touch.clientX, y: touch.clientY }; if (touchStart && touchEnd) { let dx = touchEnd.x - touchStart.x; let dy = touchEnd.y - touchStart.y; if (Math.abs(dx) > Math.abs(dy)) { if (dx > 0) keys['ArrowRight'] = true; else keys['ArrowLeft'] = true; } else { if (dy > 0) keys['ArrowDown'] = true; else keys['ArrowUp'] = true; } } }); canvas.addEventListener('touchend', (e) => { e.preventDefault(); touchStart = null; touchEnd = null; keys['ArrowUp'] = false; keys['ArrowDown'] = false; keys['ArrowLeft'] = false; keys['ArrowRight'] = false; }); canvas.addEventListener('click', (e) => { if (gameState === 'menu') { let rect = canvas.getBoundingClientRect(); let x = e.clientX - rect.left; let y = e.clientY - rect.top; if (x > canvasWidth / 2 - 100 && x < canvasWidth / 2 + 100 && y > canvasHeight / 2 - 40 && y < canvasHeight / 2) { selectedEnemyCount = Math.min(10, selectedEnemyCount + 1); } if (x > canvasWidth / 2 - 100 && x < canvasWidth / 2 + 100 && y > canvasHeight / 2 + 10 && y < canvasHeight / 2 + 50) { selectedTankColor = ['#4CAF50', '#FF9800', '#9C27B0', '#00BCD4'][Math.floor(Math.random() * 4)]; } } });

Connectez-vous pour voir le prompt complet

Continuer avec:

En vous connectant, vous acceptez nos Conditions et Confidentialité

Utilisation

Ce prompt est conçu pour être utilisé avec coding. Copiez le contenu ci-dessus et collez-le dans votre outil d’IA préféré.

Pour de meilleurs résultats, personnalisez les espaces réservés (indiqués par des crochets ou des majuscules) selon vos besoins.

Références

Catégories :coding| twitter| javascript| canvas

Discussion