Discusión

Deriva Axial: Juego de Laberinto Físico Adaptable a Móviles

De Wikiprompt, la enciclopedia libre de prompts

loshu2000
Contribuido porloshu2000Fuente

17 may 2026

Deriva Axial: Juego de Laberinto Físico Adaptable a Móviles Un prompt completo para generar un juego de laberinto HTML5 de un solo archivo con mecánicas de cambio de gravedad, controles de inclinación móvil y estilo visual Bauhaus.

Contenido del PromptGuardar

🌐
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <title>Axial Drift</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } html, body { width: 100%; height: 100%; overflow: hidden; background: #1a1a1a; font-family: 'Helvetica Neue', Arial, sans-serif; touch-action: none; -webkit-user-select: none; user-select: none; } #gameCanvas { display: block; width: 100%; height: 100%; background: #f0f0f0; } #hud { position: fixed; top: 12px; left: 12px; color: #fff; background: rgba(0,0,0,0.6); padding: 8px 14px; font-size: 14px; letter-spacing: 1px; border-left: 4px solid #ff3b30; pointer-events: none; z-index: 10; text-transform: uppercase; } #winOverlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.75); display: none; align-items: center; justify-content: center; z-index: 20; color: #fff; text-align: center; } #winOverlay.show { display: flex; } #winBox { background: #2b2b2b; border: 4px solid #ffcc00; padding: 30px 50px; box-shadow: 0 0 40px rgba(255,204,0,0.4); } #winBox h1 { font-size: 32px; margin-bottom: 10px; color: #ffcc00; letter-spacing: 3px; } #winBox p { margin: 8px 0; font-size: 16px; } #restartBtn { margin-top: 20px; padding: 12px 30px; font-size: 18px; background: #ffcc00; color: #1a1a1a; border: none; cursor: pointer; font-weight: bold; letter-spacing: 1px; border-radius: 2px; } #restartBtn:hover { background: #ffdd44; } #gyroHint { position: fixed; bottom: 12px; left: 50%; transform: translateX(-50%); color: #aaa; font-size: 12px; background: rgba(0,0,0,0.5); padding: 4px 12px; border-radius: 12px; pointer-events: none; z-index: 10; } </style> </head> <body> <div id="hud">Axial Drift</div> <div id="gyroHint">Drag mouse / tilt device</div> <div id="winOverlay"> <div id="winBox"> <h1>VORTEX REACHED</h1> <p>Marble stabilized in the axial core.</p> <button id="restartBtn">RUN AGAIN</button> </div> </div> <canvas id="gameCanvas"></canvas> <script> (function(){ const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const winOverlay = document.getElementById('winOverlay'); const restartBtn = document.getElementById('restartBtn'); // ---------- VIEWPORT / DPR ---------- let W, H, DPR; function resize() { DPR = Math.min(window.devicePixelRatio || 1, 2); W = window.innerWidth; H = window.innerHeight; 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); } window.addEventListener('resize', resize); resize(); // ---------- GAME STATE ---------- const state = { gravity: { x: 0, y: 0.2 }, // normalized -1..1 targetGravity: { x: 0, y: 0.2 }, marble: { x: 0, y: 0, vx: 0, vy: 0, radius: 12 }, maze: null, win: false, time: 0, trapdoors: [], vortex: { x: 0, y: 0, radius: 22 } }; // ---------- MAZE DEFINITION (Bauhaus style) ---------- // Walls as line segments: [x1,y1,x2,y2] // Coordinates in normalized space (0..1) relative to canvas function buildMaze() { const w = 0.06; // wall thickness factor return { walls: [ // Outer boundary [0.05, 0.05, 0.95, 0.05], [0.95, 0.05, 0.95, 0.95], [0.95, 0.95, 0.05, 0.95], [0.05, 0.95, 0.05, 0.05], // Inner structures [0.25, 0.05, 0.25, 0.35], [0.25, 0.35, 0.55, 0.35], [0.55, 0.35, 0.55, 0.65], [0.55, 0.65, 0.25, 0.65], [0.25, 0.65, 0.25, 0.95], [0.75, 0.05, 0.75, 0.25], [0.75, 0.25, 0.45, 0.25], [0.45, 0.25, 0.45, 0.55], [0.45, 0.55, 0.75, 0.55], [0.75, 0.55, 0.75, 0.95], // Diagonal accent [0.05, 0.75, 0.35, 0.45], [0.35, 0.45, 0.65, 0.75], [0.65, 0.75, 0.95, 0.45] ], // Trapdoors: dynamic segments that open/close trapdoors: [ { x1: 0.25, y1: 0.35, x2: 0.55, y2: 0.35, phase: 0, speed: 0.8, active: true }, { x1: 0.55, y1: 0.65, x2: 0.25, y2: 0.65, phase: 2, speed: 0.6, active: true }, { x1: 0.45, y1: 0.25, x2: 0.45, y2: 0.55, phase: 4, speed: 0.7, active: true } ], // Vortex position (center) vortex: { x: 0.5, y: 0.5 } }; } // ---------- INITIALIZE ---------- function initGame() { state.maze = buildMaze(); state.marble.x = 0.12; state.marble.y = 0.12; state.marble.vx = 0; state.marble.vy = 0; state.gravity = { x: 0, y: 0.2 }; state.targetGravity = { x: 0, y: 0.2 }; state.win = false; state.time = 0; winOverlay.classList.remove('show'); // Reset trapdoor phases state.maze.trapdoors.forEach(t => { t.active = true; t.phase = Math.random() * 6; }); } // ---------- INPUT ---------- let dragging = false; let lastMouse = { x: 0, y: 0 }; function handleDragStart(e) { if (state.win) return; dragging = true; const pos = getEventPos(e); lastMouse = pos; } function handleDragMove(e) { if (!dragging || state.win) return; const pos = getEventPos(e); const dx = pos.x - lastMouse.x; const dy = pos.y - lastMouse.y; // Map drag to gravity shift (inverted: drag down -> gravity up) state.targetGravity.x = Math.max(-1, Math.min(1, state.targetGravity.x + dx * 0.002)); state.targetGravity.y = Math.max(-1, Math.min(1, state.targetGravity.y + dy * 0.002)); lastMouse = pos; } function handleDragEnd() { dragging = false; } function getEventPos(e) { if (e.touches && e.touches.length > 0) { return { x: e.touches[0].clientX, y: e.touches[0].clientY }; } return { x: e.clientX, y: e.clientY }; } // Mouse canvas.addEventListener('mousedown', handleDragStart); window.addEventListener('mousemove', handleDragMove); window.addEventListener('mouseup', handleDragEnd); // Touch canvas.addEventListener('touchstart', (e) => { e.preventDefault(); handleDragStart(e); }, { passive: false }); window.addEventListener('touchmove', (e) => { e.preventDefault(); handleDragMove(e); }, { passive: false }); window.addEventListener('touchend', handleDragEnd); // Device orientation function handleOrientation(e) { if (state.win) return; // gamma: left-right tilt (-90..90), beta: front-back (-180..180) const gamma = e.gamma || 0; const beta = e.beta || 0; // Map to gravity: tilt right -> gravity right, tilt forward -> gravity down state.targetGravity.x = Math.max(-1, Math.min(1, gamma / 45)); state.targetGravity.y = Math.max(-1, Math.min(1, beta / 45)); } window.addEventListener('deviceorientation', handleOrientation); // ---------- PHYSICS ---------- const FRICTION = 0.98; const DAMPING = 0.995; const MAX_SPEED = 0.02; const GRAVITY_STRENGTH = 0.0015; function updatePhysics(dt) { if (state.win) return; // Smooth gravity transition state.gravity.x += (state.targetGravity.x - state.gravity.x) * 0.1; state.gravity.y += (state.targetGravity.y - state.gravity.y) * 0.1; // Apply gravity state.marble.vx += state.gravity.x * GRAVITY_STRENGTH * dt * 60; state.marble.vy += state.gravity.y * GRAVITY_STRENGTH * dt * 60; // Friction and damping state.marble.vx *= FRICTION; state.marble.vy *= FRICTION; state.marble.vx *= DAMPING; state.marble.vy *= DAMPING; // Clamp speed const speed = Math.hypot(state.marble.vx, state.marble.vy); if (speed > MAX_SPEED) { state.marble.vx = (state.marble.vx / speed) * MAX_SPEED; state.marble.vy = (state.marble.vy / speed) * MAX_SPEED; } // Move marble state.marble.x += state.marble.vx * dt * 60; state.marble.y += state.marble.vy * dt * 60; // Collision with walls const m = state.marble; const radius = m.radius / Math.min(W, H); // normalized radius const walls = state.maze.walls; for (let i = 0; i < walls.length; i++) { const w = walls[i]; const x1 = w[0], y1 = w[1], x2 = w[2], y2 = w[3]; // Closest point on segment to marble const dx = x2 - x1; const dy = y2 - y1; const len2 = dx*dx + dy*dy; let t = ((m.x - x1)*dx + (m.y - y1)*dy) / len2; t = Math.max(0, Math.min(1, t)); const closestX = x1 + t * dx; const closestY = y1 + t * dy; const distX = m.x - closestX; const distY = m.y - closestY; const dist = Math.hypot(distX, distY); if (dist < radius) { // Push out const overlap = radius - dist; const nx = dist > 0.0001 ? distX / dist : 0; const ny = dist > 0.0001 ? distY / dist : 0; m.x += nx * overlap; m.y += ny * overlap; // Reflect velocity (with damping) const dot = m.vx * nx + m.vy * ny; if (dot < 0) { m.vx -= 2 * dot * nx; m.vy -= 2 * dot * ny; m.vx *= 0.8; m.vy *= 0.8; } } } // Trapdoor collisions (only if active) state.maze.trapdoors.forEach(t => { if (!t.active) return; const x1 = t.x1, y1 = t.y1, x2 = t.x2, y2 = t.y2; const dx = x2 - x1; const dy = y2 - y1; const len2 = dx*dx + dy*dy; let tt = ((m.x - x1)*dx + (m.y - y1)*dy) / len2; tt = Math.max(0, Math.min(1, tt)); const closestX = x1 + tt * dx; const closestY = y1 + tt * dy; const distX = m.x - closestX; const distY = m.y - closestY; const dist = Math.hypot(distX, distY); if (dist < radius) { const overlap = radius - dist; const nx = dist > 0.0001 ? distX / dist : 0; const ny = dist > 0.0001 ? distY / dist : 0; m.x += nx * overlap; m.y += ny * overlap; const dot = m.vx * nx + m.vy * ny; if (dot < 0) { m.vx -= 2 * dot * nx; m.vy -= 2 * dot * ny; m.vx *= 0.7; m.vy *= 0.7; } } }); // Boundary (safety) m.x = Math.max(0.05, Math.min(0.95, m.x)); m.y = Math.max(0.05, Math.min(0.95, m.y)); // Vortex check const vx = state.maze.vortex.x; const vy = state.maze.vortex.y; const distToVortex = Math.hypot(m.x - vx, m.y - vy); if (distToVortex < state.vortex.radius / Math.min(W, H)) { state.win = true; winOverlay.classList.add('show'); } } // ---------- RENDERING ---------- function draw() { ctx.clearRect(0, 0, W, H); // Background ctx.fillStyle = '#f0f0f0'; ctx.fillRect(0, 0, W, H); // Bauhaus grid pattern (subtle) ctx.strokeStyle = 'rgba(0,0,0,0.05)'; ctx.lineWidth = 1; const gridSize = 40; for (let x = 0; x < W; x += gridSize) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke(); } for (let y = 0; y < H; y += gridSize) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke(); } // Draw walls (Bauhaus style: bold black lines with colored blocks) const walls = state.maze.walls; ctx.lineWidth = 6; ctx.strokeStyle = '#1a1a1a'; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; // Shadow pass ctx.shadowColor = 'rgba(0,0,0,0.3)'; ctx.shadowBlur = 8; ctx.shadowOffsetX = 3; ctx.shadowOffsetY = 3; // Draw wall segments for (let i = 0; i < walls.length; i++) { const w = walls[i]; ctx.beginPath(); ctx.moveTo(w[0] * W, w[1] * H); ctx.lineTo(w[2] * W, w[3] * H); ctx.stroke(); } // Draw colored blocks (Bauhaus primary colors) at intersections ctx.shadowBlur = 0; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; const colors = ['#ff3b30', '#ffcc00', '#007aff']; const blockPoints = [ [0.25, 0.35], [0.55, 0.35], [0.55, 0.65], [0.25, 0.65], [0.75, 0.25], [0.45, 0.25], [0.45, 0.55], [0.75, 0.55], [0.35, 0.45], [0.65, 0.75] ]; for (let i = 0; i < blockPoints.length; i++) { const p = blockPoints[i]; ctx.fillStyle = colors[i % colors.length]; ctx.fillRect(p[0] * W - 10, p[1] * H - 10, 20, 20); } // Draw trapdoors state.maze.trapdoors.forEach(t => { if (!t.active) return; ctx.strokeStyle = '#ff3b30'; ctx.lineWidth = 5; ctx.shadowColor = 'rgba(255,59,48,0.5)'; ctx.shadowBlur = 10; ctx.beginPath(); ctx.moveTo(t.x1 * W, t.y1 * H); ctx.lineTo(t.x2 * W, t.y2 * H); ctx.stroke(); }); // Reset shadow ctx.shadowBlur = 0; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; // Draw vortex const vx = state.maze.vortex.x * W; const vy = state.maze.vortex.y * H; const vr = state.vortex.radius; // Outer ring ctx.strokeStyle = '#007aff'; ctx.lineWidth = 4; ctx.beginPath(); ctx.arc(vx, vy, vr, 0, Math.PI * 2); ctx.stroke(); // Inner swirl ctx.fillStyle = '#007aff'; ctx.beginPath(); ctx.arc(vx, vy, vr * 0.4, 0, Math.PI * 2); ctx.fill(); // Animated rotation lines ctx.strokeStyle = 'rgba(0,122,255,0.6)'; ctx.lineWidth = 2; for (let i = 0; i < 3; i++) { const angle = state.time * 0.002 + (i * Math.PI * 2 / 3); ctx.beginPath(); ctx.moveTo(vx + Math.cos(angle) * vr * 0.5, vy + Math.sin(angle) * vr * 0.5); ctx.lineTo(vx + Math.cos(angle) * vr, vy + Math.sin(angle) * vr); ctx.stroke(); } // Draw marble const mx = state.marble.x * W; const my = state.marble.y * H; const mr = state.marble.radius; // Shadow ctx.shadowColor = 'rgba(0,0,0,0.4)'; ctx.shadowBlur = 12; ctx.shadowOffsetX = 4; ctx.shadowOffsetY = 4; // Marble body const gradient = ctx.createRadialGradient(mx - mr*0.3, my - mr*0.3, mr*0.2, mx, my, mr); gradient.addColorStop(0, '#ffffff'); gradient.addColorStop(0.5, '#cccccc'); gradient.addColorStop(1, '#666666'); ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(mx, my, mr, 0, Math.PI * 2); ctx.fill(); // Marble outline ctx.shadowBlur = 0; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; ctx.strokeStyle = '#1a1a1a'; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(mx, my, mr, 0, Math.PI * 2); ctx.stroke(); // Highlight ctx.fillStyle = 'rgba(255,255,255,0.6)'; ctx.beginPath(); ctx.arc(mx - mr*0.25, my - mr*0.25, mr*0.2, 0, Math.PI * 2); ctx.fill(); } // ---------- GAME LOOP ---------- let lastTime = performance.now(); function gameLoop(now) { const dt = Math.min((now - lastTime) / 1000, 0.05); lastTime = now; state.time += dt * 1000; // Update trapdoors state.maze.trapdoors.forEach(t => { t.phase += dt * t.speed; t.active = Math.sin(t.phase) > -0.2; }); updatePhysics(dt); draw(); requestAnimationFrame(gameLoop); } // ---------- RESTART ---------- restartBtn.addEventListener('click', () => { initGame(); }); // ---------- START ---------- initGame(); requestAnimationFrame(gameLoop); })(); </script> </body> </html>

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ías:coding| prompts.chat| game-development| html5

Discusión