한양과학기술고등학교

에듀테크 프로젝트 대시보드

보이드 무리 짓기 (Boids)

보이드 무리 짓기 (Boids)

한 마리 한 마리는 딱 3가지 규칙만 본다. 리더도, 전체 계획도 없다. 그런데 화면 전체에서는 살아 있는 새 떼처럼 보인다 — 이것이 창발(emergence)이다.

어디서 봤을까? 영화 배트맨 리턴즈(1992)의 박쥐 떼와 라이온 킹의 누 떼 질주, 수많은 게임 속 물고기·새·좀비 군중, 드론 군집 비행 연구 — 뿌리는 모두 1986년 크레이그 레이놀즈가 만든 Boids("bird-oid object", 새 비슷한 것)다. 개체를 에이전트라 부르고, 이런 방식을 에이전트 기반 시뮬레이션이라 한다.

1. 규칙 3개

각 보이드는 자기 주변 지각 반경(perception radius) 안에 있는 이웃만 본다. 그 이웃들을 보고 세 방향의 조향력(steering force)을 계산해 더한다.

분리 Separation
너무 가까운 이웃에게서 멀어진다. 충돌·뭉침 방지.

정렬 Alignment
이웃들의 평균 속도(방향)에 내 방향을 맞춘다. 같이 흐른다.

응집 Cohesion
이웃들의 무게중심으로 향한다. 무리에서 낙오하지 않는다.

회색 원 = 지각 반경, 흰 보이드 = 나, 파란 보이드 = 이웃, 화살표 = 그 규칙이 만드는 조향 방향

2. 직접 만져보는 새 떼

세 규칙의 가중치를 바꿔 보라. 분리만 크면 서로 밀어내 흩어지고, 응집만 크면 한 점으로 뭉친다. 셋이 균형을 이룰 때 비로소 "떼"가 된다.

분리 가중치 1.60
정렬 가중치 1.00
응집 가중치 0.90
지각 반경 55
최고 속도 3.2
개체 수 140
캔버스를 클릭·드래그하면 그 지점을 피한다

가장자리는 반대편으로 이어진다(토러스). 매를 켜면 무리가 갈라지며 도망치는 회피 물결이 보인다.

3. 한 보이드가 매 프레임 하는 일

  1. 지각 반경 안의 이웃을 모은다 (없으면 그냥 직진).
  2. 분리 가까운 이웃일수록 강하게, 그 반대 방향 벡터를 합산.
  3. 정렬 이웃 속도의 평균을 구해, 그 방향으로 조향.
  4. 응집 이웃 위치의 평균(무게중심)을 구해, 그쪽으로 조향.
  5. 세 힘에 가중치를 곱해 더하고 가속도로 삼는다.
  6. 속도 += 가속도, 속도를 최고 속도로 제한(clamp), 위치 += 속도.

이웃마다 거리를 재므로 소박하게 짜면 O(n²)다. 개체가 많아지면 공간 격자나 쿼드트리로 "근처 칸"만 검사해 O(n)에 가깝게 줄인다.

코드로 보는 핵심

function flock(b, boids, r, wSep, wAli, wCoh, maxSpeed, maxForce) {
  let sep = {x:0,y:0}, ali = {x:0,y:0}, coh = {x:0,y:0}, n = 0;

  for (const o of boids) {
    if (o === b) continue;
    const dx = b.x - o.x, dy = b.y - o.y;
    const d = Math.hypot(dx, dy);
    if (d > 0 && d < r) {
      sep.x += dx / (d*d);  sep.y += dy / (d*d);   // 가까울수록 강하게 밀어냄
      ali.x += o.vx;        ali.y += o.vy;          // 이웃 속도 합
      coh.x += o.x;         coh.y += o.y;           // 이웃 위치 합
      n++;
    }
  }
  if (n === 0) return;

  ali.x /= n; ali.y /= n;                           // 평균 속도
  coh.x = coh.x/n - b.x; coh.y = coh.y/n - b.y;     // 무게중심으로의 방향

  const acc = {
    x: wSep*unit(sep).x + wAli*unit(ali).x + wCoh*unit(coh).x,
    y: wSep*unit(sep).y + wAli*unit(ali).y + wCoh*unit(coh).y
  };

  b.vx = clampMag(b.vx + acc.x, maxSpeed);
  b.vy = clampMag(b.vy + acc.y, maxSpeed);
  b.x += b.vx; b.y += b.vy;
}

unit()는 벡터를 길이 1로, clampMag()는 길이 상한을 씌운다. 실제 구현은 각 규칙에도 maxForce로 조향력 상한을 둬 급회전을 막는다.

규칙을 더 얹으면

추가 규칙효과
목표 추종 (seek)먹이·이동 경로 등 특정 지점으로 무리를 유도
장애물 회피앞쪽 벽·바위를 감지해 미리 옆으로 틈
포식자 회피 (flee)매·상어가 지각 반경에 들어오면 강한 반대 방향 힘
시야각 제한뒤쪽 이웃은 무시 → 더 자연스러운 흐름
경계 처리벽에서 튕김 / 화면을 감싸는 토러스 / 중앙으로 살짝 당김
한 문장 요약: 보이드는 "부딪히지 마 · 같이 가 · 뒤처지지 마" 세 마디를 개체마다 반복시킨 것이다. 똑똑한 개체가 아니라 단순한 규칙의 상호작용에서 무리의 지능이 태어난다.