토론

회전하는 육각형 안에서 튕기는 공

Wikiprompt, 무료 프롬프트 백과사전에서

Alex Prompter

2025년 8월 15일

회전하는 육각형 안에서 튕기는 공 회전하는 육각형 내부에서 중력과 마찰을 적용하여 공이 튕기는 물리 시뮬레이션을 생성하는 프롬프트.

프롬프트 내용저장

🌐
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Bouncing ball inside spinning hexagon</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #1a1a2e; min-height: 100vh; display: flex; justify-content: center; align-items: center; font-family: 'Segoe UI', system-ui, sans-serif; } .container { background: #16213e; padding: 20px; border-radius: 24px; box-shadow: 0 20px 30px rgba(0,0,0,0.6); } canvas { display: block; width: 600px; height: 600px; border-radius: 16px; box-shadow: inset 0 0 20px rgba(0,0,0,0.5); background: #0f0f1f; cursor: grab; } .info { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; color: #a0c4ff; font-weight: 500; letter-spacing: 0.5px; } .info span { background: #1e2a4a; padding: 6px 14px; border-radius: 40px; font-size: 0.9rem; box-shadow: 0 4px 6px rgba(0,0,0,0.3); } .info button { background: #3d5a80; border: none; color: white; padding: 8px 18px; border-radius: 40px; font-weight: 600; cursor: pointer; transition: 0.2s; box-shadow: 0 4px 8px rgba(0,0,0,0.4); letter-spacing: 0.3px; } .info button:hover { background: #4f6f9f; transform: scale(1.02); } .info button:active { transform: scale(0.98); } </style> </head> <body> <div class="container"> <canvas id="sceneCanvas" width="600" height="600"></canvas> <div class="info"> <span>⚡ gravity + friction</span> <button id="resetBtn">↻ reset ball</button> </div> </div> <script> (function() { const canvas = document.getElementById('sceneCanvas'); const ctx = canvas.getContext('2d'); // ----- configuration ----- const W = 600, H = 600; const CENTER_X = W / 2; const CENTER_Y = H / 2; const HEX_RADIUS = 220; // distance from center to vertex const GRAVITY = 0.25; // px/frame^2 const FRICTION_AIR = 0.998; // per-frame velocity multiplier (air drag) const WALL_RESTITUTION = 0.78; // bounce energy retention const WALL_FRICTION = 0.96; // tangential velocity damping on collision // hexagon rotation let hexAngle = 0; const HEX_SPIN_SPEED = 0.0035; // radians per frame // ball state let ball = { x: CENTER_X, y: CENTER_Y - 40, vx: 3.2, vy: -1.8, radius: 12 }; // ----- helper: get hexagon vertices (rotated) ----- function getHexVertices(angle) { const verts = []; for (let i = 0; i < 6; i++) { const a = angle + (i * Math.PI) / 3; // 60° steps const x = CENTER_X + HEX_RADIUS * Math.cos(a); const y = CENTER_Y + HEX_RADIUS * Math.sin(a); verts.push({ x, y }); } return verts; } // ----- rotate a point around center (for local frame) ----- function rotatePoint(px, py, angle) { const cos = Math.cos(angle); const sin = Math.sin(angle); const dx = px - CENTER_X; const dy = py - CENTER_Y; return { x: CENTER_X + dx * cos - dy * sin, y: CENTER_Y + dx * sin + dy * cos }; } // ----- collision detection & response (ball vs hexagon edges) ----- function handleCollisions() { // get current vertices (world) const verts = getHexVertices(hexAngle); // build edges (with world coordinates) const edges = []; for (let i = 0; i < 6; i++) { const p1 = verts[i]; const p2 = verts[(i + 1) % 6]; edges.push({ p1, p2 }); } // we'll do multiple iterations for stability (max 3) for (let iter = 0; iter < 3; iter++) { let collided = false; for (let e of edges) { const { p1, p2 } = e; // edge vector const ex = p2.x - p1.x; const ey = p2.y - p1.y; const edgeLen = Math.hypot(ex, ey); if (edgeLen < 0.0001) continue; // unit normal (pointing outward from hexagon center) // for a convex polygon, we want normal pointing away from center let nx = ey / edgeLen; let ny = -ex / edgeLen; // check if normal points toward center or away: adjust to outward const toCenterX = CENTER_X - p1.x; const toCenterY = CENTER_Y - p1.y; const dot = nx * toCenterX + ny * toCenterY; if (dot > 0) { // normal points inward, flip nx = -nx; ny = -ny; } // distance from ball center to edge line (signed) const dist = (ball.x - p1.x) * nx + (ball.y - p1.y) * ny; // if ball is penetrating (dist < radius) if (dist < ball.radius) { // find closest point on edge segment to ball center const t = ((ball.x - p1.x) * ex + (ball.y - p1.y) * ey) / (edgeLen * edgeLen); const clampedT = Math.max(0, Math.min(1, t)); const closestX = p1.x + clampedT * ex; const closestY = p1.y + clampedT * ey; const dx = ball.x - closestX; const dy = ball.y - closestY; const distToClosest = Math.hypot(dx, dy); // if the closest point is inside the ball (or very close) if (distToClosest < ball.radius || dist < ball.radius) { // push ball out along normal const overlap = ball.radius - dist; if (overlap > 0) { ball.x += nx * overlap; ball.y += ny * overlap; } else if (distToClosest < ball.radius) { // corner case: push out from closest point const pushDist = ball.radius - distToClosest; if (distToClosest > 0.0001) { const pushX = dx / distToClosest; const pushY = dy / distToClosest; ball.x += pushX * pushDist; ball.y += pushY * pushDist; } else { // fallback: push along normal ball.x += nx * ball.radius; ball.y += ny * ball.radius; } } // ---- velocity response ---- // decompose velocity into normal and tangential components const vn = ball.vx * nx + ball.vy * ny; // normal component (inward negative) const vt_x = ball.vx - vn * nx; const vt_y = ball.vy - vn * ny; // only reflect if moving toward the wall (vn < 0) if (vn < 0) { // normal impulse with restitution const newVn = -vn * WALL_RESTITUTION; // tangential friction (dampen tangential speed) const frictionFactor = WALL_FRICTION; // apply new velocity ball.vx = vt_x * frictionFactor + newVn * nx; ball.vy = vt_y * frictionFactor + newVn * ny; } else { // if moving away, still apply slight friction ball.vx = vt_x * WALL_FRICTION + vn * nx; ball.vy = vt_y * WALL_FRICTION + vn * ny; } collided = true; } } } if (!collided) break; // no more penetration } // extra safety: keep ball inside hexagon (if somehow escaped) // but we trust the collision response; still we clamp to a max radius const distFromCenter = Math.hypot(ball.x - CENTER_X, ball.y - CENTER_Y); const maxAllowed = HEX_RADIUS - ball.radius * 0.5; if (distFromCenter > maxAllowed) { const angle = Math.atan2(ball.y - CENTER_Y, ball.x - CENTER_X); ball.x = CENTER_X + maxAllowed * Math.cos(angle); ball.y = CENTER_Y + maxAllowed * Math.sin(angle); // reflect velocity component outward const nx = Math.cos(angle); const ny = Math.sin(angle); const vn = ball.vx * nx + ball.vy * ny; if (vn > 0) { ball.vx -= 2 * vn * nx; ball.vy -= 2 * vn * ny; } } } // ----- physics update ----- function updatePhysics() { // gravity ball.vy += GRAVITY; // air friction (global) ball.vx *= FRICTION_AIR; ball.vy *= FRICTION_AIR; // integrate position ball.x += ball.vx; ball.y += ball.vy; // collision with rotating hexagon handleCollisions(); // small speed limit to avoid explosion const speed = Math.hypot(ball.vx, ball.vy); const maxSpeed = 18; if (speed > maxSpeed) { ball.vx = (ball.vx / speed) * maxSpeed; ball.vy = (ball.vy / speed) * maxSpeed; } } // ----- drawing ----- function drawHexagon(angle) { const verts = getHexVertices(angle); ctx.beginPath(); ctx.moveTo(verts[0].x, verts[0].y); for (let i = 1; i < 6; i++) { ctx.lineTo(verts[i].x, verts[i].y); } ctx.closePath(); ctx.strokeStyle = '#8ecae6'; ctx.lineWidth = 4; ctx.shadowColor = '#4cc9f0'; ctx.shadowBlur = 18; ctx.stroke(); ctx.shadowBlur = 0; // draw subtle vertex dots ctx.fillStyle = '#ffb703'; for (let v of verts) { ctx.beginPath(); ctx.arc(v.x, v.y, 4, 0, 2 * Math.PI); ctx.fill(); } } function drawBall() { // glow effect ctx.shadowColor = '#ff6b6b'; ctx.shadowBlur = 25; ctx.beginPath(); ctx.arc(ball.x, ball.y, ball.radius, 0, 2 * Math.PI); ctx.fillStyle = '#fca311'; ctx.fill(); ctx.shadowBlur = 0; // highlight ctx.beginPath(); ctx.arc(ball.x - 3, ball.y - 3, 4, 0, 2 * Math.PI); ctx.fillStyle = '#ffe5b4'; ctx.fill(); } // ----- animation loop ----- function animate() { // rotate hexagon hexAngle += HEX_SPIN_SPEED; // physics updatePhysics(); // draw ctx.clearRect(0, 0, W, H); // subtle background grid ctx.strokeStyle = '#2a2a4a'; ctx.lineWidth = 0.5; for (let i = 0; i < W; i += 30) { ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, H); ctx.stroke(); ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(W, i); ctx.stroke(); } // draw hexagon drawHexagon(hexAngle); // draw ball drawBall(); requestAnimationFrame(animate); } // ----- reset ball to center with random velocity ----- function resetBall() { ball.x = CENTER_X; ball.y = CENTER_Y - 30; ball.vx = (Math.random() - 0.5) * 6; ball.vy = (Math.random() - 0.5) * 4 - 2; // ensure not zero if (Math.abs(ball.vx) < 0.5) ball.vx = 1.2; if (Math.abs(ball.vy) < 0.5) ball.vy = -1.5; } // ----- event listener for reset button ----- document.getElementById('resetBtn').addEventListener('click', resetBall); // start animation animate(); })(); </script> </body> </html>

전체 프롬프트를 보려면 로그인하세요

Continue with:

By logging in, you agree to our Terms of Use and Privacy Policy

사용법

이 프롬프트는 coding와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.

최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.

참고 자료

분류:coding| twitter| html| css

토론