Diskussion

Gravity Flux: Physikbasiertes Flugausweichspiel

Von Wikiprompt, der freien Prompt-Enzyklopädie

loshu2000
Beigetragen vonloshu2000Quelle

17. Mai 2026

Gravity Flux: Physikbasiertes Flugausweichspiel Ein detaillierter Prompt zur Erzeugung eines Ein-Datei-HTML-Spiels mit physikbasiertem Gameplay, prozeduralen Bezier-Hindernissen, Web-Audio-Synthese und einer zen-artigen Pastellästhetik.

Prompt-InhaltSpeichern

🌐
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Gravity Flux · soft‑body evasion</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #1e1e2f; min-height: 100vh; display: flex; align-items: center; justify-content: center; font-family: 'Segoe UI', system-ui, sans-serif; } .canvas-wrapper { background: #2b2b3c; padding: 12px; border-radius: 32px; box-shadow: 0 20px 40px rgba(0,0,0,0.6); } canvas { display: block; width: 800px; height: 600px; border-radius: 24px; box-shadow: inset 0 0 30px rgba(0,0,0,0.5); background: #f2efe9; /* soft pastel base */ } .info { display: flex; justify-content: space-between; margin-top: 14px; color: #b9b3c7; font-weight: 400; letter-spacing: 0.5px; padding: 0 8px; } .info span { background: #2e2e42; padding: 6px 16px; border-radius: 30px; font-size: 0.9rem; box-shadow: 0 4px 8px rgba(0,0,0,0.3); } .accent { color: #d8d0e8; } </style> </head> <body> <div> <div class="canvas-wrapper"> <canvas id="gameCanvas" width="800" height="600"></canvas> <div class="info"> <span>🌫️ <span class="accent">Gravity Flux</span></span> <span id="gateCount">gates: 0</span> <span>⬆️⬇️ / W·S</span> </div> </div> </div> <script> (function() { // ---------- canvas & context ---------- const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const W = 800, H = 600; // ---------- audio (Web Audio API) ---------- let audioCtx = null; let masterGain = null; let pulseOsc = null; let pulseGain = null; let sweepOsc = null; let sweepGain = null; function initAudio() { if (audioCtx) return; try { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); masterGain = audioCtx.createGain(); masterGain.gain.value = 0.18; masterGain.connect(audioCtx.destination); // ambient pulse: low sine, slow LFO pulseOsc = audioCtx.createOscillator(); pulseOsc.type = 'sine'; pulseOsc.frequency.value = 55; // deep hum pulseGain = audioCtx.createGain(); pulseGain.gain.value = 0.5; const lfo = audioCtx.createOscillator(); lfo.frequency.value = 0.2; const lfoGain = audioCtx.createGain(); lfoGain.gain.value = 0.3; lfo.connect(lfoGain); lfoGain.connect(pulseGain.gain); pulseOsc.connect(pulseGain); pulseGain.connect(masterGain); pulseOsc.start(); lfo.start(); // sweep oscillator (reactive, triggered on gate pass) sweepOsc = audioCtx.createOscillator(); sweepOsc.type = 'sine'; sweepOsc.frequency.value = 180; sweepGain = audioCtx.createGain(); sweepGain.gain.value = 0.0; sweepOsc.connect(sweepGain); sweepGain.connect(masterGain); sweepOsc.start(); } catch (e) { // audio not available audioCtx = null; } } // reactive sweep: short glissando function playSweep() { if (!audioCtx || !sweepOsc) return; const now = audioCtx.currentTime; sweepGain.gain.cancelScheduledValues(now); sweepGain.gain.setValueAtTime(0.25, now); sweepGain.gain.exponentialRampToValueAtTime(0.001, now + 0.4); sweepOsc.frequency.setValueAtTime(220, now); sweepOsc.frequency.exponentialRampToValueAtTime(720, now + 0.25); } // ---------- soft‑body sphere (self‑inflating) ---------- const ball = { x: 200, y: 300, baseRadius: 16, radius: 16, vy: 0, vx: 0, squash: 0.0, // visual squash factor phase: 0.0 }; // ---------- game state ---------- let gates = []; let gateCount = 0; let time = 0; let gameOver = false; let score = 0; // ---------- procedural gate generation ---------- const GATE_SPACING = 260; // horizontal distance const GATE_WIDTH = 130; // base width const GATE_HEIGHT = 180; // vertical span function generateGate(xPos, seedOffset) { // each gate is a pair of bezier curves (upper and lower) // they morph via sine functions const baseY = H * 0.5 + Math.sin(seedOffset * 1.3) * 60; return { x: xPos, baseY: baseY, seed: seedOffset, phaseOffset: Math.random() * Math.PI * 2, width: GATE_WIDTH, height: GATE_HEIGHT, passed: false }; } // initial gates for (let i = 0; i < 6; i++) { gates.push(generateGate(300 + i * GATE_SPACING, i * 0.9 + 0.2)); } // ---------- helper: get gate top/bottom curves at time t ---------- function getGateCurves(gate, t) { const x = gate.x; const baseY = gate.baseY; const w = gate.width; const h = gate.height; const seed = gate.seed; const ph = gate.phaseOffset; // dynamic morph: sine waves shift control points const wave1 = Math.sin(t * 0.8 + ph) * 22; const wave2 = Math.cos(t * 0.6 + ph * 1.7) * 18; const wave3 = Math.sin(t * 1.2 + ph * 0.3) * 14; // upper curve (ceiling) – from left to right const topY = baseY - h/2 + wave1 * 0.6; const bottomY = baseY + h/2 + wave2 * 0.5; // control points for upper bezier (cubic) const cp1x = x - w * 0.2; const cp1y = topY - 20 + wave3 * 0.8; const cp2x = x + w * 0.2; const cp2y = topY + 15 + wave2 * 0.7; // lower bezier (floor) const cp3x = x - w * 0.25; const cp3y = bottomY + 18 + wave1 * 0.9; const cp4x = x + w * 0.25; const cp4y = bottomY - 12 + wave3 * 0.6; return { top: { start: {x: x - w/2, y: topY}, cp1: {x: cp1x, y: cp1y}, cp2: {x: cp2x, y: cp2y}, end: {x: x + w/2, y: topY} }, bottom: { start: {x: x - w/2, y: bottomY}, cp1: {x: cp3x, y: cp3y}, cp2: {x: cp4x, y: cp4y}, end: {x: x + w/2, y: bottomY} } }; } // ---------- collision detection (point vs bezier approx) ---------- function pointInGate(px, py, gate, t) { const curves = getGateCurves(gate, t); const top = curves.top; const bottom = curves.bottom; // sample bezier at 8 points for each curve const steps = 10; let minTop = Infinity; let maxBottom = -Infinity; for (let i = 0; i <= steps; i++) { const u = i / steps; // cubic bezier formula const topY = bezierPoint(top.start.y, top.cp1.y, top.cp2.y, top.end.y, u); const bottomY = bezierPoint(bottom.start.y, bottom.cp1.y, bottom.cp2.y, bottom.end.y, u); if (topY < minTop) minTop = topY; if (bottomY > maxBottom) maxBottom = bottomY; } // also check x range const xLeft = gate.x - gate.width/2; const xRight = gate.x + gate.width/2; if (px < xLeft || px > xRight) return false; // vertical check return (py > minTop && py < maxBottom); } function bezierPoint(p0, p1, p2, p3, t) { const mt = 1 - t; return mt*mt*mt*p0 + 3*mt*mt*t*p1 + 3*mt*t*t*p2 + t*t*t*p3; } // ---------- update physics & logic ---------- function update(dt) { if (gameOver) return; // soft‑body inflation: radius oscillates gently ball.phase += dt * 2.2; ball.radius = ball.baseRadius + Math.sin(ball.phase) * 2.5; // vertical control (gravity + thrust) const gravity = 0.15; const thrust = 0.30; ball.vy += gravity * dt * 60; if (keys['ArrowUp'] || keys['w'] || keys['W']) ball.vy -= thrust * dt * 60; if (keys['ArrowDown'] || keys['s'] || keys['S']) ball.vy += thrust * dt * 60; // damping ball.vy *= 0.98; ball.y += ball.vy * dt * 60; ball.x += 0.8; // auto scroll // boundaries if (ball.y < 20) { ball.y = 20; ball.vy = 0; } if (ball.y > H - 20) { ball.y = H - 20; ball.vy = 0; } // collision with gates (only if not passed yet) for (let gate of gates) { if (gate.passed) continue; // check if ball center is inside the gate opening (no contact) const inside = pointInGate(ball.x, ball.y, gate, time); // if ball is within x range and NOT inside opening -> collision const xLeft = gate.x - gate.width/2 - ball.radius; const xRight = gate.x + gate.width/2 + ball.radius; if (ball.x > xLeft && ball.x < xRight) { // check if center is outside the vertical opening (with radius margin) const curves = getGateCurves(gate, time); const topY = bezierPoint(curves.top.start.y, curves.top.cp1.y, curves.top.cp2.y, curves.top.end.y, 0.5); const bottomY = bezierPoint(curves.bottom.start.y, curves.bottom.cp1.y, curves.bottom.cp2.y, curves.bottom.end.y, 0.5); // approximate using mid point (more robust) if (ball.y - ball.radius < topY || ball.y + ball.radius > bottomY) { gameOver = true; playSweep(); // low alert return; } } // check if ball passed the gate (x beyond gate center + width) if (!gate.passed && ball.x > gate.x + gate.width/2) { gate.passed = true; gateCount++; score++; playSweep(); // success sweep } } // recycle gates: remove far left, add new right if (gates.length > 0 && gates[0].x < -200) { gates.shift(); const lastX = gates.length > 0 ? gates[gates.length-1].x : 300; const newSeed = gates.length * 0.7 + 1.3; gates.push(generateGate(lastX + GATE_SPACING, newSeed)); } // update gate x positions (move left) for (let gate of gates) { gate.x -= 0.8; // same as ball speed } // time for morphing time += dt; } // ---------- rendering ---------- function draw() { ctx.clearRect(0, 0, W, H); // soft pastel background ctx.fillStyle = '#f2efe9'; ctx.fillRect(0, 0, W, H); // deep soft shadows for depth ctx.shadowColor = 'rgba(60, 50, 70, 0.25)'; ctx.shadowBlur = 24; ctx.shadowOffsetY = 6; // draw gates (organic bezier) for (let gate of gates) { const curves = getGateCurves(gate, time); const top = curves.top; const bottom = curves.bottom; ctx.beginPath(); ctx.moveTo(top.start.x, top.start.y); ctx.bezierCurveTo(top.cp1.x, top.cp1.y, top.cp2.x, top.cp2.y, top.end.x, top.end.y); ctx.lineTo(top.end.x, top.end.y - 18); ctx.lineTo(top.start.x, top.start.y - 18); ctx.closePath(); ctx.fillStyle = '#cbc3d8'; ctx.fill(); ctx.beginPath(); ctx.moveTo(bottom.start.x, bottom.start.y); ctx.bezierCurveTo(bottom.cp1.x, bottom.cp1.y, bottom.cp2.x, bottom.cp2.y, bottom.end.x, bottom.end.y); ctx.lineTo(bottom.end.x, bottom.end.y + 18); ctx.lineTo(bottom.start.x, bottom.start.y + 18); ctx.closePath(); ctx.fillStyle = '#b8b0c4'; ctx.fill(); // subtle outline ctx.shadowBlur = 8; ctx.strokeStyle = '#a39aad'; ctx.lineWidth = 1.5; ctx.beginPath(); ctx.moveTo(top.start.x, top.start.y); ctx.bezierCurveTo(top.cp1.x, top.cp1.y, top.cp2.x, top.cp2.y, top.end.x, top.end.y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(bottom.start.x, bottom.start.y); ctx.bezierCurveTo(bottom.cp1.x, bottom.cp1.y, bottom.cp2.x, bottom.cp2.y, bottom.end.x, bottom.end.y); ctx.stroke(); } // draw soft‑body sphere (self‑inflating) ctx.shadowBlur = 30; ctx.shadowColor = 'rgba(80, 60, 80, 0.4)'; ctx.beginPath(); ctx.ellipse(ball.x, ball.y, ball.radius, ball.radius * 0.9, 0, 0, Math.PI * 2); ctx.fillStyle = '#e8e0f0'; ctx.fill(); ctx.shadowBlur = 12; ctx.strokeStyle = '#b7aec2'; ctx.lineWidth = 2; ctx.stroke(); // inner highlight ctx.shadowBlur = 0; ctx.beginPath(); ctx.ellipse(ball.x - 3, ball.y - 3, ball.radius * 0.3, ball.radius * 0.2, 0, 0, Math.PI * 2); ctx.fillStyle = '#ffffff90'; ctx.fill(); // reset shadow ctx.shadowBlur = 0; ctx.shadowOffsetY = 0; // UI text document.getElementById('gateCount').innerText = `gates: ${gateCount}`; if (gameOver) { ctx.font = 'bold 28px "Segoe UI", sans-serif'; ctx.fillStyle = '#5e4a5e'; ctx.shadowBlur = 20; ctx.shadowColor = '#b0a0b0'; ctx.fillText('contact · flux', 240, 280); ctx.shadowBlur = 0; } } // ---------- input ---------- const keys = {}; window.addEventListener('keydown', (e) => { keys[e.key] = true; if (e.key === ' ' || e.key === 'ArrowUp' || e.key === 'ArrowDown') e.preventDefault(); initAudio(); // resume audio on first interaction }); window.addEventListener('keyup', (e) => { keys[e.key] = false; }); // ---------- animation loop ---------- let lastTime = performance.now(); function loop(now) { const dt = Math.min(0.05, (now - lastTime) / 1000); lastTime = now; update(dt); draw(); requestAnimationFrame(loop); } // start requestAnimationFrame(loop); })(); </script> </body> </html>

Melde dich an, um den vollständigen Prompt zu sehen

Weiter mit:

Mit der Anmeldung akzeptierst du unsere Nutzungsbedingungen und Datenschutz

Verwendung

Dieser Prompt ist für die Verwendung mit coding gedacht. Kopiere den Inhalt oben und füge ihn in dein bevorzugtes KI-Tool ein.

Für beste Ergebnisse passe die Platzhalter (eckige Klammern oder Großbuchstaben) an deine Anforderungen an.

Referenzen

Kategorien:coding| prompts.chat| game-development| physics-engine

Diskussion