2014년 3월 2일 일요일

[일기]Box2D, collision-fiter, 충돌 거르기

충돌에서 중요한 것은 고정체이다. 이 고정체의 외형에 따라 충돌이 결정되기 때문이다.

그러나 때때로 특정 물체끼리는 충돌이 일어나지 않았으면 하는 때가 존재한다.

충돌 거르기란, 어떠한 고정체들끼리 충돌을 하고 안할지를 선별하는 작업을 의미한다.

이러한 거르기를 위한 플래그는 다음 세 가지가 있다. 고정체의 필터 속성에 있는 멤버이다.

 categoryBits : 소속 비트, 16비트 unsigned 형 정수(unsigned short) 값이다.
 maskBits      : 충돌 비트, 16비트 unsigned 형 정수 값이다.
 groupIndex   : 그룹을 정의하며, 상위 2개 플래그를 덮어씌운다.

카테고리비트란, 자신이 속한 부류를 설정한다. 비트열로 연산하므로 이 부류는 최대 16가지가 있으며, 기본값은 1이다. 0이 지정되면 절대 충돌하지 않는 물체가 된다.
값의 예는 다음과 같다.
0x0000
0x0001  // 00000000 00000001
0x0002  // 00000000 00000010
0x0004  // 00000000 00000100
0x0008  // 00000000 00001000
0x0010  // 00000000 00010000
0x0020  // 00000000 00100000
...
0x1000  // 00010000 00000000
0x2000  // 00100000 00000000
0x4000  // 01000000 00000000
0x8000  // 10000000 00000000

마스크비트는 자신이 충돌할 카테고리비트를 지정하는 플래그이다. 이 플래그의 값과 & 연산을 하여 0이 아닌 값이 반환될 경우 충돌은 이루어진다.

 만일 0x100F(00010000 00001111)와 같은 값을 가지는 경우 이러한 마스크비트가 지정된 고정체는 카테고리가 0x1000, 0x0008, 0x0004, 0x0002, 0x0001과 충돌한다. 기본값은 0xFFFF이다.

 여기서 중요한 것은 충돌이 이루어지려면 두 고정체 상호간에 모두가 충돌이 이루어져야 한다고 판단이 이루어져야만이 진짜 충돌이 이루어진다는 것이다. 무슨 말인고 하니....

 고정체 A의 카테고리는 0x0001이며, 마스크는 0xFFFF 이다.
 고정체 B의 카테고리는 0x0002이며, 마스크는 0xF000 이다.

 A의 경우 B와 충돌할 것이다. B는 A와 충돌하지 않을 것이다. 이 경우 한 고정체(A)는 충돌을 가정했으나 나머지 고정체(B)는 충돌을 무시했으니 상호간 충돌이 모두 결정되지 않았으므로 충돌은 이루어지지 않는다. 즉, 충돌은 모두가 충돌이 이루어져야 한다고 결정할 때(두 고정체 모두 자신의 마스크와 상대의 카테고리의 & 연산의 결과가 0이 아니어야 한다) 이루어진다.

 충돌이 이루어지는지에 대한 값을 얻는 논리코드를 작성하면 다음과 같다.

 bool collide =
          (filterA.maskBits & filterB.categoryBits) != 0 &&
          (filterA.categoryBits & filterB.maskBits) != 0;

 collide가 참값이어야 충돌이 이루어진다. 이것이 참값이 되기 위한 조건은 위 코드를 보면 판단이 가능할 것이라 본다.

 그렇다면 그룹 인덱스란 무엇인가. 이것은 위와 같은 카테고리와 마스크 비트를 덮어씌운다고 표현하였다. 이러한 표현을 한 이유는 위에서 설명한 규칙이 무시될 수 있기 때문이다.

 인덱스 그룹의 값에 따른 규칙이 있는데 다음과 같다. 참고로 인덱스 그룹은 signed integer 자료형이며 기본값은 0이다.

 만일 두 고정체중 어느 하나라도 인덱스 그룹이 0이면, 위에서 설명한 카테고리와 마스크의 규칙을 따른다.

 만일 두 고정체의 인덱스 그룹이 모두 0이 아니지만 그 값이 서로 다른 경우, 위에서 설명한 카테고리와 마스크의 규칙을 따른다.

 만일 두 고정체의 인덱스 그룹값이 서로 같고 양수인 경우, 충돌한다.

 만일 두 고젇체의 인덱스 그룹값이 서로 같고 음수인 경우, 충돌하지 않는다.


이것이 규칙이다.

 이러한 값을 설정하는 방법은 다음과 같다.

 첫째, 고정체의 정의 구조체를 이용하는 방법이다.

 FixtureDef myFixtureDef;
 myFixtureDef.filter.categoryBits = 0x0100;
 myFixtureDef.filter.maskBits = 0x01FF;
 myFixtureDef.filter.groupIndex = -1;

 둘째, 고정체 포인터 변수로부터 필터를 얻어와 동적으로 변경하는 방법이다.

 b2Filter filter = pFixture->GetFilterData();
 filter.categoryBits = 0x8000;
 filter.maskBits = 0xFFF0;
 filter.groupIndex = -23;

 pFixture->SetFilterData(filter);


충돌을 제어하기 위한 좀더 섬세한 작업을 가능하도록 하기 위해서 box2D는 ShouldCollide 콜백을 제공한다.

 bool b2ContactFilter::ShouldCollide(b2Fixture* fixtureA, b2Fixture* fixtureB);

 참을 리턴하면 충돌하며, 거짓을 리턴하면 충돌하지 않는다.

 기존 콜백 메서드를 사용하는 방식과 마찬가지로 이 클래스도 사용하려면 계 클래스에 콜백으로 지정하여 사용한다.

 각 고정체를 판단하기 위해 사용자 데이터를 사용하는 것 등을 포함해서 즉, 기존 메서드들과 마찬가지 방식으로 동일하게 사용하면 된다. 단지 리턴값이 존재할 뿐이다.

댓글 없음:

댓글 쓰기