보이드 (군집 시뮬레이션)

Boids

분리·정렬·응집이라는 세 가지 단순한 규칙만으로 새 떼나 물고기 떼 같은 군집 움직임을 만드는 시뮬레이션. Craig Reynolds가 1987년 발표했습니다.

다른 이름: Flocking simulationCraig Reynolds boids
···
js
const c = document.createElement('canvas');
document.body.appendChild(c);
c.style.width = '100%'; c.style.height = '100%';
const ctx = c.getContext('2d');
const cs = getComputedStyle(document.documentElement);
const ACCENT = cs.getPropertyValue('--accent').trim() || '#5b5bf7';
let w, h;
function resize() {
  const dpr = Math.min(devicePixelRatio || 1, 2);
  w = innerWidth; h = innerHeight;
  c.width = w * dpr; c.height = h * dpr;
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
addEventListener('resize', resize);
resize();

const N = 150, VISION = 46, MAXSPD = 2.6;
const boids = Array.from({ length: N }, () => ({ x: Math.random() * w, y: Math.random() * h, vx: (Math.random() - 0.5) * 2, vy: (Math.random() - 0.5) * 2 }));
function step() {
  for (const b of boids) {
    let sx = 0, sy = 0, ax = 0, ay = 0, cx = 0, cy = 0, n = 0;
    for (const o of boids) {
      if (o === b) continue;
      const dx = o.x - b.x, dy = o.y - b.y, d2 = dx * dx + dy * dy;
      if (d2 < VISION * VISION) {
        n++; ax += o.vx; ay += o.vy; cx += o.x; cy += o.y;
        if (d2 < 320) { sx -= dx; sy -= dy; }
      }
    }
    if (n) {
      b.vx += (ax / n - b.vx) * 0.04 + (cx / n - b.x) * 0.0006 + sx * 0.004;
      b.vy += (ay / n - b.vy) * 0.04 + (cy / n - b.y) * 0.0006 + sy * 0.004;
    }
    const spd = Math.hypot(b.vx, b.vy) || 1;
    if (spd > MAXSPD) { b.vx = (b.vx / spd) * MAXSPD; b.vy = (b.vy / spd) * MAXSPD; }
    b.x += b.vx; b.y += b.vy;
    if (b.x < 0) b.x += w; if (b.x > w) b.x -= w;
    if (b.y < 0) b.y += h; if (b.y > h) b.y -= h;
  }
}
function draw() {
  ctx.fillStyle = 'rgba(13,13,18,0.28)';
  ctx.fillRect(0, 0, w, h);
  ctx.fillStyle = ACCENT;
  for (const b of boids) {
    const ang = Math.atan2(b.vy, b.vx);
    ctx.save(); ctx.translate(b.x, b.y); ctx.rotate(ang);
    ctx.beginPath(); ctx.moveTo(6, 0); ctx.lineTo(-5, 3.5); ctx.lineTo(-5, -3.5); ctx.closePath(); ctx.fill();
    ctx.restore();
  }
}
for (let i = 0; i < 40; i++) step(); // 무리가 형성된 상태로 시작
draw();
(function loop() { step(); draw(); requestAnimationFrame(loop); })();

개체(보이드) 하나하나는 전체 무리의 모양을 알지 못합니다. 각자 자기 주변의 이웃만 보고 세 가지 규칙을 따를 뿐입니다. 너무 가까운 이웃에게서는 멀어지고(분리), 이웃들의 평균 진행 방향을 따라가고(정렬), 이웃들의 평균 위치 쪽으로 살짝 끌려갑니다(응집). 이 국소적인 규칙 세 개만으로 중앙 통제 없이도 유체처럼 흐르는 무리 움직임이 나타납니다.

이런 현상을 창발(emergence)이라고 부릅니다 — 전체의 복잡한 행동이 단순한 개별 규칙들의 상호작용만으로 저절로 생겨나는 것입니다. Reynolds는 이 결과를 영화 배트맨 리턴즈(1992)의 박쥐 떼·펭귄 떼 장면에 실제로 사용했고, 이 공로로 1998년 아카데미 기술상을 받았습니다.

디자인에서는 인터랙티브 배경, 파티클 로고 애니메이션, 게임의 군중·새 떼 AI에 그대로 쓰입니다. 세 규칙의 가중치를 조절하면 흩어지는 나비 떼부터 밀집한 물고기 떼까지 완전히 다른 느낌을 낼 수 있습니다.

언제 쓰나

커서를 따라다니는 인터랙티브 파티클 배경, 로고를 이루는 입자 군집 애니메이션에. 개체 수가 많아지면 이웃 탐색에 공간 분할(그리드)을 쓰세요.