讨论

重力流变:基于物理的飞行躲避游戏

来自 Wikiprompt,自由的提示词百科全书

loshu2000

2026年5月17日

重力流变:基于物理的飞行躲避游戏 一个用于生成单文件HTML游戏的详细提示,该游戏包含基于物理的玩法、程序化贝塞尔曲线障碍物、Web Audio合成,以及禅意柔和色调的美学风格。

提示词内容收藏

🌐
<!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 flight</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #1e1e2f; min-height: 100vh; display: flex; justify-content: center; align-items: center; font-family: 'Segoe UI', system-ui, sans-serif; } .canvas-wrapper { background: #2a2a3b; border-radius: 2rem; padding: 0.6rem; box-shadow: 0 20px 40px rgba(0,0,0,0.6); } canvas { display: block; width: 100%; max-width: 900px; height: auto; border-radius: 1.5rem; background: #f2ede4; /* pastel paper */ box-shadow: inset 0 0 30px rgba(0,0,0,0.05); cursor: none; } .info { display: flex; justify-content: space-between; align-items: center; margin-top: 0.8rem; color: #b9b3c7; font-weight: 300; letter-spacing: 1px; padding: 0 0.5rem; } .info span { background: #2e2e42; padding: 0.3rem 1rem; border-radius: 30px; font-size: 0.8rem; box-shadow: 0 2px 6px rgba(0,0,0,0.3); } .status { color: #cdc6e0; } .reset-btn { background: none; border: 1px solid #5d5b70; color: #b9b3c7; padding: 0.3rem 1.2rem; border-radius: 30px; font-size: 0.8rem; cursor: pointer; transition: 0.2s; } .reset-btn:hover { background: #3a3a52; border-color: #8f8aa8; color: white; } </style> </head> <body> <div style="display: flex; flex-direction: column; width: min(900px, 95vw);"> <div class="canvas-wrapper"> <canvas id="gameCanvas" width="900" height="600"></canvas> </div> <div class="info"> <span>🌫️ GRAVITY FLUX</span> <span class="status" id="statusDisplay">◌ drift</span> <button class="reset-btn" id="resetButton">↻ reset</button> </div> </div> <script> (function(){ // ---------- CANVAS & CONTEXT ---------- const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const statusEl = document.getElementById('statusDisplay'); // ---------- FIXED DIMENSIONS ---------- const W = 900, H = 600; // ---------- GLOBAL STATE ---------- let time = 0; // global time for sine morph let gameActive = true; let score = 0; let gatePassCooldown = 0; // frames to avoid double count // ---------- SOFT-BODY SPHERE (player) ---------- const player = { x: 180, y: 300, vx: 0, vy: 0, radius: 18, // base radius squash: 0.0, // dynamic squash factor (visual) phase: 0.0 }; // ---------- GATE / OBSTACLE SYSTEM ---------- // we use a set of vertical "gates" (upper and lower bezier walls) // each gate has a base gap center and gap size, morphing over time const GATE_COUNT = 5; const GATE_SPACING = 200; // x distance between gates const FIRST_GATE_X = 500; // first gate appears ahead // each gate: { baseY, gapHalfRange, gapSize, speed, phaseOffset } let gates = []; function initGates() { gates = []; for (let i = 0; i < GATE_COUNT; i++) { const baseY = 200 + Math.sin(i * 1.7) * 100 + 100; // varied heights gates.push({ baseY: baseY, gapHalfRange: 60 + 20 * Math.sin(i * 2.3), // vertical drift range gapSize: 70 + 25 * Math.sin(i * 1.2), // base gap size speed: 0.6 + 0.3 * Math.cos(i * 1.1), phaseOffset: i * 1.8, }); } } initGates(); // ---------- RESET FUNCTION ---------- function resetGame() { player.x = 180; player.y = 300; player.vx = 0; player.vy = 0; player.squash = 0; time = 0; score = 0; gameActive = true; gatePassCooldown = 0; initGates(); statusEl.innerText = '◌ drift'; } // ---------- AUDIO (Web Audio API) ---------- let audioCtx = null; let ambientGain = null; let ambientOsc = null; let sweepGain = null; function initAudio() { if (audioCtx) return; // already created try { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } catch(e) { return; } // ambient pulse: low sine + slow LFO ambientGain = audioCtx.createGain(); ambientGain.gain.value = 0.04; ambientGain.connect(audioCtx.destination); ambientOsc = audioCtx.createOscillator(); ambientOsc.type = 'sine'; ambientOsc.frequency.value = 55; // low drone const lfo = audioCtx.createOscillator(); lfo.type = 'sine'; lfo.frequency.value = 0.3; const lfoGain = audioCtx.createGain(); lfoGain.gain.value = 12; lfo.connect(lfoGain); lfoGain.connect(ambientOsc.frequency); lfo.start(); ambientOsc.connect(ambientGain); ambientOsc.start(); // sweep gain (for gate pass) sweepGain = audioCtx.createGain(); sweepGain.gain.value = 0; sweepGain.connect(audioCtx.destination); } // reactive acoustic sweep (called on gate pass) function playSweep() { if (!audioCtx || !sweepGain) return; const now = audioCtx.currentTime; const osc = audioCtx.createOscillator(); osc.type = 'sine'; osc.frequency.setValueAtTime(180, now); osc.frequency.exponentialRampToValueAtTime(720, now + 0.25); const gain = audioCtx.createGain(); gain.gain.setValueAtTime(0.12, now); gain.gain.exponentialRampToValueAtTime(0.001, now + 0.4); osc.connect(gain); gain.connect(sweepGain); osc.start(now); osc.stop(now + 0.4); } // ---------- HELPER: BEZIER POINT (cubic) ---------- function cubicBezier(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; } // ---------- GATE GEOMETRY (MORPHING) ---------- // returns upper and lower boundary y at given x (for a gate index) // uses two cubic beziers: upper wall and lower wall function getGateBoundaries(gate, gateX, time) { const t = time * 0.5 + gate.phaseOffset; // vertical oscillation of the whole gate const driftY = Math.sin(t * 0.9) * 25; const centerY = gate.baseY + driftY; // gap size morphs with sine const gapHalf = gate.gapSize * 0.5 + Math.sin(t * 1.3) * 12; // control points for upper/lower walls (smooth curves) // we create a gentle S‑curve using control points offset const x0 = gateX - 45; const x1 = gateX - 15; const x2 = gateX + 15; const x3 = gateX + 45; // upper wall: from left to right, y goes slightly up/down const upperBase = centerY - gapHalf; const lowerBase = centerY + gapHalf; // control points for upper wall (add curvature) const cp1u = upperBase - 18 + Math.sin(t * 1.1) * 10; const cp2u = upperBase + 12 + Math.cos(t * 0.7) * 8; const cp1l = lowerBase + 15 + Math.cos(t * 0.8) * 10; const cp2l = lowerBase - 10 + Math.sin(t * 1.2) * 8; // return boundary functions (y at given x) return { upperY: (x) => { if (x <= x0) return upperBase; if (x >= x3) return upperBase; const tt = (x - x0) / (x3 - x0); return cubicBezier(upperBase, cp1u, cp2u, upperBase, tt); }, lowerY: (x) => { if (x <= x0) return lowerBase; if (x >= x3) return lowerBase; const tt = (x - x0) / (x3 - x0); return cubicBezier(lowerBase, cp1l, cp2l, lowerBase, tt); }, x0, x3 }; } // ---------- COLLISION DETECTION (sphere vs gate) ---------- function checkCollision() { // player sphere vs each gate for (let i = 0; i < gates.length; i++) { const gateX = FIRST_GATE_X + i * GATE_SPACING; // only check if player is near gate horizontally if (Math.abs(player.x - gateX) < 50 + player.radius) { const gate = gates[i]; const bounds = getGateBoundaries(gate, gateX, time); // sample at player.x const upper = bounds.upperY(player.x); const lower = bounds.lowerY(player.x); // sphere collision: if player.y - radius < upper OR player.y + radius > lower if (player.y - player.radius < upper || player.y + player.radius > lower) { return true; // collision } } } return false; } // ---------- GATE PASS DETECTION (score) ---------- function checkGatePass() { if (gatePassCooldown > 0) return; for (let i = 0; i < gates.length; i++) { const gateX = FIRST_GATE_X + i * GATE_SPACING; // if player just crossed the gate center (within a small window) if (player.x > gateX - 6 && player.x < gateX + 6) { // check if inside gap (no collision) const gate = gates[i]; const bounds = getGateBoundaries(gate, gateX, time); const upper = bounds.upperY(player.x); const lower = bounds.lowerY(player.x); if (player.y - player.radius > upper && player.y + player.radius < lower) { // passed through! score++; gatePassCooldown = 20; // frames playSweep(); statusEl.innerText = `✦ ${score}`; break; } } } } // ---------- PHYSICS & UPDATE ---------- function update() { if (!gameActive) return; // simple gravity + damping player.vy += 0.12; // gravity player.vx *= 0.98; player.vy *= 0.98; // mouse control (target velocity based on pointer) // we use mouse position to steer (stored in mouse) const targetX = mouse.x; const targetY = mouse.y; const dx = targetX - player.x; const dy = targetY - player.y; // gentle spring force player.vx += dx * 0.002; player.vy += dy * 0.002; // clamp velocity const maxSpeed = 4.5; const sp = Math.hypot(player.vx, player.vy); if (sp > maxSpeed) { player.vx = (player.vx / sp) * maxSpeed; player.vy = (player.vy / sp) * maxSpeed; } // integrate player.x += player.vx; player.y += player.vy; // soft boundaries (canvas) if (player.x < 20) { player.x = 20; player.vx *= -0.3; } if (player.x > W-20) { player.x = W-20; player.vx *= -0.3; } if (player.y < 20) { player.y = 20; player.vy *= -0.3; } if (player.y > H-20) { player.y = H-20; player.vy *= -0.3; } // squash & stretch based on vertical velocity player.squash = Math.max(-0.15, Math.min(0.15, player.vy * 0.01)); player.phase += 0.05; // collision detection if (checkCollision()) { gameActive = false; statusEl.innerText = '✖ impact'; // soft reset after short delay? we keep frozen } // gate pass scoring if (gameActive) { checkGatePass(); if (gatePassCooldown > 0) gatePassCooldown--; } // global time advance time += 0.02; } // ---------- RENDERING ---------- function draw() { ctx.clearRect(0, 0, W, H); // pastel background ctx.fillStyle = '#f2ede4'; ctx.fillRect(0, 0, W, H); // soft shadow layer for depth ctx.shadowColor = 'rgba(80, 70, 90, 0.25)'; ctx.shadowBlur = 24; ctx.shadowOffsetY = 6; // draw all gates (organic bezier walls) for (let i = 0; i < gates.length; i++) { const gateX = FIRST_GATE_X + i * GATE_SPACING; const gate = gates[i]; const bounds = getGateBoundaries(gate, gateX, time); // draw upper wall (filled shape) ctx.beginPath(); ctx.moveTo(bounds.x0 - 20, -10); ctx.lineTo(bounds.x0 - 20, bounds.upperY(bounds.x0 - 20)); // sample bezier const steps = 20; for (let s = 0; s <= steps; s++) { const x = bounds.x0 + (bounds.x3 - bounds.x0) * (s / steps); const y = bounds.upperY(x); ctx.lineTo(x, y); } ctx.lineTo(bounds.x3 + 20, bounds.upperY(bounds.x3 + 20)); ctx.lineTo(bounds.x3 + 20, -10); ctx.closePath(); ctx.fillStyle = '#d9d2c0'; ctx.fill(); // lower wall ctx.beginPath(); ctx.moveTo(bounds.x0 - 20, H+10); ctx.lineTo(bounds.x0 - 20, bounds.lowerY(bounds.x0 - 20)); for (let s = 0; s <= steps; s++) { const x = bounds.x0 + (bounds.x3 - bounds.x0) * (s / steps); const y = bounds.lowerY(x); ctx.lineTo(x, y); } ctx.lineTo(bounds.x3 + 20, bounds.lowerY(bounds.x3 + 20)); ctx.lineTo(bounds.x3 + 20, H+10); ctx.closePath(); ctx.fillStyle = '#d9d2c0'; ctx.fill(); // subtle inner glow lines ctx.shadowBlur = 12; ctx.strokeStyle = '#b8af9a'; ctx.lineWidth = 1.5; ctx.beginPath(); for (let s = 0; s <= steps; s++) { const x = bounds.x0 + (bounds.x3 - bounds.x0) * (s / steps); const y = bounds.upperY(x); if (s === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.stroke(); ctx.beginPath(); for (let s = 0; s <= steps; s++) { const x = bounds.x0 + (bounds.x3 - bounds.x0) * (s / steps); const y = bounds.lowerY(x); if (s === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.stroke(); } // reset shadow for player ctx.shadowBlur = 30; ctx.shadowColor = 'rgba(120, 100, 130, 0.3)'; // draw soft-body sphere (self-inflating effect) const rad = player.radius * (1 + 0.1 * Math.sin(time * 2.5)); const squashX = 1 + player.squash; const squashY = 1 - player.squash; ctx.save(); ctx.translate(player.x, player.y); ctx.scale(squashX, squashY); // gradient for soft volume const grad = ctx.createRadialGradient(-4, -4, 3, 0, 0, rad+6); grad.addColorStop(0, '#fff9f0'); grad.addColorStop(0.7, '#e3d7c5'); grad.addColorStop(1, '#b7aa99'); ctx.fillStyle = grad; ctx.beginPath(); ctx.arc(0, 0, rad, 0, Math.PI*2); ctx.fill(); // highlight ctx.shadowBlur = 8; ctx.beginPath(); ctx.arc(-3, -3, rad*0.3, 0, Math.PI*2); ctx.fillStyle = 'rgba(255,255,240,0.6)'; ctx.fill(); ctx.restore(); // reset shadow ctx.shadowBlur = 0; ctx.shadowColor = 'transparent'; // draw gate markers (subtle) ctx.font = '12px monospace'; ctx.fillStyle = '#8f8aa8'; ctx.fillText('GRAVITY FLUX', 30, 40); } // ---------- MOUSE TRACKING ---------- const mouse = { x: W/2, y: H/2 }; function onMouseMove(e) { const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; mouse.x = (e.clientX - rect.left) * scaleX; mouse.y = (e.clientY - rect.top) * scaleY; } // ---------- ANIMATION LOOP ---------- function animate() { update(); draw(); requestAnimationFrame(animate); } // ---------- EVENT LISTENERS ---------- canvas.addEventListener('mousemove', onMouseMove); canvas.addEventListener('mouseenter', () => { initAudio(); // start audio on first interaction }); document.getElementById('resetButton').addEventListener('click', () => { resetGame(); initAudio(); // ensure audio context }); // initial audio init on first click/touch canvas.addEventListener('click', function initOnce() { initAudio(); }, { once: true }); // start animation resetGame(); // ensure clean start animate(); // also init audio on first keydown (just in case) window.addEventListener('keydown', () => initAudio(), { once: true }); })(); </script> </body> </html>

登录以查看完整提示词

继续使用:

登录即表示你同意我们的 使用条款 和 隐私政策

用法

此提示词专为 coding 设计。复制上方内容并粘贴到你常用的 AI 工具中。

为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。

参考资料

分类:coding| prompts.chat| game-development| physics-engine

讨论