2014년 2월 26일 수요일

[일기]Box2D, Fixture.

나는 인터넷 상에서 고정물(붙박이)라 표현하는 Fixture를 알아봤다.

 이 친구는 물체의 모양, 재질과 크기 정보를 묘사하는데 쓰인다. 한 개 강체에 여러개를 붙일 수 있으며 질량은 이것들의 밀도와 면적을 합한 산술량의 총합으로 계산된다. 이것들이 실제 물체의 모양을 결정짓는다. 즉, 외형을 묘사하므로 충돌 반응은 이들의 특성에 의존하게 된다.

 고정물은 그 윤곽을 가지고 있으며, 보통 물체의 재질을 대표하는 것으로서 탄성과 마찰, 밀도를 설정할 수 있다. 이후에 이 고정물을 '감지기'로 활용할 수도 있는데 이는 추후에 다룬다.

 전에 언급하였다시피 고정물은 이를 부착할 강체의 포인터를 이용하여 생성해내며 그 전에 사전 정의(특성 정의)가 만들어져야 함은 동일하다.

 b2BodyDef bdef;
 bdef.type = b2_dynamicBody;
 bdef.position.Set( 0, 0 );

 b2Body* body = m_world->CreateBody(&bdef);

 를 통해 강체의 포인터를 얻었다.

  body->CreateFixture( ... )를 통해 생성이 가능하다.

 고정물은 물체가 어떻게 생겨먹었는가를 지정하는 것이 가장 중요하다. 이는 다음과 같은 구조체들을 통해 설정한다.

 b2CircleShape : 원모양의 윤곽을 생성하기 위해 사용. 멤버로 m_p를 가지며 position 정보를 갖는다. m_radius 멤버로 그 크기를 반지름으로 지정한다.

 b2PolygonShape : 다각형의 모양을 정의하기 위함. SetAsBox 메서드를 통해 사각 박스(사각형)를 손쉽게 생성할 수도 있으며, 다각형의 정점을 배열로 선언하여 모양을 정의할 수 있다. 단, 엔진 내 코드에서 최대 정점은 8개로 제한되며, 볼록 다각형을 정의하여야 하고 정점의 방향은 반시계 방향으로 정의되어야 한다.

예시 )

 b2CircleShape cShape;
 cShape.m_p.Set(0, 0);
 cShape.m_radius = 1;

 b2FixtureDef fdef;
 fdef.shape = &cShape;        // 주소를 넣는다.

 body->CreateFixture(&fdef);

 -----------------------------------------

 b2Vec2 vertices[5];
 vertices[0].Set(-1, 2);
 vertices[1].Set(-1, 0);
 vertices[2].Set( 0,-3);
 vertices[3].Set( 1, 0);
 vertices[4].Set( 1, 1);

 b2PolygonShape pShape;
 pShape.Set(vertices, 5);

//pShape.SetAsBox(2, 2);   //여기서 각각은 폭, 높이이며 절반의 길이이다.

 fdef.shape = &pShape;
 body->CreateFixture(&fdef);
------------------------------------------

 직선인, 면적이 0인 모양도 정의할 수 있는데 Box2D의 2.1.2버전부터 b2EdgeShape를 이용하여 이 모양을 정의할 수 있도록 변경되었다.

 b2EdgeShape eShape;
 eShape.Set( b2Vec2(-10, 0), b2Vec2(10, 0) );

 fdef.shape = &edgeShape;


 이와 같이 고정물의 특징을 정의한다. 그러나 위 예제코드만 작성해서는 프로그램 실행 시에 이들이 절대 회전을 하거나 미끄러지거나 하지 않는 것을 알 수 있다. 이는 물체의 특성인 밀도를 정의하는 변수가 0을 기본값으로 가지기 때문인데 이로 인해 질량이 0이 된다. 즉 모든 질량에 관계된 물리량이 0이 되었기에 운동 특성이 나타나지 않는다.

 질량은 고정물의 면적에 밀도를 곱하여 이루어진다.

 이에 고정물의 density 값을 양의 값으로 지정하면 질량이 생기고 이상적으로 물체가 움직이게 된다.

 fdef.density = 1;

 위와 같이 하나의 강체 포인터를 통해 지속적으로 고정물을 생성하면 이 강체에 고정물들이 여러개가 붙게된다. 기본적으로 부착되는 위치는 강체의 중심이며 이는 강체의 위치이다(강체 정의 구조체로부터 정의된).

 이 부착될 위치는 따로 정의가 가능하며 이는 고정물의 정의 구조체(b2FixtureDef)의 외형을 정의하는 Shape들로부터 설정할 수 있다.

 정점의 배열을 통해 외형을 결정하는 경우 정점의 위치 0, 0이 강체의 중심과 같으므로 이 위치를 편향시켜 부착될 위치를 결정할 수 있다. SetAsBox나 Set과 같은 메서드를 통해 윤곽을 정의할 경우 이들의 오버로드된 메서드를 이용하여 중심의 위치를 정할 수 있다(오버로드된 메서드에 중심을 명시하는 정점을 지정할 수 있다).

  vertices[0].Set(-1 +10,  2);  //x위치가 우측으로 10만큼 편향됨
  vertices[1].Set(-1 +10,  0);
  vertices[2].Set( 0 +10, -3);
  vertices[3].Set( 1 +10,  0);
  vertices[4].Set( 1 +10,  1);

  pShape.SetAsBox( 2, 2, b2Vec2(100, 0), 0); //셋째 인자가 지정할 중심, 강체로부터 우측으로 100만큼 떨어진 곳에 할당한다. 넷째 인자는 회전각이다.

 물체의 특성인 마찰을 적용할 수 있다.

 friction 멤버를 이용하여 값을 지정하며 0~1 사이의 값을 할당한다. 이 속성은 질량이 있어야 적용되므로 밀도가 0이면 적용되지 않는다.

 값이 1 이라고 해서 반드시 안 미끄러지지는 않는다. 마찰력은 마찰계수로부터 정의가 되며 수직항력에 마찰력이 비례함을 알 것이다.

 고정물이 강체에 여럿 붙어있는 경우, 각 고정물마다 다른 마찰값을 지정할 수 있다. 이는 밀도도 마찬가지이며 다른 타 속성들도 마찬가지이다.

 restitution 멤버를 이용하여 탄성의 정도를 지정할 수 있다. 이 또한 0 ~ 1 사이의 값을 가지나 값이 0이라 해서 반드시 튀지 않는다는 것은 아니다.

 고정물의 속성을 실행중에 변경하는 것은 고정물 포인터를 통한 메서드 호출로써 가능하다.

 fixturePointer->SetDensity( ... );
 fixturePointer->SetRestitution( ... );
 fixturePointer->SetFriction( ... );

 그렇다면 이 포인터를 얻는 방법이 필요한데, 이는 강체의 GetFixtureList() 메서드를 통해 구할 수 있다. 반환값은 리스트 구조이다.

  for (b2Fixture* f = body->GetFixtureList(); f; f = f->GetNext())
  {
      //이와 같이 순환하며 각 고정물마다 필요한 작업을 시킬 수 있다.
  }

 강체는 World 클래스로부터 그 목록을 구할 수 있음을 전에 언급했었다.

 주의할 점은 밀도를 변경시킨다고 하여 질량이 같이 변경되어 적용되지 않는다는 것이다. 밀도가 변경되면 강체에게 ResetMassData() 메서드를 호출하게 해서 질량값을 새로 구해야 한다.

 고정물의 제거는 강체의 DestroyFixture(pFixture)의 형태로 메서드를 호출함으로 이루어진다.

 만일 강체를 제거하면 붙어 있는 고정물도 같이 제거된다. 그러므로 포인터 변수에 접근할 때 이와 같이 암묵적으로 같이 제거된 변수에 접근하지 않도록 할 필요가 있는데 이는 아직 파악하지는 못하였으나 공식 사용자 메뉴얼 상에 있는 묵시적 제거를 위한 리스너 클래스를 이용해 콜백 메서드를 통해 처리하는 단계가 있다고 한다.

댓글 없음:

댓글 쓰기