회전하는 육각형 안에서 튕기는 공
Wikiprompt, 무료 프롬프트 백과사전에서
회전하는 육각형 안에서 튕기는 공 회전하는 육각형 내부에서 중력과 마찰을 적용하여 공이 튕기는 물리 시뮬레이션을 생성하는 프롬프트.
프롬프트 내용저장
🌐
<!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 도구에 붙여넣으세요.
최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.
토론
댓글 0개