Learning
레슨 5 / 8·20분

충돌 감지와 이벤트

충돌 필터링과 이벤트 심화

Matter.js는 충돌 필터링 시스템을 통해 어떤 바디끼리 충돌할지 세밀하게 제어할 수 있습니다. collisionFilter의 category와 mask를 비트 연산으로 설정하여 그룹 간 충돌을 허용하거나 차단합니다. Events 시스템은 collisionStart, collisionActive, collisionEnd, beforeUpdate, afterUpdate 등 다양한 시점에 훅을 걸 수 있습니다.

javascript
const { Events, Bodies, World, Body } = Matter;

// ── 충돌 필터 (Collision Filter) ──
// category: 비트 플래그 (1, 2, 4, 8, 16, ...)
// mask: 충돌 가능한 카테고리의 비트 OR 조합
const categoryA = 0x0001; // 그룹 A (1)
const categoryB = 0x0002; // 그룹 B (2)
const categoryC = 0x0004; // 그룹 C (4)

// 그룹 A: B와만 충돌
const ballA = Bodies.circle(200, 100, 25, {
  label: 'ballA',
  collisionFilter: {
    category: categoryA,
    mask: categoryB,         // B와만 충돌
  },
  render: { fillStyle: '#e74c3c' },
});

// 그룹 B: A, C 모두와 충돌
const ballB = Bodies.circle(400, 100, 25, {
  label: 'ballB',
  collisionFilter: {
    category: categoryB,
    mask: categoryA | categoryC,  // A와 C 모두 충돌
  },
  render: { fillStyle: '#3498db' },
});

// 그룹 C: B와만 충돌
const ballC = Bodies.circle(600, 100, 25, {
  label: 'ballC',
  collisionFilter: {
    category: categoryC,
    mask: categoryB,
  },
  render: { fillStyle: '#2ecc71' },
});

World.add(engine.world, [ballA, ballB, ballC]);
javascript
// ── 이벤트 심화: 게임 로직에 활용 ──
let score = 0;

// 충돌 시작
Events.on(engine, 'collisionStart', (event) => {
  event.pairs.forEach((pair) => {
    const labels = [pair.bodyA.label, pair.bodyB.label];

    // 특정 바디 충돌 감지
    if (labels.includes('player') && labels.includes('coin')) {
      const coin = pair.bodyA.label === 'coin' ? pair.bodyA : pair.bodyB;
      score += 10;
      World.remove(engine.world, coin);
      console.log('점수: ' + score);
    }

    // 위험 구역 충돌
    if (labels.includes('player') && labels.includes('hazard')) {
      console.log('게임 오버!');
    }
  });
});

// 충돌 지속 중 (매 프레임)
Events.on(engine, 'collisionActive', (event) => {
  event.pairs.forEach((pair) => {
    // 지속 충돌 중인 바디 처리
  });
});

// 엔진 업데이트 전/후 훅
Events.on(engine, 'beforeUpdate', (event) => {
  // 물리 연산 전에 바디 상태 조정
  // 예: 속도 제한, 영역 이탈 체크
  const bodies = Composite.allBodies(engine.world);
  bodies.forEach((body) => {
    // 화면 밖으로 나간 바디 제거
    if (body.position.y > 700) {
      World.remove(engine.world, body);
    }
  });
});
  • collisionFilter.category — 바디의 충돌 그룹 (비트 플래그)
  • collisionFilter.mask — 충돌 허용 그룹 마스크 (비트 OR)
  • collisionStart — 충돌이 처음 시작될 때 발생
  • collisionActive — 충돌이 유지되는 동안 매 프레임 발생
  • collisionEnd — 충돌이 끝났을 때 발생
  • beforeUpdate / afterUpdate — 물리 엔진 업데이트 전후 훅
  • event.pairs — 충돌한 바디 쌍 배열 (bodyA, bodyB)
💡

충돌 필터의 category와 mask는 비트 연산을 사용합니다. category는 반드시 2의 거듭제곱(1, 2, 4, 8...)이어야 하며, mask에는 여러 카테고리를 | (OR) 연산자로 결합합니다. 기본값은 category: 0x0001, mask: 0xFFFFFFFF(모두 충돌)입니다.