Axial Drift : Jeu de labyrinthe physique adapté aux mobiles
De Wikiprompt, l’encyclopédie libre de prompts
Axial Drift : Jeu de labyrinthe physique adapté aux mobiles Une invite complète pour générer un jeu de labyrinthe HTML5 en fichier unique avec des mécaniques de changement de gravité, des contrôles d'inclinaison mobile et un style visuel Bauhaus.
Contenu du PromptEnregistrer
🌐
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Axial Drift</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
body {
background: #1a1a1a;
overflow: hidden;
touch-action: none;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
margin: 0 auto;
background: #f5f0e8;
box-shadow: 0 0 40px rgba(0,0,0,0.8);
}
#ui {
position: fixed;
top: 10px;
left: 50%;
transform: translateX(-50%);
color: #ff4500;
font-size: 14px;
font-weight: bold;
letter-spacing: 2px;
text-shadow: 2px 2px 0 #000;
z-index: 10;
background: rgba(0,0,0,0.6);
padding: 6px 16px;
border: 2px solid #ff4500;
border-radius: 4px;
pointer-events: none;
}
#win {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #ffd700;
font-size: 28px;
font-weight: bold;
text-shadow: 3px 3px 0 #000;
z-index: 20;
display: none;
background: rgba(0,0,0,0.8);
padding: 20px 40px;
border: 4px solid #ffd700;
border-radius: 8px;
text-align: center;
pointer-events: none;
}
#restart {
position: fixed;
bottom: 20px;
right: 20px;
background: #ff4500;
color: #fff;
border: 3px solid #000;
padding: 10px 20px;
font-size: 16px;
font-weight: bold;
cursor: pointer;
z-index: 30;
border-radius: 4px;
box-shadow: 4px 4px 0 #000;
}
#restart:active {
transform: translate(2px, 2px);
box-shadow: 2px 2px 0 #000;
}
</style>
</head>
<body>
<div id="ui">AXIAL DRIFT - TILT TO NAVIGATE</div>
<div id="win">VORTEX REACHED!<br>YOU WIN</div>
<button id="restart">RESTART</button>
<canvas id="gameCanvas"></canvas>
<script>
(function() {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const ui = document.getElementById('ui');
const winDiv = document.getElementById('win');
const restartBtn = document.getElementById('restart');
// Game state
let W, H, DPR;
let marble = { x: 0, y: 0, vx: 0, vy: 0, radius: 12 };
let gravity = { x: 0, y: 0.5 };
let targetGravity = { x: 0, y: 0.5 };
let mazeWalls = [];
let trapdoors = [];
let vortex = { x: 0, y: 0, radius: 20 };
let gameWon = false;
let lastTime = 0;
let shakeTimer = 0;
// Physics constants
const FRICTION = 0.985;
const DAMPING = 0.95;
const MAX_SPEED = 8;
const GRAVITY_STRENGTH = 0.35;
const TILT_SENSITIVITY = 0.15;
// Maze layout (grid-based, 20x20 cells)
const GRID_SIZE = 20;
const CELL_SIZE = 40;
const mazeLayout = [
"####################",
"#S........#........#",
"#.#####...#...####.#",
"#.#...#...#...#..#.#",
"#.#.#.#.#####.#..#.#",
"#.#.#.#.....#.#..#.#",
"#.#.#####.#.#.#..#.#",
"#.#.....#.#.#.#..#.#",
"#.#####.#.#.#.#..#.#",
"#.....#.#.#.#.#..#.#",
"#####.#.#.#.#.#..#.#",
"#.....#.#.#.#.#..#.#",
"#.#####.#.#.#.#..#.#",
"#.#.....#.#.#.#..#.#",
"#.#.#####.#.#.#..#.#",
"#.#.....#.#.#.#..#.#",
"#.#####.#.#.#.#..#.#",
"#.....#.#.#.#.#..#.#",
"#.....#...V.....#..#",
"####################"
];
function initMaze() {
mazeWalls = [];
trapdoors = [];
const rows = mazeLayout.length;
const cols = mazeLayout[0].length;
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const char = mazeLayout[row][col];
const x = col * CELL_SIZE + CELL_SIZE / 2;
const y = row * CELL_SIZE + CELL_SIZE / 2;
if (char === '#') {
mazeWalls.push({
x: x,
y: y,
w: CELL_SIZE,
h: CELL_SIZE,
type: 'wall'
});
} else if (char === 'S') {
marble.x = x;
marble.y = y;
marble.vx = 0;
marble.vy = 0;
} else if (char === 'V') {
vortex.x = x;
vortex.y = y;
} else if (char === 'T') {
trapdoors.push({
x: x,
y: y,
w: CELL_SIZE * 0.8,
h: CELL_SIZE * 0.8,
active: true,
timer: 0,
phase: Math.random() * Math.PI * 2
});
}
}
}
// Add dynamic trapdoors at strategic positions
trapdoors.push(
{ x: 5 * CELL_SIZE + CELL_SIZE/2, y: 8 * CELL_SIZE + CELL_SIZE/2, w: CELL_SIZE*0.7, h: CELL_SIZE*0.7, active: true, timer: 0, phase: 0 },
{ x: 14 * CELL_SIZE + CELL_SIZE/2, y: 12 * CELL_SIZE + CELL_SIZE/2, w: CELL_SIZE*0.7, h: CELL_SIZE*0.7, active: true, timer: 0, phase: Math.PI },
{ x: 8 * CELL_SIZE + CELL_SIZE/2, y: 15 * CELL_SIZE + CELL_SIZE/2, w: CELL_SIZE*0.7, h: CELL_SIZE*0.7, active: true, timer: 0, phase: Math.PI/2 },
{ x: 12 * CELL_SIZE + CELL_SIZE/2, y: 5 * CELL_SIZE + CELL_SIZE/2, w: CELL_SIZE*0.7, h: CELL_SIZE*0.7, active: true, timer: 0, phase: Math.PI*1.5 }
);
}
function resizeCanvas() {
DPR = window.devicePixelRatio || 1;
const maxWidth = window.innerWidth;
const maxHeight = window.innerHeight;
const mazeWidth = GRID_SIZE * CELL_SIZE;
const mazeHeight = GRID_SIZE * CELL_SIZE;
const scale = Math.min(maxWidth / mazeWidth, maxHeight / mazeHeight);
W = mazeWidth * scale;
H = mazeHeight * scale;
canvas.width = W * DPR;
canvas.height = H * DPR;
canvas.style.width = W + 'px';
canvas.style.height = H + 'px';
ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
// Scale marble and vortex positions
const scaleFactor = scale;
marble.radius = 12 * scaleFactor;
vortex.radius = 20 * scaleFactor;
// Scale all maze elements
mazeWalls.forEach(wall => {
wall.x *= scaleFactor;
wall.y *= scaleFactor;
wall.w *= scaleFactor;
wall.h *= scaleFactor;
});
trapdoors.forEach(td => {
td.x *= scaleFactor;
td.y *= scaleFactor;
td.w *= scaleFactor;
td.h *= scaleFactor;
});
marble.x *= scaleFactor;
marble.y *= scaleFactor;
vortex.x *= scaleFactor;
vortex.y *= scaleFactor;
}
function handleMouse(e) {
if (gameWon) return;
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
// Map mouse position to gravity direction
const centerX = W / 2;
const centerY = H / 2;
const dx = (mouseX - centerX) / (W / 2);
const dy = (mouseY - centerY) / (H / 2);
targetGravity.x = dx * GRAVITY_STRENGTH;
targetGravity.y = dy * GRAVITY_STRENGTH;
}
function handleMouseUp() {
targetGravity.x = 0;
targetGravity.y = 0.5;
}
function handleOrientation(e) {
if (gameWon) return;
const gamma = e.gamma; // left/right tilt (-90 to 90)
const beta = e.beta; // front/back tilt (-180 to 180)
if (gamma !== null && beta !== null) {
// Normalize to -1 to 1 range
const xTilt = Math.max(-1, Math.min(1, gamma / 45));
const yTilt = Math.max(-1, Math.min(1, (beta - 45) / 45));
targetGravity.x = xTilt * GRAVITY_STRENGTH * 1.5;
targetGravity.y = yTilt * GRAVITY_STRENGTH * 1.5;
}
}
function checkCollisions() {
// Wall collisions
for (const wall of mazeWalls) {
const closestX = Math.max(wall.x - wall.w/2, Math.min(marble.x, wall.x + wall.w/2));
const closestY = Math.max(wall.y - wall.h/2, Math.min(marble.y, wall.y + wall.h/2));
const dx = marble.x - closestX;
const dy = marble.y - closestY;
const dist = Math.sqrt(dx*dx + dy*dy);
if (dist < marble.radius) {
if (dist === 0) {
// Push out
marble.x = wall.x + wall.w/2 + marble.radius;
} else {
const overlap = marble.radius - dist;
const nx = dx / dist;
const ny = dy / dist;
marble.x += nx * overlap;
marble.y += ny * overlap;
// Reflect velocity
const dot = marble.vx * nx + marble.vy * ny;
marble.vx -= 2 * dot * nx;
marble.vy -= 2 * dot * ny;
marble.vx *= 0.8;
marble.vy *= 0.8;
}
}
}
// Trapdoor collisions
for (const td of trapdoors) {
if (!td.active) continue;
const closestX = Math.max(td.x - td.w/2, Math.min(marble.x, td.x + td.w/2));
const closestY = Math.max(td.y - td.h/2, Math.min(marble.y, td.y + td.h/2));
const dx = marble.x - closestX;
const dy = marble.y - closestY;
const dist = Math.sqrt(dx*dx + dy*dy);
if (dist < marble.radius) {
// Bounce off trapdoor
if (dist === 0) {
marble.x = td.x + td.w/2 + marble.radius;
} else {
const overlap = marble.radius - dist;
const nx = dx / dist;
const ny = dy / dist;
marble.x += nx * overlap;
marble.y += ny * overlap;
const dot = marble.vx * nx + marble.vy * ny;
marble.vx -= 2 * dot * nx;
marble.vy -= 2 * dot * ny;
marble.vx *= 0.5;
marble.vy *= 0.5;
}
}
}
// Boundary check
if (marble.x < marble.radius) { marble.x = marble.radius; marble.vx *= -0.5; }
if (marble.x > W - marble.radius) { marble.x = W - marble.radius; marble.vx *= -0.5; }
if (marble.y < marble.radius) { marble.y = marble.radius; marble.vy *= -0.5; }
if (marble.y > H - marble.radius) { marble.y = H - marble.radius; marble.vy *= -0.5; }
// Vortex check
const dx = marble.x - vortex.x;
const dy = marble.y - vortex.y;
const dist = Math.sqrt(dx*dx + dy*dy);
if (dist < vortex.radius + marble.radius) {
gameWon = true;
winDiv.style.display = 'block';
}
}
function updatePhysics(dt) {
if (gameWon) return;
// Smooth gravity transition
gravity.x += (targetGravity.x - gravity.x) * 0.1;
gravity.y += (targetGravity.y - gravity.y) * 0.1;
// Apply gravity
marble.vx += gravity.x * dt * 60;
marble.vy += gravity.y * dt * 60;
// Apply friction and damping
marble.vx *= FRICTION;
marble.vy *= FRICTION;
// Clamp speed
const speed = Math.sqrt(marble.vx*marble.vx + marble.vy*marble.vy);
if (speed > MAX_SPEED) {
marble.vx = (marble.vx / speed) * MAX_SPEED;
marble.vy = (marble.vy / speed) * MAX_SPEED;
}
// Update position
marble.x += marble.vx * dt * 60;
marble.y += marble.vy * dt * 60;
// Update trapdoors
const time = performance.now() / 1000;
trapdoors.forEach(td => {
td.timer += dt;
const cycle = Math.sin(time * 2 + td.phase);
td.active = cycle > 0;
});
checkCollisions();
}
function drawBauhausBackground() {
// Background
ctx.fillStyle = '#f5f0e8';
ctx.fillRect(0, 0, W, H);
// Bauhaus geometric pattern
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
// Diagonal lines
for (let i = -H; i < W; i += 80) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i + H, H);
ctx.stroke();
}
// Circles
ctx.fillStyle = '#ff4500';
ctx.beginPath();
ctx.arc(W * 0.1, H * 0.1, 30, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.fillStyle = '#0047ab';
ctx.beginPath();
ctx.arc(W * 0.9, H * 0.9, 25, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
// Triangles
ctx.fillStyle = '#ffd700';
ctx.beginPath();
ctx.moveTo(W * 0.85, H * 0.1);
ctx.lineTo(W * 0.95, H * 0.1);
ctx.lineTo(W * 0.9, H * 0.2);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
function drawMaze() {
// Draw walls
ctx.fillStyle = '#0047ab';
ctx.strokeStyle = '#000';
ctx.lineWidth = 3;
ctx.shadowColor = 'rgba(0,0,0,0.5)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
mazeWalls.forEach(wall => {
ctx.fillRect(wall.x - wall.w/2, wall.y - wall.h/2, wall.w, wall.h);
ctx.strokeRect(wall.x - wall.w/2, wall.y - wall.h/2, wall.w, wall.h);
});
// Draw trapdoors
trapdoors.forEach(td => {
if (td.active) {
ctx.fillStyle = '#ff4500';
ctx.shadowColor = 'rgba(255,69,0,0.5)';
ctx.shadowBlur = 15;
ctx.fillRect(td.x - td.w/2, td.y - td.h/2, td.w, td.h);
ctx.strokeRect(td.x - td.w/2, td.y - td.h/2, td.w, td.h);
// Warning stripes
ctx.fillStyle = '#000';
for (let i = 0; i < 3; i++) {
ctx.fillRect(td.x - td.w/2, td.y - td.h/2 + i * td.h/3, td.w, 2);
}
}
});
// Draw vortex
ctx.shadowColor = 'rgba(255,215,0,0.8)';
ctx.shadowBlur = 20;
ctx.fillStyle = '#ffd700';
ctx.beginPath();
ctx.arc(vortex.x, vortex.y, vortex.radius, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#000';
ctx.lineWidth = 3;
ctx.stroke();
// Vortex spiral
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
for (let i = 0; i < 3; i++) {
ctx.beginPath();
ctx.arc(vortex.x, vortex.y, vortex.radius * (0.3 + i * 0.2), 0, Math.PI * 1.5);
ctx.stroke();
}
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
function drawMarble() {
// Marble shadow
ctx.shadowColor = 'rgba(0,0,0,0.6)';
ctx.shadowBlur = 15;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
// Marble body
const gradient = ctx.createRadialGradient(
marble.x - marble.radius * 0.3,
marble.y - marble.radius * 0.3,
marble.radius * 0.1,
marble.x,
marble.y,
marble.radius
);
gradient.addColorStop(0, '#ffffff');
gradient.addColorStop(0.5, '#e0e0e0');
gradient.addColorStop(1, '#808080');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(marble.x, marble.y, marble.radius, 0, Math.PI * 2);
ctx.fill();
// Marble outline
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.stroke();
// Highlight
ctx.fillStyle = 'rgba(255,255,255,0.8)';
ctx.beginPath();
ctx.arc(marble.x - marble.radius * 0.3, marble.y - marble.radius * 0.3, marble.radius * 0.2, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
}
function drawGravityIndicator() {
// Gravity direction indicator
const centerX = W / 2;
const centerY = H / 2;
const indicatorX = centerX + gravity.x * 100;
const indicatorY = centerY + gravity.y * 100;
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.setLineDash([5, 5]);
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.lineTo(indicatorX, indicatorY);
ctx.stroke();
ctx.setLineDash([]);
// Arrow head
const angle = Math.atan2(indicatorY - centerY, indicatorX - centerX);
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.moveTo(indicatorX, indicatorY);
ctx.lineTo(indicatorX - 10 * Math.cos(angle - 0.4), indicatorY - 10 * Math.sin(angle - 0.4));
ctx.lineTo(indicatorX - 10 * Math.cos(angle + 0.4), indicatorY - 10 * Math.sin(angle + 0.4));
ctx.closePath();
ctx.fill();
}
function draw() {
ctx.clearRect(0, 0, W, H);
drawBauhausBackground();
drawMaze();
drawMarble();
drawGravityIndicator();
// UI text
ctx.fillStyle = '#000';
ctx.font = 'bold 12px Courier New';
ctx.fillText('AXIAL DRIFT', 10, 20);
ctx.fillText('TILT TO NAVIGATE', 10, 35);
}
function gameLoop(timestamp) {
const dt = Math.min((timestamp - lastTime) / 1000, 0.05);
lastTime = timestamp;
updatePhysics(dt);
draw();
requestAnimationFrame(gameLoop);
}
// Event listeners
canvas.addEventListener('mousedown', handleMouse);
canvas.addEventListener('mousemove', (e) => {
if (e.buttons === 1) handleMouse(e);
});
canvas.addEventListener('mouseup', handleMouseUp);
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
const touch = e.touches[0];
handleMouse({ clientX: touch.clientX, clientY: touch.clientY });
});
canvas.addEventListener('touchmove', (e) => {
e.preventDefault();
const touch = e.touches[0];
handleMouse({ clientX: touch.clientX, clientY: touch.clientY });
});
canvas.addEventListener('touchend', handleMouseUp);
if (window.DeviceOrientationEvent) {
window.addEventListener('deviceorientation', handleOrientation, true);
}
restartBtn.addEventListener('click', () => {
gameWon = false;
winDiv.style.display = 'none';
initMaze();
resizeCanvas();
gravity = { x: 0, y: 0.5 };
targetGravity = { x: 0, y: 0.5 };
});
// Initialize
initMaze();
resizeCanvas();
window.addEventListener('resize', () => {
// Re-initialize with new scale
const oldScale = W / (GRID_SIZE * CELL_SIZE);
const newScale = Math.min(window.innerWidth / (GRID_SIZE * CELL_SIZE), window.innerHeight / (GRID_SIZE * CELL_SIZE));
const ratio = newScale / oldScale;
marble.x *= ratio;
marble.y *= ratio;
marble.radius *= ratio;
vortex.x *= ratio;
vortex.y *= ratio;
vortex.radius *= ratio;
mazeWalls.forEach(wall => {
wall.x *= ratio;
wall.y *= ratio;
wall.w *= ratio;
wall.h *= ratio;
});
trapdoors.forEach(td => {
td.x *= ratio;
td.y *= ratio;
td.w *= ratio;
td.h *= ratio;
});
resizeCanvas();
});
lastTime = performance.now();
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