Cyber Grid Link : Jeu de Serpent sur Canvas HTML5
De Wikiprompt, l’encyclopédie libre de prompts
Cyber Grid Link : Jeu de Serpent sur Canvas HTML5 Invite détaillée pour générer un jeu HTML5 complet sur canvas à fichier unique avec une mécanique de serpent cyberpunk, incluant les spécifications de rendu, de contrôles et de style visuel.
Contenu du PromptEnregistrer
🌐
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cyber Grid Link</title>
<style>
body {
margin: 0;
padding: 0;
background: #0a0a0f;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: 'Courier New', monospace;
overflow: hidden;
}
canvas {
border: 2px solid #00ffcc;
box-shadow: 0 0 30px rgba(0, 255, 204, 0.3);
background: #0a0a0f;
}
#ui {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
color: #00ffcc;
font-size: 18px;
text-shadow: 0 0 10px #00ffcc;
z-index: 10;
text-align: center;
}
#gameOver {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ff0044;
font-size: 32px;
text-shadow: 0 0 20px #ff0044;
z-index: 10;
display: none;
text-align: center;
}
#gameOver button {
background: #0a0a0f;
border: 2px solid #ff0044;
color: #ff0044;
font-family: 'Courier New', monospace;
font-size: 18px;
padding: 10px 20px;
margin-top: 20px;
cursor: pointer;
text-shadow: 0 0 10px #ff0044;
}
#gameOver button:hover {
background: #ff0044;
color: #0a0a0f;
}
</style>
</head>
<body>
<div id="ui">
<span>SCORE: <span id="score">0</span></span>
<span style="margin-left: 30px;">LINK: <span id="length">3</span></span>
</div>
<div id="gameOver">
<div>LINK BROKEN</div>
<button onclick="restartGame()">REINITIALIZE</button>
</div>
<canvas id="gameCanvas"></canvas>
<script>
// ========== CONFIG ==========
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const lengthEl = document.getElementById('length');
const gameOverEl = document.getElementById('gameOver');
const GRID_SIZE = 40;
const CELL_SIZE = 20;
const VIEW_WIDTH = GRID_SIZE * CELL_SIZE;
const VIEW_HEIGHT = GRID_SIZE * CELL_SIZE;
canvas.width = VIEW_WIDTH;
canvas.height = VIEW_HEIGHT;
// ========== GAME STATE ==========
let snake = [];
let direction = { x: 1, y: 0 };
let nextDirection = { x: 1, y: 0 };
let crystals = [];
let firewalls = [];
let score = 0;
let gameRunning = true;
let lastTime = 0;
let moveTimer = 0;
const MOVE_INTERVAL = 150; // ms per grid step
// ========== INITIALIZE ==========
function initGame() {
snake = [];
// Start with 3 segments in the middle
const startX = Math.floor(GRID_SIZE / 2);
const startY = Math.floor(GRID_SIZE / 2);
for (let i = 0; i < 3; i++) {
snake.push({ x: startX - i, y: startY, px: startX - i, py: startY });
}
direction = { x: 1, y: 0 };
nextDirection = { x: 1, y: 0 };
crystals = [];
firewalls = [];
score = 0;
gameRunning = true;
gameOverEl.style.display = 'none';
updateUI();
spawnCrystal();
spawnFirewalls();
}
// ========== SPAWNING ==========
function spawnCrystal() {
let pos;
do {
pos = {
x: Math.floor(Math.random() * GRID_SIZE),
y: Math.floor(Math.random() * GRID_SIZE)
};
} while (snake.some(s => s.x === pos.x && s.y === pos.y) ||
crystals.some(c => c.x === pos.x && c.y === pos.y));
crystals.push({ x: pos.x, y: pos.y, pulse: 0 });
}
function spawnFirewalls() {
// Spawn 3-5 firewalls at random positions, moving in random directions
const count = 3 + Math.floor(Math.random() * 3);
for (let i = 0; i < count; i++) {
let pos;
do {
pos = {
x: Math.floor(Math.random() * GRID_SIZE),
y: Math.floor(Math.random() * GRID_SIZE)
};
} while (snake.some(s => s.x === pos.x && s.y === pos.y) ||
crystals.some(c => c.x === pos.x && c.y === pos.y) ||
firewalls.some(f => f.x === pos.x && f.y === pos.y));
const dirs = [
{ x: 1, y: 0 }, { x: -1, y: 0 }, { x: 0, y: 1 }, { x: 0, y: -1 }
];
firewalls.push({
x: pos.x, y: pos.y,
dir: dirs[Math.floor(Math.random() * 4)],
speed: 0.5 + Math.random() * 0.5
});
}
}
// ========== INPUT ==========
document.addEventListener('keydown', (e) => {
if (!gameRunning) return;
switch(e.key) {
case 'ArrowUp':
case 'w':
if (direction.y !== 1) nextDirection = { x: 0, y: -1 };
break;
case 'ArrowDown':
case 's':
if (direction.y !== -1) nextDirection = { x: 0, y: 1 };
break;
case 'ArrowLeft':
case 'a':
if (direction.x !== 1) nextDirection = { x: -1, y: 0 };
break;
case 'ArrowRight':
case 'd':
if (direction.x !== -1) nextDirection = { x: 1, y: 0 };
break;
}
});
// ========== GAME LOGIC ==========
function update(deltaTime) {
if (!gameRunning) return;
// Move firewalls
firewalls.forEach(fw => {
fw.x += fw.dir.x * fw.speed * (deltaTime / 1000) * 5;
fw.y += fw.dir.y * fw.speed * (deltaTime / 1000) * 5;
// Wrap around edges
if (fw.x < 0) fw.x = GRID_SIZE - 1;
if (fw.x >= GRID_SIZE) fw.x = 0;
if (fw.y < 0) fw.y = GRID_SIZE - 1;
if (fw.y >= GRID_SIZE) fw.y = 0;
});
// Move snake on timer
moveTimer += deltaTime;
if (moveTimer >= MOVE_INTERVAL) {
moveTimer -= MOVE_INTERVAL;
direction = { ...nextDirection };
// Save previous positions for interpolation
snake.forEach(seg => {
seg.px = seg.x;
seg.py = seg.y;
});
// Move head
const head = { ...snake[0] };
head.x += direction.x;
head.y += direction.y;
// Wrap around
if (head.x < 0) head.x = GRID_SIZE - 1;
if (head.x >= GRID_SIZE) head.x = 0;
if (head.y < 0) head.y = GRID_SIZE - 1;
if (head.y >= GRID_SIZE) head.y = 0;
// Check collision with self
if (snake.some(s => s.x === head.x && s.y === head.y)) {
gameOver();
return;
}
// Check collision with firewalls
if (firewalls.some(fw => Math.floor(fw.x) === head.x && Math.floor(fw.y) === head.y)) {
gameOver();
return;
}
snake.unshift(head);
// Check crystal collection
const crystalIdx = crystals.findIndex(c => c.x === head.x && c.y === head.y);
if (crystalIdx !== -1) {
crystals.splice(crystalIdx, 1);
score += 10;
updateUI();
spawnCrystal();
// Don't remove tail - snake grows
} else {
snake.pop();
}
updateUI();
}
}
function gameOver() {
gameRunning = false;
gameOverEl.style.display = 'block';
}
function restartGame() {
initGame();
}
function updateUI() {
scoreEl.textContent = score;
lengthEl.textContent = snake.length;
}
// ========== RENDERING ==========
function drawGrid(time) {
ctx.strokeStyle = 'rgba(0, 255, 204, 0.15)';
ctx.lineWidth = 1;
// Warp effect near head
const head = snake[0];
const warpRadius = 3;
for (let i = 0; i <= GRID_SIZE; i++) {
ctx.beginPath();
for (let j = 0; j <= GRID_SIZE; j++) {
let x = i * CELL_SIZE;
let y = j * CELL_SIZE;
// Apply warp displacement
const dx = (i - head.x) / warpRadius;
const dy = (j - head.y) / warpRadius;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 1) {
const warp = (1 - dist) * 8;
x += dx * warp;
y += dy * warp;
}
if (j === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
}
for (let j = 0; j <= GRID_SIZE; j++) {
ctx.beginPath();
for (let i = 0; i <= GRID_SIZE; i++) {
let x = i * CELL_SIZE;
let y = j * CELL_SIZE;
const dx = (i - head.x) / warpRadius;
const dy = (j - head.y) / warpRadius;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 1) {
const warp = (1 - dist) * 8;
x += dx * warp;
y += dy * warp;
}
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.stroke();
}
}
function drawSnake(time) {
if (snake.length === 0) return;
// Draw chain with pulsing gradient
for (let i = snake.length - 1; i >= 0; i--) {
const seg = snake[i];
// Interpolate position
const alpha = Math.min(1, moveTimer / MOVE_INTERVAL);
const x = seg.px + (seg.x - seg.px) * alpha;
const y = seg.py + (seg.y - seg.py) * alpha;
const screenX = x * CELL_SIZE + CELL_SIZE / 2;
const screenY = y * CELL_SIZE + CELL_SIZE / 2;
// Pulsing gradient based on position in chain
const pulse = Math.sin(time / 200 + i * 0.5) * 0.5 + 0.5;
const size = CELL_SIZE * 0.7 * (1 - (i / snake.length) * 0.3);
const hue = 160 + (i / snake.length) * 40;
const gradient = ctx.createRadialGradient(screenX, screenY, 0, screenX, screenY, size);
gradient.addColorStop(0, `hsla(${hue}, 100%, 70%, ${0.9 - (i / snake.length) * 0.5})`);
gradient.addColorStop(1, `hsla(${hue}, 100%, 30%, ${0.3 - (i / snake.length) * 0.2})`);
ctx.beginPath();
ctx.arc(screenX, screenY, size, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
// Glow effect
ctx.shadowColor = `hsla(${hue}, 100%, 50%, ${0.5 * pulse})`;
ctx.shadowBlur = 15;
ctx.beginPath();
ctx.arc(screenX, screenY, size * 0.5, 0, Math.PI * 2);
ctx.fillStyle = `hsla(${hue}, 100%, 80%, ${0.3 * pulse})`;
ctx.fill();
ctx.shadowBlur = 0;
}
// Head glow
const head = snake[0];
const headX = head.px + (head.x - head.px) * Math.min(1, moveTimer / MOVE_INTERVAL);
const headY = head.py + (head.y - head.py) * Math.min(1, moveTimer / MOVE_INTERVAL);
ctx.shadowColor = '#00ffcc';
ctx.shadowBlur = 25;
ctx.beginPath();
ctx.arc(headX * CELL_SIZE + CELL_SIZE / 2, headY * CELL_SIZE + CELL_SIZE / 2, CELL_SIZE * 0.8, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(0, 255, 204, 0.3)';
ctx.fill();
ctx.shadowBlur = 0;
}
function drawCrystals(time) {
crystals.forEach(c => {
c.pulse += 0.05;
const pulse = Math.sin(c.pulse) * 0.5 + 0.5;
const x = c.x * CELL_SIZE + CELL_SIZE / 2;
const y = c.y * CELL_SIZE + CELL_SIZE / 2;
// Diamond shape
ctx.save();
ctx.translate(x, y);
ctx.rotate(time / 1000);
ctx.shadowColor = '#ff00ff';
ctx.shadowBlur = 20 * pulse;
ctx.beginPath();
ctx.moveTo(0, -CELL_SIZE * 0.6);
ctx.lineTo(CELL_SIZE * 0.4, 0);
ctx.lineTo(0, CELL_SIZE * 0.6);
ctx.lineTo(-CELL_SIZE * 0.4, 0);
ctx.closePath();
const gradient = ctx.createLinearGradient(-CELL_SIZE * 0.4, 0, CELL_SIZE * 0.4, 0);
gradient.addColorStop(0, '#ff00ff');
gradient.addColorStop(0.5, '#00ffff');
gradient.addColorStop(1, '#ff00ff');
ctx.fillStyle = gradient;
ctx.fill();
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 1;
ctx.stroke();
ctx.restore();
});
}
function drawFirewalls(time) {
firewalls.forEach(fw => {
const x = fw.x * CELL_SIZE + CELL_SIZE / 2;
const y = fw.y * CELL_SIZE + CELL_SIZE / 2;
ctx.shadowColor = '#ff0044';
ctx.shadowBlur = 15;
// Animated flame-like shape
const flameHeight = CELL_SIZE * (0.8 + Math.sin(time / 100 + fw.x) * 0.2);
ctx.beginPath();
ctx.moveTo(x - CELL_SIZE * 0.3, y + CELL_SIZE * 0.4);
ctx.quadraticCurveTo(x - CELL_SIZE * 0.4, y - flameHeight * 0.3, x, y - flameHeight);
ctx.quadraticCurveTo(x + CELL_SIZE * 0.4, y - flameHeight * 0.3, x + CELL_SIZE * 0.3, y + CELL_SIZE * 0.4);
ctx.closePath();
const gradient = ctx.createLinearGradient(x, y + CELL_SIZE * 0.4, x, y - flameHeight);
gradient.addColorStop(0, 'rgba(255, 0, 68, 0.8)');
gradient.addColorStop(1, 'rgba(255, 100, 0, 0.2)');
ctx.fillStyle = gradient;
ctx.fill();
ctx.shadowBlur = 0;
});
}
// ========== MAIN LOOP ==========
function gameLoop(time) {
const deltaTime = Math.min(50, time - lastTime);
lastTime = time;
update(deltaTime);
// Clear
ctx.fillStyle = '#0a0a0f';
ctx.fillRect(0, 0, VIEW_WIDTH, VIEW_HEIGHT);
// Draw everything
drawGrid(time);
drawCrystals(time);
drawFirewalls(time);
drawSnake(time);
requestAnimationFrame(gameLoop);
}
// ========== START ==========
initGame();
requestAnimationFrame(gameLoop);
</script>
</body>
</html>
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.
Discussion
0 commentaires