도르래입니다. 정확한 번역은 모르겠네요. pull이 "당기다"의 의미를 가지고 있으니 대강 비슷한 느낌으로 생각하셔도 될듯합니다. 다른 joint type들에 비해서 조금 특수합니다. body들도 많이 필요하고 anchor들도 많이 필요합니다. joint의 정의 또한 좀 더 복잡한 편이구요.

그래도 도르래가 뭔지 알고있다면 나머지는 definition의 속성들을 바꿔가면서 익힐 수 있으니 그리 어렵지 않게 적을 할 수 있습니다. 말 10마디 하는것보다 직접 보는게 낫더군요.
이번엔 pulley joint 뿐만 아니라 revolute joint도 같이 적용해봤습니다. 저울이죠. 저울의 양쪽에 revolute joint로 균등하게 엮여 있습니다. 

마우스로 허공에 클릭하면 일정한 크기의 상자가 생기구요. '추'라고 가정해봅니다. 저울 양쪽에 추를 올려놓고 무게에 따른 길이 변화를 관찰하는 것이 목적인 예제 입니다. 사실 저울과는 거리가 좀 있죠. 양쪽의 길이 변화가 ratio 값에 따라 영향을 받는듯 합니다. 만들면서 아직 제가 감을 덜 잡은 부분이기도 하구요. ratio에 따른 길이 변화를 좀 더 명확하게 이해시켜주실분이 어디 없을지..

function onClick(event:MouseEvent):void {

createBox(mouseX, mouseY, 30, 30, 0, true);

}


마우스 이벤트 핸들러입니다. 클릭된 위치에 가로 세로 30 크기의 dynamic body를 만들어냅니다.

function createBox(px:Number, py:Number, w:Number, h:Number, angle:Number, isDynamic:Boolean):b2Body;


function createCircle(px:Number, py:Number, r:Number, isDynamic:Boolean):b2Body;


언제나 한결같은 box와 circle 생성함수입니다. 언급만 하고 패스.

function createRevoluteJoint(body1:b2Body, body2:b2Body, anchor:b2Vec2):void

{

var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();

jointDef.Initialize(body1, body2, anchor);

jointDef.collideConnected = true;

jointDef.enableLimit = false;

jointDef.enableMotor = false;

world.CreateJoint(jointDef);

}


지난번에 다뤘던 revolute joint 생성함수 입니다. 이 예제에서는 limit과 motor 모두 사용하지 않는 부분만 보시면 되겠습니다.

function createScale(px:Number, py:Number, w:Number, h:Number):b2Body

{

var center:b2Body = createCircle(px, py, 5, true);

var flate:b2Body = createBox(px, py+h, w, 10, 0, true);

createRevoluteJoint(flate, center, new b2Vec2((px-w/2)/scale, (py+h)/scale));

createRevoluteJoint(flate, center, new b2Vec2((px+w/2)/scale, (py+h)/scale));

return center;

}


저울의 한쪽 부분을 생성하는 함수입니다. 지정된 위치에 중심 body를 동그랗게 만들고 h만큼 아래쪽에 판을 w만큼 길게 만들어 냅니다. 그리고 그 판의 양쪽 끝을 중심 body랑 각각 revolute joint 로 엮어주죠. 당연히 body들은 모두 dynamic 이겠죠. 안그럼 저울의 의미가 없으니까. 마지막으로 모든 단위는 각 스케일에 맞게 적용되어야 합니다. 픽셀과 미터는 전혀 상관없는 단위니까요.

function createPulleyJoint(body1:b2Body, body2:b2Body, ground1:b2Vec2, ground2:b2Vec2, anchor1:b2Vec2, anchor2:b2Vec2):void

{

var jointDef:b2PulleyJointDef = new b2PulleyJointDef();

jointDef.Initialize(body1, body2, ground1, ground2, anchor1, anchor2, 1);

jointDef.collideConnected = true;

jointDef.maxLengthA = 190;

jointDef.maxLengthB = 190;

world.CreateJoint(jointDef);

}


그리고 이번에 새롭게 만들어진 pulley joint 입니다. body1을 ground1이랑 anchor1을 기준으로 엮어주고 body2는 ground2, anchor2 와 엮어놓으면 두 body들이 pulley joint의 영향을 받게 됩니다. 하지만 양쪽에 할당될 수 있는 길이는 한정되어 있기 때문에 그 최대값을 여유있게 잡아 놓았습니다. 이 예제에서는 기본적으로 100 정도의 높이에 각 저울이 위치하고 무개에 맞게 늘어나고 줄어들되 0보다 작을수 없으며 190보다 커질 수 없게 해두었습니다.

또한 두 저울이 서로 간섭을 할 수 있게 했네요. 사실 좀 떨어진 거리여서 간섭을 만들어도 별다른 차이점이 이 예제에서 찾아보기 힘듭니다만. : )

var ground1:b2Body = createCircle(100, 0, 5, false);

var ground2:b2Body = createCircle(400, 0, 5, false);

var scale1:b2Body = createScale(100, 200, 140, 90);

var scale2:b2Body = createScale(400, 200, 140, 90);


createPulleyJoint(scale1, scale2, ground1.GetWorldCenter(), ground2.GetWorldCenter(), scale1.GetWorldCenter(), scale2.GetWorldCenter());


이 부분이 createPulleyJoint 함수를 사용해서 두 저울을 생성하고 엮어내는 부분입니다. 저는 생성자 함수에서 이 부분을 사용하고 있습니다. anchor 들은 각 body들의 중심점이므로 간편하게 .GetWorldCenter 함수를 통해서 이용하고 있습니다.

'programming > box2d docs' 카테고리의 다른 글

b2PrismaticJoint 예제  (0) 2011.02.01
b2Revolute Joint 예제  (0) 2011.01.30
b2DistanceJoint 예제  (0) 2011.01.28
b2Body 예제 - Box2DFlash  (4) 2011.01.17
Box2DFlash v2.1a Update Notes 비공식 한글문서  (0) 2011.01.16

WRITTEN BY
buzzler

,
prismatic joint는 여닫이 문이나 피스톤같은 제한적 직선운동을 만들때 필요한 joint type입니다. 슬라이더라고도 하죠. revolute joint 처럼 limit을 설정할 수 있어서 일정 바운더리 안에서의 직선 이동을 하도록 제한할 수 있습니다. 또한 motor를 활성화 할 수도 있구요. 이를 이용해서 아래 침대 스프링 형태의 joint를 만들어봤습니다.
마우스로 클릭하면 클릭한 위치에 원이 생기고 자유낙를 하죠. 떨어지는 힘을 받아서 바닥의 스프링들이 출렁거리게 만들어졌습니다.

각 스프링들은 위로 향하는 motor가 돌고 있구요. 하지만 limit이 제한되어 있기 때문에 계속 올라가진 않고 한계점에 서 있게 됩니다. 원과 부딛힌 스프링은 충돌받은 힘만큼 반동으로 아래로 밀려나게 했습니다만 restitution(반발력)이 0 이기 때문에 서로 밀쳐내서 얌채공처럼 튀어오르는 느낌을 없앴습니다. 얌채공같이 반발력이 좋은 물체를 만들려면 restitution값을 1로 설정하면 될것입니다.

joint나 body의 많은 속성들을 바꿔가면서 속성을 이해하는것이 중요한것 같습니다. 말로는 이해하기 힘들어도 속성값 바꿔가면서 느끼니 훨씬 다루기 쉬워지네요.

아래는 예제를 만들기 위해 기본적으로 필요했던 함수 두개 입니다. 상자를 만드는 함수와 원을 만드는 함수죠. 설명 생략하고 함수 구현은 첨부된 .as 파일을 참조하시길.

function createBox(x:Number, y:Number, w:Number, h:Number, angle:Number, isDynamic:Boolean):b2Body;


function createCircle(x:Number, y:Number, r:Number, isDynamic:Boolean):b2Body;


그리고 덤으로 클릭시 원을 생성해주는 핸들러 입니다. 위의 함수를 이용하구요.

function onClick(event:MouseEvent):void {

createCircle(mouseX, mouseY, Math.random()*20+15, true);

}


이제 prismatic joint를 만드는 부분을 보겠습니다.

function createPrismaticJoint(body1:b2Body, body2:b2Body, anchor:b2Vec2, axis:b2Vec2, thick:Number):void

{

var jointDef:b2PrismaticJointDef = new b2PrismaticJointDef();

jointDef.Initialize(body1, body2, anchor, axis);

jointDef.collideConnected = false;

jointDef.enableLimit = true;

jointDef.enableMotor = true;

jointDef.lowerTranslation = -(thick/2)/scale;

jointDef.upperTranslation = (thick/2)/scale;

jointDef.motorSpeed = 3;

jointDef.maxMotorForce = 2900;

world.CreateJoint(jointDef);

}


이는 이 예제에서 사용하기 위한 전용 함수라고 봐도 되겠네요. 모든 prismatic joint가 항상 위와같은 설정값을 가질 필요는 없으니까요. 함수 인자들은 joint definition의 인자와 거의 동일합니다. 단지 limit을 설정하기 위한 변수가 하나 더 추가 되었습니다.

prismatic joint에서 주의깊게 봐야할 부분은 axis 같네요. body가 움질일 축의 방향 벡터 입니다. 단위벡터일 필요는 없습니다. axis의 값이 x="1.0" y="0.0" 이면 x축의 우측 방향을 양의 방향으로 하는 prismatic joint를 생성한다는 의미이고, x="0.0" y="-1.0" 이면 y축의 위쪽을 양의 방향으로 하는 prismatic joint가 됩니다.

신경써야 할 또하나는, 두 body가 처음 joint로 엮이는 그 시점 두 위치가 prismatic joint의 translation값이 0인 지점으로 설정 된다는 것입니다. 그래서 joint를 생성할 당시의 body 위치가 중요합니다.

function createSpring(px:Number, py:Number, w:Number, h:Number):void

{

var b1:b2Body = createBox(px, py-h/2, 5, h, 0, false);

var b2:b2Body = createBox(px, py-h, w, h-10, 0, true);

var anchor:b2Vec2 = new b2Vec2(px/scale,py/scale);

var axis:b2Vec2 = new b2Vec2(0,-1);

createPrismaticJoint(b1,b2,anchor,axis,h-10);

}


이 함수는 위에서 살펴본 createPrismaticJoint를 이용해서 스프링 형태의 물체를 만들어 내는 함수 입니다.  아래에는 static body를 만들고 그 위에 dynamic body를 올리는 형태입니다. axis는 y축 위 방향으로 설정하였습니다.

이 함수에서 매개변수 w는 스프링의 상단 dynamic body의 가로 길이가 되고, h는 static body의 세로 길이가 됩니다.

그렇게 해서 위의 함수를 이용해서 화면의 바닥을 가득 채우는 스프링을 만들어줍니다. 아래 코드는 생성자 함수의 마지막 부분에 들어가면 되겠죠?

for (var i:int = 0 ; i < 10 ; i++) {

createSpring(25+50*i, 400, 48, 30);

}


스프링들이 딱 맞닿아 있으면 마찰때문에 다같이 영향을 받기 때문에 간격은 50으로 하되, 스프링의 가로 길이는 48로 설정했습니다. 최종 소스를 첨부하겠습니다.

'programming > box2d docs' 카테고리의 다른 글

b2PulleyJoint 예제  (0) 2011.02.01
b2Revolute Joint 예제  (0) 2011.01.30
b2DistanceJoint 예제  (0) 2011.01.28
b2Body 예제 - Box2DFlash  (4) 2011.01.17
Box2DFlash v2.1a Update Notes 비공식 한글문서  (0) 2011.01.16

WRITTEN BY
buzzler

,
경첩이나 진자운동같은 형태를 만들기 위해 필요한 joint type입니다. 즉, 원운동에 필요한 joint. 이전에 했던 distance joint와는 다른점이.. 두 body 사이의 길이가 고정 이라는 점. 즉 무거운 물체가 두 물체 사이에 힘을 가한다고 해도 body 사이의 간격이 달라지지 않죠. 게다가 joint는 motor 역할을 시킬수도 있습니다. 그래서 자동차 바퀴가 돌아가는것이나 선풍기날같은 원운동을 하는 자동화된 모터가 되기도 합니다.

이번 예제해서는 revolute joint를 응용해서 진자 운동을 하는 '추' 와 조금 더 응용된 '밧줄'을 만들어보겠습니다. 만들면서 cut the rope가 생각나더군요.
마우스 액션은 없지만 이전의 예제들보다 한층 더 동적인 모습입니다. 밧줄처럼 보이는 것들은 사실 세로로 길죽한 네모를 여러개 붙인 모양이구요. 밧줄의 두께가 너무 얇으면 밧줄끼리 서로 뚫고 지나가서 엉키더군요. 아마 그런 것들을 방지하기 위해서 tracing을 할 수 있게 제공하긴 하지만, cpu사용량이 엄청나게 늘어나기 때문에 이 옵션을 켜는것은 무모한 생각이 듭니다.

소스를 들여다보기 전에.. DebugDraw나 b2Body를 생성하는 방법에 대한 내용은 다루지 않고 지나갈 예정입니다. 이 부분에 대한 자세한 코드나 활용법이 궁금하신분은 같은 programming 카테고리에 있는 User Manual예제코드를 보시면 됩니다.

function createRevoluteJoint(b1:b2Body, b2:b2Body, a:b2Vec2):b2Joint

{

var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();

jointDef.Initialize(b1, b2, a);

jointDef.collideConnected = false;

jointDef.enableLimit = false;

jointDef.enableMotor = false;

return world.CreateJoint(jointDef);

}


joint를 만들어 주는 함수입니다. joint 또한 definition 클래스를 먼저 만들고 world 클래스의 factory 함수를 통해서 만들죠. 따라서 개발자가 직접 Joint를 생성할 필요가 없습니다.

모든 joint의 설정값이 다 다를 수 있기 때문에 이같이 함수에 따로 묶어두는것이 바람직한 방법은 아닙니다. revolute joint는 기준점에서 body가 서로 이루는 각도를 제한할 수도 있으며 회전운동을 시킬 수 있는데 이같은 속성들을 하나씩 바꿔가면서 생성하려면 좀더 구체적인 방법이 필요할 것입니다. 이 예제는 revolute joint를 사용하는 모든 body가 같은 설정값을 가진다는 가정으로 제작되었습니다. 즉 회전 각도의 한계값은 적용되지 않고 (enableLimit) 또한 자동적으로 회전하는 motor joint도 없습니다(enableMotor). 그리고 각 body들은 서로 충돌하지 않도록 간섭을 피했습니다(collideConnected).

function createBox(px:Number, py:Number, w:Number, h:Number, angle:Number, isDynamic:Boolean):b2Body;


function createCircle(px:Number, py:Number, r:Number, isDynamic:Boolean):b2Body;


네모 상자와 원형 body를 만들어 주는 함수들입니다. 설명은 생략하겠습니다.

다음의 소스는 '진자'를 만들어주는 함수입니다. 진자는 자명종 시계의 추를 생각하시면 되죠. 마우스 액션이 따로 없는 대신 추의 시작 각도를 정할 수 있게 만들어봤습니다.

function createPendulum(px:Number, py:Number, r:Number, h:Number, angle:Number):void

{

var pivot:b2Body = createCircle(px, py, 1, false);

var _radian:Number = angle/180*Math.PI;

var _x:Number = Math.sin(_radian)*h;

var _y:Number = Math.cos(_radian)*h;

var circle:b2Body = createCircle(px+_x, py+_y, r, true);


createRevoluteJoint(pivot, circle, pivot.GetWorldCenter());

}


추가 고정될 위치를 먼저 잡습니다. joint는 반드시 두 body가 연결되어야하기 때문에 편의상 여기서는 반지름 1의 원을 하나 만들었습니다. 그리고 추는 고정되어야 하므로 static body 로 만들었구요.

그다음은 매개변수로 입력받은 초기 추의 각도를 기반으로 추가 생성될 좌표를 계산하는 코드입니다. 원운동의 좌표는 삼각함수로 미리 계산해 낼 수 있겠죠. cos 함수와 sin 함수 두개의 조합으로 원운동 좌표를 계산했습니다. 물론 radian 단위로 사용하기 때문에 degree->radian 변환도 했구요.

마지막으로 앞서 보여드린 소스중 revolute joint를 사용하는 코드입니다. 위에서 생성된 두 body와 기준 body의 중심점을 넘김으로써, 두 body가 revolute joint에 의해 엮입니다. 이렇게 하면 추는 중력의 영향을 받아서 위치변화량이 운동량으로 바뀌면서 진자 운동을 시작하겠네요.

function createRope(px:Number, py:Number, length:Number, thick:Number, seg:int = 10):void

{

var body1:b2Body = createBox(px, py, 1, 1, 0, false);

var body2:b2Body;

var pos:b2Vec2 = new b2Vec2(px, py);

var anchor:b2Vec2 = new b2Vec2(px/scale, py/scale);

var seg_w:Number = thick;

var seg_h:Number = length / seg;


for (var i:int = 0 ; i < seg ; i++) {

   body2 = createBox(pos.x,pos.y+seg_h/2,seg_w,seg_h,0,true);

   createRevoluteJoint(body1, body2, anchor);

   body1 = body2;

   pos.Set(pos.x, pos.y+seg_h);

   anchor.Set(pos.x/scale, pos.y/scale);

}

}


마지막 함수는 진자를 만드는 함수를 좀 더 응용해서 만들어본 밧줄 만드는 함수입니다. 시작위치와 밧줄의 길이, 두께, 촘촘함을 매개변수로 받아서 작은 box를 연결시켜줍니다.

위의 진자를 만드는 함수와 차이점이라면, 여러개의 body가 서로 꼬리를 물면서 revolute joint로 연결된다는 점입니다. 그리고 연결점은 기준 body의 중심점이 아닌 가장 꼬리부분으로 설정했습니다. 그렇게 해야 여로 엮여 있는 느낌이 나겠죠.

이런 joint를 만들면서 실수하기 쉬운부분은 바로 scale입니다. anchor point들은 화면 좌표계가 아닌 world 의 좌표계 이므로 픽셀 좌표로 보기 편하게 작업하던 방식과 혼돈하면 안됩니다. 그래서 포지션은 픽셀 단위로 사용하고 anchor point는 world 좌표계로 변환해서 사용했습니다. 이는 각 view와 world의 비율에 따라서 얼마든지 달라져야 하므로 개발시 상황에 맞게 튜닝 되야겠네요.

이렇게 만들어진 함수들을 초기화 함수에서 절절하게 호출해주면 이번 예제와 같은 모습이 나옵니다. 이 예제는 500 x 400 기분으로 ..

createPendulum(150, 0, 50, 350, -90);

for (var i:int = 1 ; i <= 4 ; i++) {

createRope(150+50*i, 0, 400, 10, 7);

}


이렇게 초기화 시켰습니다.

전체 소스를 첨부시키고 다음에..

'programming > box2d docs' 카테고리의 다른 글

b2PulleyJoint 예제  (0) 2011.02.01
b2PrismaticJoint 예제  (0) 2011.02.01
b2DistanceJoint 예제  (0) 2011.01.28
b2Body 예제 - Box2DFlash  (4) 2011.01.17
Box2DFlash v2.1a Update Notes 비공식 한글문서  (0) 2011.01.16

WRITTEN BY
buzzler

,

Body의 사용에 이어서 Joint를 만들어 보려고 합니다. Joint는 다양한 형태가 있습니다만.. 일단 User Manual 의 첫번째로 나왔던 Distance Joint부터. 이름에서부터 대강 어떤 역할을 하는지 알 수 있는데요, 두 Body를 일직선으로 연결하는 역할을 합니다. 그 연결성에 있어서 몇가지 속성을 적용할 수 있는데요.. 그 수치에 따라서 고무줄로 연결한 느낌이 나기도 하고 용수철같은 느낌이 나기도 합니다. 추가적으로 그 사용 예제 뿐만 아니라 몇가지 다른 형태의 Shape들도 만들겠습니다.


먼저 완성된 예제를 보겠습니다.

딱히 꾸며지진 않았지만 Distance Joint가 뭔지 감은 잡힐 겁니다. 나중에는 DebugDraw가 아닌 직접 만든 무비클립과 맵핑하면 그럴싸한 모습이 되겠네요.(저도 아직 연습해보는 단계라서 무비클립과의 맵핑은 현단계에서 해본적이 없습니다)


마우스 클릭으로 생성될 body는 circle입니다. 그리고 두 static body를 연결하는 다리를 만들었는데요, 다리의 양쪽 끝은 static type이고 중간의 흔들리는 발판은 모두 dynamic입니다. 만약 중간의 다리도 static type이라고 하면 이건 joint로 엮어봤자 전혀 제 역할을 하지 못할 것입니다. 메모리 낭비일 뿐이죠.


이 예제는 이전의 b2Body 예제에서 함수를 몇가지 추가한 형태입니다. 풀소스는 가장 마지막에 춤부할 것이고, 설명은 핵심 부분만 하겠습니다.


private function setBg():void {

var bg:Sprite = new Sprite();

bg.graphics.beginFill(0xDDDDDD);

bg.graphics.drawRect(0,0,500,400);

bg.graphics.endFill();

addChild(bg);

}


스테이지 크기와 동일한 Sprite객체를 만들어서 화면에 붙입니다.

이 부분은 이전 소스에 없던 배경 오브젝트를 생성하는 코드입니다. 단순히 블로그에 예제가 올라갈때 IE에서 wmode가 tranparent인 경우 배경이 없으면 stage의 MouseEvent.CLICK 이벤트가 발생하지 않는 이유 때문에 임의로 집어 넣었습니다. 파이어폭스나 사파리, 크롬의 경우는 아무 지장 없습니다.


다음은 이전에 만들었던 createBox 함수와 동일한 createPolygon 입니다. 중심점을 기준으로 다각형 body를 만들어 줍니다. 대신 shape를 만드는 방법이 조금 다를것입니다.


private function createPolygon(px:Number, py:Number, r:Number, numPoly:int, isDynamic:Boolean):b2Body

{

var _tempBody:b2Body;

var _tempBodyDef:b2BodyDef = new b2BodyDef();

var _tempFixtureDef:b2FixtureDef = new b2FixtureDef();

var _tempPolygonDef:b2PolygonShape = new b2PolygonShape();

var _tempVertices:Array = [];

for (var i:int = 0 ; i < numPoly ; i++) {

var vertex:b2Vec2 = new b2Vec2();

var angle:Number = (360 / numPoly * i) / 180 * Math.PI;

vertex.x = Math.cos(angle) * r;

vertex.y = Math.sin(angle) * r;

_tempVertices.push(vertex);

}

_tempPolygonDef.SetAsArray(_tempVertices);

_tempFixtureDef.shape = _tempPolygonDef;

_tempFixtureDef.friction = 0.5;

_tempFixtureDef.density = 1;

_tempBodyDef.position.Set(px / world_scale, py / world_scale);

_tempBodyDef.type = isDynamic ? b2Body.b2_dynamicBody:b2Body.b2_staticBody;

_tempBody = world.CreateBody(_tempBodyDef);

_tempBody.CreateFixture(_tempFixtureDef);

return _tempBody;

}


기존의 SetAsBox 함수를 통해서 간단하게 shape을 만들어냈던것과 반대로, 어떠한 형태로든 다각형을 만들기 위해서 각 꼭지점의 배열로 그 형태를 만들어 내고 있습니다. 이 경우 아시다시피 다각형의 각 꼭지점의 내각이 180도를 넘어서는 안되구요.

이 함수는 원점을 기준으로 필요한 갯수만큼의 다각형을 만드는데 모두 정 다면체가 됩니다. 삼각함수를 통해서 원점을 기준으로 돌기 때문이죠.

만드는김에 원형 shape를 만드는 함수까지 가볍게 넣어 봤습니다.


private function createCircle(px:Number, py:Number, r:Number, isDynamic:Boolean):b2Body

{

var _tempCircleDef:b2CircleShape = new b2CircleShape(r);

var _tempFixtureDef:b2FixtureDef = new b2FixtureDef();

var _tempBodyDef:b2BodyDef = new b2BodyDef();

var _tempBody:b2Body;

_tempFixtureDef.shape = _tempCircleDef;

_tempFixtureDef.friction = 0.5;

_tempFixtureDef.density = 1;

_tempBodyDef.position.Set(px / world_scale, py / world_scale);

_tempBodyDef.type = isDynamic ? b2Body.b2_dynamicBody:b2Body.b2_staticBody;

_tempBody = world.CreateBody(_tempBodyDef);

_tempBody.CreateFixture(_tempFixtureDef);

return _tempBody;

}


원을 만드는것은 딱히 설명드릴게 없네요. 반지름과 원점만 필요하다는 정도는 누구나 알고 계실테니.

위에 잡설이 길었습니다만, 이제 Distance Joint를 생성하는 함수를 들여다 보겠습니다. 일단 함수 전체 소스는...

private function createBridgeByDistanceJoint(body1:b2Body, body2:b2Body, seg:uint = 0, r:Number = 0.3):void

{

var _anchor1:b2Vec2 = body1.GetWorldCenter();

var _anchor2:b2Vec2 = body2.GetWorldCenter();

var _tempBody1:b2Body;

var _tempBody2:b2Body;

var _tempAnchor1:b2Vec2;

var _tempAnchor2:b2Vec2;

var _tempJoint:b2DistanceJoint;

var _tempJointDef:b2DistanceJointDef = new b2DistanceJointDef();

var _tempBodies:Vector.<b2Body> = new <b2Body>[body1];

var _tempAnchors:Vector.<b2Vec2> = new <b2Vec2>[_anchor1];

var _tempVector:b2Vec2 = new b2Vec2();

var _tempGap:b2Vec2 = new b2Vec2();

_tempGap.x = (_anchor2.x-_anchor1.x) * world_scale / (seg+1);

_tempGap.y = (_anchor2.y-_anchor1.y) * world_scale / (seg+1);

for (var i:uint = 1 ; i <= seg ; i++) {

_tempVector.x = _anchor1.x*world_scale + _tempGap.x*i;

_tempVector.y = _anchor1.y*world_scale + _tempGap.y*i;

_tempBody1 = createPolygon(_tempVector.x, _tempVector.y, r, 3, true);

_tempBodies.push(_tempBody1);

_tempAnchors.push(_tempBody1.GetWorldCenter());

}

_tempBodies.push(body2);

_tempAnchors.push(_anchor2);

for (var j:int = 0 ; j < (_tempBodies.length-1) ; j++){

_tempBody1 = _tempBodies[j];

_tempBody2 = _tempBodies[j + 1];

_tempAnchor1= _tempAnchors[j];

_tempAnchor2= _tempAnchors[j + 1];

_tempJointDef.Initialize(_tempBody1, _tempBody2, _tempAnchor1, _tempAnchor2);

_tempJointDef.collideConnected = false;

_tempJointDef.length = _tempGap.Length() / world_scale;

//_tempJointDef.dampingRatio = 0.5;

//_tempJointDef.frequencyHz = 10;

_tempJoint = world.CreateJoint(_tempJointDef) as b2DistanceJoint;

}

}


변수선언이 너무 많네요. 필요한 부분만 뽑아서 설명해보죠.

Joint또한 JointDef 객체를 통해서 world 클래스의 팩토리 함수를 통해서 Joint가 생성됩니다. 앞으로도 직접 Joint를 만들 일은 없을겁니다.

이 함수에서는 매개변수로 전달받은 두 body의 world 좌표계상의 중심점을 기준으로 joint들을 만들어 냅니다. 사실 중심점을 이용하는것이 아니고 두 body의 가까운 쪽의 점을 연결하는것이 맞을텐데.. 귀찮네요 -_-
아무튼 각 중심점들을 기준으로 필요한 만큼의 물체를 dynamic type으로 만들어서 연결해냅니다.

var _tempBodies:Vector.<b2Body> new <b2Body>[body1];

var _tempAnchors:Vector.<b2Vec2> = new <b2Vec2>[_anchor1];

var _tempVector:b2Vec2 = new b2Vec2();

var _tempGap:b2Vec2 = new b2Vec2();

_tempGap.x = (_anchor2.x-_anchor1.x) * world_scale / (seg+1);

_tempGap.y = (_anchor2.y-_anchor1.y) * world_scale / (seg+1);

for (var i:uint = 1 ; i <= seg ; i++) {

_tempVector.x = _anchor1.x*world_scale + _tempGap.x*i;

_tempVector.y = _anchor1.y*world_scale + _tempGap.y*i;

_tempBody1 = createPolygon(_tempVector.x, _tempVector.y, r, 3, true);

_tempBodies.push(_tempBody1);

_tempAnchors.push(_tempBody1.GetWorldCenter());

}

_tempBodies.push(body2);

_tempAnchors.push(_anchor2);


자 이부분을 보시면.. 연결될 모든 body들과 그 body의 중심을 생성하고 배열에 담아내는 일들을 합니다. 나중에 한꺼면에 각각의 joint를 만들어내기 위해서죠. 굉장히 복잡해 보이지만 하는일은 단지 매개변수로 넘겨받은 body 사이에 필요한 만큼의 body를 추가로 생성하는 것. 대신 그 각각의 좌표들은 균일하게 나눠져 있어야 겠죠. 그 좌표를 알아내기 위해서 기존 world의 스케일 비율을 곱해서 좌표를 뽑아냅니다. createBox 함수는 좌표를 받을때 world_scale을 고려하지 않은 픽셀좌표계 기준의 좌표를 받기 때문이죠. getWorldCenter 함수로 넘겨받는 좌표는 스케일링이 되어있어서 픽셀 좌표계와 전혀 다릅니다. 이렇게 굳이 어렵게 진행된 이유는 물리엔진은 픽셀과 상관없는 미터법을 사용하기 때문.


_tempJointDef.Initialize(_tempBody1, _tempBody2, _tempAnchor1, _tempAnchor2);

_tempJointDef.collideConnected = false;

_tempJointDef.length = _tempGap.Length() / world_scale;

//_tempJointDef.dampingRatio = 0.5;

//_tempJointDef.frequencyHz = 10;

_tempJoint = world.CreateJoint(_tempJointDef) asb2DistanceJoint;


Initialize 함수를 통해서 필요한 초기화를 하나본데요.. 왜 생성자에서 안받고 따로 호출하는 식인지는 모르겠네요. 가능성이야 여럿 있지만. 어쨌든, 여러 다른 옵션들을 설정하기도 합니다. 연결된 두 body가 서로 충돌하게 할것인지, 연결될 길이는 어느정도로 할지, 연결성질이 단단한지 등 이러한 속성들에 따라서 보는사람이 느끼는 연결 성질이 천지차이가 되겠네요.


length, dampingRatio, frequencyHz 등등을 바꾸면서 테스트를 하고 감을 잡아두는게 좋겠습니다.


마지막으로 생성하는것은 앞서 설명했던바와같이 world 객체를 통해서 생성됩니다. joint는 눈에 보이는 물체가 아니고 단지 두 물체간의 관계를 정의하기 때문에 DebugDraw를 통해서 그 연결성이 보이진 않습니다. 물체간의 인터렉션으로만 확인 가능하죠. 지금까지 만든 소스를 첨부하겠습니다.


다 적고 나니 뭔가 대단하지 않은 내용으로 주절주절 말이 많았던것 같네요. 다음부터는 joint들 여럿을 모아서 예제로 만들어보겠습니다. 씨유쑨.


WRITTEN BY
buzzler

,
2.1a 버전의 메뉴얼이 없어서 적당한 예제 문서가 없다보니 일단 간단하게나마, 각 항목별 예제 코드들을 모아보려고 합니다. 첫번째 예제는 body를 만드는 예제부터.. 만드는 절차는 필요 항목등 세세한 내용은 이미 User Manual 번역문에서 상세히 다뤘으니 못보신 분들은 그쪽을 먼저 참고 하시면 됩니다. (링크는 생략)

이번에 만들게 될 예제입니다.

마우스를 클릭해보시면 사각형이 만들어지고 중력을 받아 자유낙하하는 모습을 보실 수 있습니다. 핑크색 사각형은 깨어있는 상태의 body이고, 이는 시뮬레이션중인 상태라고 생각하시면 됩니다. 시뮬레이션이 필요없는 경우는 흑색으로 바뀝니다. sleep 상태라고 하죠. sleep 상태에서는 성능향상을 위해서 시뮬레이션 스탭에서 제외됩니다. 물론 다른 body의 영향을 받아 깨어날 수 있습니다. 마지막으로 녹색의 사각형들은 static type의 사각형이라고 보면 됩니다. 이는 움직이지 않고 스스로 어떠한 시뮬레이션도 하지 않는 물체라고 보시면 됩니다.

게임에서 빗대어 말하면 static은 지형이나 레벨일 것이고, dynamic은 캐릭터나 총알같은 움직이고 물리적 영향을 받는 것이라고 생각하면 편하겠네요.

이제 소스코드를 들여다 보겠습니다. 액션스크립트 프로젝트를 만들어 메인 클래스가 있을 것입니다. 사이즈는 500 x 400 에 framerate는 30 이라고 하겠습니다. 소스는 메인 클래스 1개 이외의 커스텀 클래스는 없다고 생각하시면 됩니다.

package {

import flash.display.Sprite;

[SWF(width="500", height="400", framerate="30")]

public class MyFirstBox2D extends Sprite {

public function MyFirstBox2D() {

super();

}

}

}


모든 시뮬레이션은 b2World 클래스 객체 단위로 이루어지므로 멤버 변수로 world 객체를 중력 벡터와 함께 선언해줍니다. 또한, 이번 예제에서는 무비클립같은 것들과 완전히 별개로 시뮬레이션만 느껴보기 위해서 디버그 드로잉을 사용할 것입니다. 이는 shape들을 시스템이 임의대로 생성해서 가시적으로 볼 수 있도록 해주는 기능입니다. 원래의 Box2D는 순수하게 물리 시뮬레이션만을 합니다. 때문에 화면에 그려지는것에 대한 것들은 전부 개발자의 몫입니다. 하지만 이 예제에서는 이 부분을 간편하게 디버그 드로잉에 의존할 뿐입니다.

private var world:b2World = new b2World(new b2Vec2(0, 20), true);

private var world_scale :Number = 30;


이렇게 메인 클래스의 멤버 변수로 두개를 선언 했습니다. 중력은 y축으로 20 인 벡터이고 sleep이 가능하게끔 설정했습니다. 또하나의 변수는 디버그 드로잉에 필요한 스케일값입니다. 드로잉이 아닌 시뮬레이션만 담당하는 라이브러리답게 Box2D는 내부적으로 미터(meter)법을 사용하기 때문에 실제로 필셀단위의 디스플레이와 정확하게 매칭 되지 않습니다. 이 스케일값은 디버그 디로잉에만 필요한 값이라고 생각하면 되겠습니다.

다음은 디버그 드로잉을 설정하는 초기화 함수입니다.

private function init():void {

var debug_draw:b2DebugDraw = new b2DebugDraw();

var debug_sprite:Sprite = new Sprite();

debug_draw.SetSprite(debug_sprite);

debug_draw.SetDrawScale(world_scale);

debug_draw.SetFlags(b2DebugDraw.e_shapeBit);

world.SetDebugDraw(debug_draw);

addChild(debug_sprite);

}


이 소스는 2.1a 소스에 포함되어있던 소스코드의 일부이기도 합니다. 디버그 드로잉을 사용하는데에 여기에서 크게 달라질 것이 없어서 일단 동일하게 사용했습니다.

캔버스 역할을 하는 하나의 Sprite 객체를 화면에 붙이고 그 객체를 디버그 드로잉 클래스에 등록하는 과정을 볼 수 있습니다. 앞서 선언했던 스케일값을 셋팅하는 코드도 포함되어있구요. Sprite 객체를 디버그 드로잉 클래스에 등록하고, 그 클래스 객체를 world 객체에 또 등록하네요. 이는 나중에 b2World.DrawDebugData 함수를 통해서 그려내기 위함입니다.

눈여겨볼만한 코드도 한 줄 또 있죠. b2DebugDraw.SetFlags 함수입니다. 이는 화면에 그려낼 정보를 셋팅하는 함수인데요.. b2DebugDraw 클래스의 레퍼런스 문서를 보시면 static 상수가 몇가지 정의되어 있는것을 보실 수 있습니다. 화면에 그릴 옵션들이죠. AABB를 그린다던지, 질량 중심을 그린다던지 그외 joint 나 컨트롤러같은것들까지 설정 할 수 있습니다. 이 예제에서는 body에 첨부된 shape만 그리는 것으로 셋팅되었습니다.

생성자에서 init 함수를 호출해줘야 합니다만 소스는 생략하겠습니다.

자 여기까지 했으면 일단 화면에 출력할 준비가 되었구요. 실제 출력하는 코드를 추가해 보겠습니다. 메인 클래스의 생성자에서 Event.ENTER_FRAME 이벤트 핸들러로 다음의 함수를 붙여줍니다.

private function onEnter(event:Event):void {

world.Step(1/30, 10, 10);

world.ClearForces();

world.DrawDebugData();

}


게임의 핵심 루프에 포함될 내용이 여기 있습니다. 바로 b2World.Step 함수인데요. 이는 world 내에 선언된 모든 정보를 바탕으로 지정해주는 시간만큼 반복해서 시뮬레이션 시키는 함수입니다. 사용자 메뉴얼에 time step을 보시면 더 상세한 내용을 보실 수 있습니다. 이 예제는 frameRate가 30이고 time step도 1/30 으로 지정했습니다. iteration은 10으로.

b2World.ClearForces 함수는 Step으로 시뮬레이팅 한 이후 호출하는 함수라고만 적혀있는데.. sub-step을 또 진행할 것이 아니라면 이 함수를 호출하라고 지시하는것을 보니 아마도 사용된 찌꺼기들 정리하는 느낌으로 보면 될것 같기도 하네요. 이것저것 뒤져보고 혹시 틀린거면 수정하겠습니다. 후훗. 마지막 함수는 말그대로 위에서 지정한 디버그 드로잉 객체에 그려주는 역할을 합니다. 첨부된 shape들을 그리도록 플래그를 설정했으니 아마도 아직까진 그려질것이 없는 상태겠네요.

여기까지는 아무것도 만들어진것도, 시뮬레이션 할 것도 없는 상황입니다. 단지 있다고 가정하고 화면에 그려주고 시뮬레이팅 할 환경만 만들었네요. 다음은 드디어 world 객체에 body를 만드는 함수입니다. world내에 필요한 포인터들은 모두 담겨 있으니 따로 body들의 포인터나 fixture의 포인터를 관리할 필요는 없겠습니다. 단지 b2World.CreateBody 함수의 호출로 만들어주면 world 객체가 내부적으로 관리하고 그려준다고 생각하면 됩니다.

private function createBox

(px:Number, py:Number, angle:Number,

w:Number, h:Number, isDynamic:Boolean):void

{

var _tempBody:b2Body;

var _tempBodyDef:b2BodyDef = newb2BodyDef();

var _tempFixtureDef:b2FixtureDef = newb2FixtureDef();

var _tempPolygonDef:b2PolygonShape = newb2PolygonShape();

_tempPolygonDef.SetAsBox(w/2/world_scale, h/2/world_scale);

_tempFixtureDef.shape = _tempPolygonDef;

_tempFixtureDef.friction = 0.5;

_tempFixtureDef.density = 1;


_tempBodyDef.position.Set(px / world_scale, py / world_scale);

_tempBodyDef.angle = angle / Math.PI * 2;

_tempBodyDef.type = isDynamic ? b2Body.b2_dynamicBody:b2Body.b2_staticBody;

_tempBody = world.CreateBody(_tempBodyDef);

_tempBody.CreateFixture(_tempFixtureDef);

}


사용자 메뉴얼에서도 나와있듯이, body는 world 객체를 통해서 생성합니다. 단지 그 CreateBody를 호출하기 위해선 definition을 지정해야 하고 그렇게 해서 만들어진 body 객체는 그 형태를 띄기 위해서는 fixture라고 하는 재질이나 속성을 다시 할당 해야 합니다. 그리고 기하적 정보는 fixture 객체의 안에 shape를 설정하는것으로 마무리 됩니다. 굉장히 복잡해 보이네요. 하지만 그나마 복잡한 실제 물체들을 시뮬레이팅 하기 위해서 속성을 추상화하는 구조체 치곤 심플하다고 할 수 있습니다.

나중에 다룰 joint와 constrain같은 것들이 추가되기 위해서 이런 구조를 필요로 한다고 생각하면 되겠죠. body를 말 그대로 신체라고 생각한다면 그 안에서 또 얼굴, 팔, 다리, 몸통 같이 세분화 해야할테구요. 그 각각은 속성이 달라질 수도 있으니까 말이죠.

어쨌든 이 함수에서는 생성하고자 하는 사각형의 좌표와 각도, 크기를 받아서 해당 위치에 원하는 크기로 생성해주고 마지막으로 필요한 경우 type을 dynamic 이나 static으로 설정해주는 역할을 합니다.

크기가 위치를 지정할 때 처음에 선언했던 world_scale 값을 꼬박꼬박 나눠서 사용하고 있는데 예상되다시피 디버그 드로잉을 할때 스캐일값만큼 키운것을 보상하기 위한 조치하고 생각하면 되겠습니다. 그 상관관계가 궁금하신 분들은 숫자를 이리 저리 바꾸면서 찾아보시면 되겠죠.

아! 빼먹을뻔 했네요. 잘 보시면 new 명령을 통햇 직접 생성하는 클래스는 Def들 밖에 없습니다. 실제 body와 fixture 는 world와 body에서 제공하는 함수를 통해서만 생성하신다고 생각하면 되겠네요.

이제 이 함수를 사용해서 지형을 만들고 마우스 클릭에 반응해서 동적으로 box들을 만들어보겠습니다. 마우스 클릭 이벤트를 처리할 함수입니다.

private function onMove(event:MouseEvent):void {

createBox(stage.mouseX,stage.mouseY,Math.random()*90, Math.random() * 35 + 15, Math.random() * 35 + 15,true);

}


플래시에 익숙하시다면 마우스 이벤트와 관련된 이야기는 굳이 말하지 않아도 되겠죠? 마우스 클릭이 있을때마다 마우스 위치에 임의의 크기와 각도록 dynamic body를 생성하는 코드입니다. 

그리고 다음은 지형을 만드는 코드죠. 이 코드는 생성자나 초기화 함수에 넣고 한번만 호출될 수 있도록 해야겠죠. 지형이 동적으로 생성될것이 아니라면.

createBox(250,200,0,100,10,false);

createBox(250,390,0,500,10,false);


보시다시피 마지막 매개변수가 false 입니다. static body로 만들라는거죠. static은 중력이나 다른 간섭을 받지 않습니다. 완전히 고정된 body가 됩니다.

자, 이제 최종 소스입니다.

package {

import Box2D.Collision.*;

import Box2D.Common.Math.b2Vec2;

import Box2D.Dynamics.*;


import flash.display.Sprite;

import flash.events.Event;

import flash.events.MouseEvent;

[SWF(width="500", height="400", framerate="30")]

public class MyFirstBox2D extends Sprite {

private var world:b2World = new b2World(new b2Vec2(0, 20), true);

private var world_scale:Number = 30;


public function MyFirstBox2D() {

super();

init();

stage.addEventListener(MouseEvent.CLICK, onMove);

stage.addEventListener(Event.ENTER_FRAME, onEnter);

}

private function init():void {

// debug draw setting

var debug_draw:b2DebugDraw = new b2DebugDraw();

var debug_sprite:Sprite = new Sprite();

debug_draw.SetSprite(debug_sprite);

debug_draw.SetDrawScale(world_scale);

debug_draw.SetFlags(b2DebugDraw.e_shapeBit);

world.SetDebugDraw(debug_draw);

addChild(debug_sprite);


//ground body (static)

createBox(250,200,0,100,10,false);

createBox(250,390,0,500,10,false);

}

private function onMove(event:MouseEvent):void {

createBox(stage.mouseX,stage.mouseY,Math.random()*90, Math.random() * 35 + 15, Math.random() * 35 + 15,true);

}


private function onEnter(event:Event):void {

world.Step(1/30, 10, 10);

world.ClearForces();

world.DrawDebugData();

}

private function createBox(px:Number, py:Number, angle:Number, w:Number, h:Number, isDynamic:Boolean):void {

var _tempBody:b2Body;

var _tempBodyDef:b2BodyDef = new b2BodyDef();

var _tempFixtureDef:b2FixtureDef = new b2FixtureDef();

var _tempPolygonDef:b2PolygonShape = new b2PolygonShape();

_tempPolygonDef.SetAsBox(w/2/world_scale, h/2/world_scale);

_tempFixtureDef.shape = _tempPolygonDef;

_tempFixtureDef.friction = 0.5;

_tempFixtureDef.density = 1;


_tempBodyDef.position.Set(px / world_scale, py / world_scale);

_tempBodyDef.angle = angle / Math.PI * 2;

_tempBodyDef.type = isDynamic ? b2Body.b2_dynamicBody:b2Body.b2_staticBody;

_tempBody = world.CreateBody(_tempBodyDef);

_tempBody.CreateFixture(_tempFixtureDef);

}

}

}



WRITTEN BY
buzzler

,
Fixtures
가장 큰 변화들 중 하나는 어떻게 shape이 동작하는가 이다. density(밀도), friction(마찰) 같은 shape의 제질 속성은 기하 속성(radius, vertice..)들과 분리되었다.

body를 만든 이후, material(재질) 속성을 위한 fixture definition을 생성해야 한다. fixture에 shape를 설정하고 b2Body.CreateFixture 를 호출한다.
1 var fd:b2FixtureDef = new b2FixtureDef();
2 fd.density = 1.0;
3 fd.shape = new b2CircleShape(5.0);
4 var fixture:b2Fixture = myBody.CreateFixture(fd);
편의를 위해서, b2Body.CreateFixture2 함수는 shape와 density를 직접 매개변수로정하여 b2FixtureDef를 생성하는것을 생략할 수 있게 해준다. 
1 var fixture:b2Fixture = myBody.CreateFixture2(newb2CircleShape(5.0), 1.0);
b2CircleShape와 b2PolygonShape는 직접적으로 사용됨을 메모하자. 그것들은 b2ShapeDef가 필요없다.

Body types
2.0 버전에서, static, dynamic 두 종류의 body가 있었다. body가 static인지 아닌지에 따라 질량이 결정되어졌다. 이번 버젼에서는 body가 움직이지만 다른 body에 영향받지 않는 kinematic 이라는 새로운 타잎의 body가 추가되었다. 이를 사용하기 위해서, body의 타입을 b2BodyDef.type 과 b2Body.SetType을 사용하여 정확하게 설정해야만 한다.
1 var bd:b2BodyDef = new b2BodyDef();
2 bd.type = b2Body.b2_dynamicBody;

Events
2.0버전의 충돌 이벤트 시스템은 사용하기 매우 어려웠었고, 이는 새로 교체되었다. 이벤트는 contact point별로가 아닌, contact 단위로 발생된다. 그것들은 contact point를 포함한 많은 정보를 담고있다. BeginContact 와 EndContact는 두 물체의 충돌 시작과 끝의 시점을 쉽게 측정할수 있도록 제공되고, sensor에 의새서 발생되는 유일한 이벤트이다. 자세한 내용은 레퍼런스 문서를 참조하라.

Initialization
2.1버전은 전체적으로 완전히 제작성 되었다. world의 사이즈를 지정할 필요가 없어졌으며, 충분히 커질만큼 커질것이다.

Game loop
최종 버전에서는 변할지도 모르지만, b2World.Step이후 즉시 b2World.ClearForces 를 호출할 필요가 없어졌다. 게임로직상의 한 스탭에 여러번의 b2World.Step을 사용하길 원하는 사용자를 위해서 안정성에 기여하기위한 목적을 가진 기능이다. 또한 디버그 드로잉을 사용하고자 한다면 b2World.DrawBedugDate()를 호출할 필요가 있을지도 모른다.

Restrictions
몇몇 제약사항을 걷어냈다.
  • world 의 바운더리가 없어졌다 
  • polygon의 꼭지점 개수 제한이 없다. 원하는 만큼 얇게 만들 수도 있지만 여전히 convex(역자주:모든 꼭지점의 내각이 180도 미만)여야한다. 좋은 모서리 형태의 shape을 만들기 위해 단 두개의 꼭지점도 유효한 다각형으로 만들 수 있다.
  • 몇몇 내부적 제약도 사라졌다는 말은 더 이상 assertion failure 때문에 당황하지 않아도 된다는 의미이다.

WRITTEN BY
buzzler

,
8. Joints
8.1. About
joint는 body를 world에서 또는 body끼리 서로 제약하기 위해 사용된다. 게임에서 그 전형적인 예로 ragdoll, teeter, pulley 들이 포함된다. joint는 흥미로운 움직임을 만들기 위해서 다양한 방법으로 합쳐질 수 있다.

어떤 joint는 한계치를 제공하여 움직임의 범위를 제어할 수 있다. 어떤 joint는 정해진 force나 torque가 넘을때까지 정해진 속도로 돌아가도록 하기위해 모터를 제공한다

joint motor는 매우 다양한 방식으로 사용될 수 있다. 포지션을 제어하기 위해서 실좌표와 예정 좌표간의 차이에 비례하는 joint velocity를 지정하여 모터를 사용할 수있다. joint friction을 시뮬레이팅하기 위해서 모터를 사용할 수도 있다. joint velocity를 0으로 설정하고 작지만 매우 큰 최대 모터의 force나 torque를 제공한다. 그러면 모터는 joint를 load가 너무 커지기 전까지의 움직임으로부터 유지하려고 시도할 것이다.

8.2. The Joint Definition
joint 타입은 각각 b2JointDef으로부터 얻은 definition을 가지고 있다. 모든 joint들은 두 body 사이를 연결한다. 하나가 static이 될것이다. 만약 메모리를 낭비하고 싶다면, 두 static body사이에 joint를 생성하면 된다. : )

어떤 joint type에도 사용자 데이터를 설정할 수 있고 첨부된 body들끼리 서로 충돌하는것을 예방하기 위해서 플래그를 제공할 수 있다. 실제 이는 기본 기능이고 당신은 collideConnected boolean값을 연결된 두 body가 서로 충돌하길 허용하도록 설정 해야한다.

많은 joint definition은 당신이 기하 데이터를  제공하기를 필요로 한다. 종종 joint는 anchor point에 의해서 정의되기도 한다.  이것들은 첨부된 body에 고정된 점이다. Box2D는 로컬 좌표계에 명시되어지는 이러한 점들을 요구한다. 이리하여 joint는 현제 body transform이 (게임이 세이브되고 다시 로딩될때 공통적으로 발생하는) joint constraint를 벗어날 때 조차 지정될 수 있다. 추가적으로, 어떤 joint definition은 body들 사이의 기본 사잇각을 알기를 필요로한다. 이는 joint limit 이나 고정된 각도를 통해서 정확하게 회전을 제한하기 위해 필수이다.

기하 데이터를 초기화하는것은 귀찮을 수 있으므로 많은 joint는 현제 body transform을 제거하기 위해 사용되는 초기화 함수를 가지고 있다. 그러나 이러한 초기화 함수들은 일반적으로 프로토타이핑을 위해서만 사용되어야 한다. 제작 코드는 기하 데이터를 직접적으로 정의해야한다. 이것은 joint 기능을 더욱 탄탄하게 만들 것이다.

jooint definition의 나머지 데이타는 다음번에 다뤄질 joint type에 따라서 달라진다.

8.3. Distance Joint
가장 단순한 joint중 하나는 두 body에 있는 두점 사이의 거리를 제한해주는 distance joint이다. 두 body에 distance joint를 지정할때, 두 body는 이미 생성되어있어야한다. 그런다음 world 좌표계에 두 anchor point를 지정한다. 첫번째 anchor point는 1번 body에 연결되어지고, 두번째 anchor point는 2번 body에 연결된다. 이러한 포인트는 제한 거리의 길이를 의미한다.

distance joint definitiondml 예제가 있다. 이러한 경우 우리는 body들이 충돌하는것을 허용할지 결정한다.
1 var jointDef:b2DistanceJointDef = new b2DistanceJointDef();
2 jointDef.Initialize(myBody1, myBody2, worldAnchorOnBody1, worldAnchorOnBody2);
3 jointDef.collideConnected = true;

8.4. Revolute Joint
종종 hinge point(경첩) 라고 불리는 revolute joint는 두 body가 하나의 공동된 anchor point를 공유한다. revolute joint는 두 body의 상대적 회전의 자유로움에 있어서 단일 각도를 가진다. 이를 joint angle이라고 부른다.

revolute를 지정하기 위해서는 world에 존재하는 두개의 body와 하나의 anchor point를 필요로 한다. 초기화 함수는 body들이 정확한 포지션에 있는지 확인한다.

이 예제에서는, 두 body가 하나의 첫번째 body의 질량 중점의 revolute joint에 의해 연결되어있다.
1 var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
2 jointDef.Initialize(myBody1, myBody2, myBody1->GetWorldCenter());
body2가 angle point에 대해 반시계방향으로 회전할 때 revolute joint angle은 양수이다. Box2D의 모든 각도들과같이, revolute angle도 라디안값으로 측정한다. joint가 Initialize함수에 의해 생성될 때 관례적으로 현제 두 body의 회전에 상관없이 revolute joint angle은 0 이다.

어떤경우에는 joint angle을 제어하길 원할지도 모른다. 이런 경우, revolute joint는 부가적으로 joint limit 을 시뮬레이션하면서 동시에 motor로 시뮬레이션 할 수 있다.

joint limit은 joint angle을 최저, 최고 한계치 사이를 유지하도록 한다. limit은 이를 발생시키도록 만드는것이 필요로 한 만큼 많은 토크를 적용할 것이다. 그 한계 범위는 0을 포함하는 반면 joint는 시뮬레이션을 시작할 때 요동칠 것이다(lurch).

joint motor는 joint speed를 지정할 수 있게 해준다. 속도는 음수일 수도 있고 양수일 수도 있다. motor는 무한한 힘을 가질 수 있지만 이는 보통 바람직하진 않다. 다음과 같은 표현을 들어본 적 있을 것이다.

주의

"움직이지 않는 물체(immovable object)가 압도적인 힘(irresistible force)을 만나면 어떤 일이 벌어질까?"

이는 그다지 귀엽지 않다. 그래서 최대 토크를 joint motor에 제공할 수 있다. joint motor는 요구되는 토크가 지정된 최대치를 넘어가지 않는한 지정된 속도를 유지할 것이다. 최대 토크가 초과되는 순간, joint는 속도를 줄일 것이고 심지어 거꾸로 돌아갈 것이다.

joint friction을 시뮬레이팅 하기 위해서 joint motor를 사용할 수 있다. joint speed를 0으로 셋팅하고 최대 토크를 작은 값으로 두지만 중요한 값이다. 그 motor는 joint를 회전하지 못하게 시도할 것이지만 중요한 로드에 양보할 것이다.

여기에 위에서 말한 revolute joint 의 정의에 대한 수정안이 있다. 이 순간 joint는 limit과 활성화된 motor를 가지고 있다. 그 motor는 joint friction을 시뮬레이팅하기 위해 설정되었다.
1 var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
2 jointDef.Initialize(body1, body2, myBody1.GetWorldCenter());
3 jointDef.lowerAngle     = -0.5 * b2Settings.b2_pi; // -90 degrees
4  
5 jointDef.upperAngle     = 0.25 * b2Settings.b2_pi; // 45 degrees
6 jointDef.enableLimit    = true;
7 jointDef.maxMotorTorque = 10.0;
8 jointDef.motorSpeed     = 0.0;
9 jointDef.enableMotor    = true;

8.5. Prismatic Joint
prismatic joint는 특정 축을 따라서 두 body의 위치이동을 해준다(역자주: 미닫이문을 연상하면 된다). prismatic joint는 회전을 억제한다.

prismatic joint definition은 revolute joint 설명글과 매우 유사하다. 각도 대신 위치이동, 그리고 힘 대신 토크로 바꾸면 된다. 이러한 유사점을 이용하여 joint limit과 friction motor가 있는 prismatic joint definition 예제를 제공한다.
01 var worldAxis:b2Vec2 = new b2Vec2(1.00.0);
02  
03 var jointDef:b2PrismaticJointDef = new b2PrismaticJointDef();
04 jointDef.Initialize(myBody1, myBody2, myBody1.GetWorldCenter(), worldAxis);
05 jointDef.lowerTranslation= -5.0;
06 jointDef.upperTranslation= 2.5;
07 jointDef.enableLimit     = true;
08 jointDef.maxMotorForce   = 1.0;
09 jointDef.motorSpeed      = 0.0;
10 jointDef.enableMotor     = true;
revolute joint는 스크린밖으로 뻗은 내장된 축을 가지고 있다. prismatic joint는 화면과 평행하게 드러난 축이 필요하다. 이 축은 두 body에 고정되고 그들의 움직임을 따른다.
revolute joint 처럼 Initialize() 함수를 사용하여 joint가 생성될때 위치이동값은 0으로 셋팅된다. 그러므로 당신의 최저, 최대 위치이동값 사이에 0이 있어야 함을 명심하라.

8.6. Pulley Joint
pulley는 이상화된 도르래를 만들기 위해서 사용된다. 도르래는 두 body를 ground에서 각각의 body로 연결한다. 한 body가 올라가면 다른 하나는 내려온다. 도르래 줄의 총 길이는 초기 설정에 따라서 보존된다.

length1 + length2 == constant

block과 tackle을 시뮬레이팅하는 비율을 제공할 수 있다. 이는 도르래의 한쪽을 반대쪽보다 빠르게 팽창하는 것을 유발한다. 동시에 통제하는 힘은 첫번째 쪽이 다른곳보다 작아진다. 당신은 기계적인 지렛대를 생성하는것에 사용할 수 있다.

length1 + ratio * length2 == constant

예를들면, 비율이 2라면 length1은 legnth2보다 두배로 달라진다. 또한 body1에 첨부된 로프에 실리는 그 힘은 body2에 첨부된 로프에 실리는 constraint force 절반이 될 것이다.

pulley는 한쪽이 완전히 팽창했을때 골칫거리가 될 수 있다. 반대쪽의 로프는 길이가 0이 될것이다. 이 지점에 constraint 방정식은 단수가 된다. 그러므로 pulley joint는 양측이 나타낼 수 있는 최대 길이를 제한한다. 또한, 게임 플레이상의 이유로 최대 길이를 제어하길 원할지 모른다. 그래서 최대 길이는 안정성을 향상시키고 더 많은 제어권을 당신에게 줄 것이다.

pulley definition의 예제이다.
01 var anchor1:b2Vec2 = myBody1.GetWorldCenter();
02 var anchor2:b2Vec2 = myBody2.GetWorldCenter();
03 var groundAnchor1:b2Vec2 = new b2Vec2(p1.x, p1.y + 10.0);
04  
05 var groundAnchor2:b2Vec2 = new b2Vec2(p2.x, p2.y + 12.0);
06 var ratio:Number 1.0;
07  
08 var jointDef:b2PulleyJointDef = new b2PulleyJointDef();
09 jointDef.Initialize(myBody1, myBody2, groundAnchor1, groundAnchor2, anchor1, anchor2, ratio);
10 jointDef.maxLength1 = 18.0;
11 jointDef.maxLength2 = 20.0;

8.7. Gear Joint
정교한 메카닉 기계를 만들기 원한다면 gear를 사용하길 원할것이다.  Box2D에선 원칙적으로 기어 이빨 모델에 복합적인 shape를 사용함으로써 gear를 만들 수 있다. 이는 그리 효율적이지 않을뿐더러 제작하기 귀찮다. 또한 gear를 가지런히 해야 이빨이 부드럽게 맞물린다. Box2D는 gear(gear joint)를 만들기위한 간편한 함수를 가지고 있다.

gear joint는 경첩(revolute)이나 미닫이(prismatic) 형식의 joint에 의해서 ground body와 연결된 두 body를 필요로 한다. 저런 joint type들의 어떠한 조합이라도 사용할 수 있다. 또한, Box2D는 ground형태의 body1에서 생성된 revolute joint와 prismatic joint를 요구한다.

pulley ratio 와 같이, gear ratio를 지정할 수 있다. 하지만, 이경우 gear ratio는 음수가 될 수 있다. 또한 한 joint가 revolute joint이고 다른쪽은 prismatic joint라면 gear ratio는 단위 길이나 1이상의 길이를 가질 수 있다는것을 명심하라.

coordinate1 + ratio * coordinate2 == contant

gear joint의 예제이다
1 var jointDef:b2GearJointDef = new b2GearJointDef();
2 jointDef.body1 = myBody1;
3 jointDef.body2 = myBody2;
4 jointDef.joint1 = myRevoluteJoint;
5 jointDef.joint2 = myPrismaticJoint;
6 jointDef.ratio = 2.0 * b2Settings.b2_pi / myLength;
gear joint는 두개의 다른 joint에 의존한다는것을 메모하자. 이는 허술한 상황을 만들어낸다. 만약 저런 joint들이 삭제되면 어떤일이 벌어질까?

경고

언제나 gear에 있는 revolute/prismatic joint보다 먼저 gear joint가 삭제하라. 그렇지 않으면 당신의 코드는 gear joint내에 엉뚱한곳을 참조하는 joint 포인터 때문에 나쁜 방향으로 망가져 버릴것이다. 관련된 body들 또한 gear joint보다 먼제 삭제되어서는 안된다.

8.9. Mouse Joint
mouse joint는 테스트베드에서 마우스로 body들을 다루기 위해 사용된다. 상세설명은 testbed와 b2MouseJoint.as 을 읽어보길 권한다.

8.9. Joint Factory
joint는 world factory 함수들을 이용해서 만들어지고 삭제된다. 이는 오래된 이슈를 떠올린다.

주의

body와 joint를 new 문법으로 직접 생성하려고 시도하지 말라. 반드시 b2World 클래스의 함수들을 통해서 생성하고 삭제해야한다.

이것은 revolute joint의 라이프타임 예제이다.
1 var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
2 jointDef.body1 = myBody1;
3 jointDef.body2 = myBody2;
4 jointDef.anchorPoint = myBody1.GetCenterPosition();
5  
6 var joint:b2RevoluteJoint = myWorld.CreateJoint(jointDef);
7 ... do stuff ...
8 myWorld.DestroyJoint(joint);
9 joint = null;
그것들을 삭제한 이후에 포인터를 null로 비워두는것이 좋다. 이는 포인터를 제사용하려 시도했을때 통제된 형식의 프로그램 충돌을 만들어낼 것이다.

joint의 라이프타임은 간단하지 않다. 이런 경고를 잘 새겨들어라:

주의

joint는 첨부된 body가 삭제될때 같이 삭제된다.

이 예방조치가 항상 필수인것은 아니다. 당신은 게임 엔진을 조직화하여 body 전에 joint 가 항상 삭제되게끔할지도 모른다. 이런경우 당신은 리스너 클래스를 구현할 필요가 없다. 자세히 알고싶다면 10.2장 Implicit Destruction 을 보라.

8.10. Using Joints
많은 시뮬레이션은 joint들을 만들고 삭제될때까지 다시 접근하지 않는다. 그러나 joint에 포함된, 질좋은 시뮬레이션을 만드는데 사용할 수 있는 유용한 데이타가 많이 있다.

그중 첫 번째로 joint로 부터 body, anchor point, 사용자 데이터들을 얻어올 수 있다.
1 GetBody1():b2Body;
2 GetBody2():b2Body;
3 GetAnchor1():b2Vec2;
4 GetAnchor2():b2Vec2;
5 GetUserData():*;
모든 joint들은 reaction force(반동력)와 reaction torque(반동회전력) 둘다 가지고 있다. anchor point에서 이것들은 body2에 적용되어진다. 반동력을 joint를 깨뜨리는데 사용하거나 다른 게임 이벤트의 트리거로 사용할 수 있다. 이런 함수들은 어느정도의 연산을 해야하므로 결과값이 필요한 경우가 아니라면 호출을 하지 않는것이 좋다.
1 function GetReactionForce():b2Vec2;
2 function GetReactionTorque():Number;

8.11. Using Distance Joints
distance joint는 motor나 limit를 가지고 있지 않아서 distance joint만을 위한 부가적인 런타임 함수가 없다.

8.12. Using Revolute Joints
revolute joint의 angle, speed, motor torque에 접근할 수 있다.
1 function GetJointAngle():Number;
2 function GetJointSpeed():Number;
3  
4 function GetMotorTorque():Number;
또한 각 step마다 motor 매개변수들을 갱신한다.
1 function SetMotorSpeed(speed:Number):void;
2  
3 function SetMaxMotorTorque(torque:Number):void;
joint motor는 흥미로운 능력을 몇가지 가지고 있다. joint speed를 매 time step마다 업데이트 할수 있어서 joint가 사이파처럼 왔다갔다 움직이게 만들 수 있고 또는 당신이 원하는 어떠한 함수라도 따르도록 만들 수 있다.
1 // ... Game Loop Begin ...
2 myJoint.SetMotorSpeed(Math.cos(0.5 * time));
3  
4 // ... Game Loop End ...
joint motor는 원하는 joint angle로 이끄는데 사용할 수도 있다. 예를들면..
1 // ... Game Loop Begin ...
2 var angleError:Number = myJoint.GetJointAngle() - angleTarget;
3 var gain:Number 0.1;
4 myJoint.SetMotorSpeed(-gain * angleError)
5  
6 // ... Game Loop End ...
일반적으로 얻어진 매개변수는 너무 덩치를 키우지 않아야 한다. 그렇지 않는다면 joint는 불안정해질 것이다.

8.13. Using Prismatic Joints
prismatic joint를 사용하는 것은 revolute joint의 사용과 유사하다. 관련된 멤버 함수들이 있다.
1 function GetJointTranslation() :Number
2 function GetJointSpeed():Number
3  
4 function GetMotorForce():Number
5 function SetMotorSpeed(speed:Number)
6 function SetMotorForce(force:Number)

8.14. Using Pulley Joints
pulley joint 는 현제 길이를 제공한다.
1 function GetLength1():Number
2 function GetLength2():Number

8.15. Using Gear Joints
gear joint는 b2Joint에 정의된 함수들 이외의 어떠한 정보도 제공하지 않는다.

8.16. Using Mouse Joints
mouse joint은 매 time step마다 타겟 지점을 갱신하면서 첨부된 body를 다룰 수 있다.

9. Contacts
9.1. About
contact은 shape들 사이에 충돌을 관리하기 위해서 Box2D에 의해 만들어진 객체를 말한다. 서로 다른 종류의 shape 사이에서 contact를 관리하기 위한 b2Contact로 부터 얻어진 다양한 종류의 contact들이 존재한다. 예를들면, polygon-polygon 충돌 관리를 위한 contact 클래스가 있고 circle-circle 충돌 관리를 위한 contact 클래스가 따로 있다. 이는 보통 그리 중요한 것은 아니지만 당신이 알고있으면 좋을것이라 생각한다.

아래에 요약된 contact 용어 용어집이 있다. 이 전문 용어는 Box2D에 한정되긴 하지만 다른 물리엔들들의 용어들에서도 유사함을 찾을 수 있을 것이다.

contact point
두 shape가 맞닿은 지점을 말한다. 사실적으로, 물체들의 겉면이 닿는 때에 물체의 영역을 넘어설지도 모른다. Box2D는 적은수의 포인트들로 근사치를 낸다. 
contact normal
shape1에서 shape2를 가리키는 단위 벡터이다.
contact separation
separation은 '관통'의 반대이다. shape들이 서로 겹칠때 separation은 음수가 된다.  차기 버전의 Box2D은 양수의 separation으로 contact 포인트들을 생성이 가능해짐으로써, contact 포인트들이 보고될 때 체크 표시를 하길 원하게 될지도 모른다.
normal force
Box2D는 contact solver를 반복적으로 사용하고 결과로 나온 contact point를 저장한다. 당신은 충돌강도를 표시하기위해  normal force를 안전하게 사용할 수 있을 것이다. 예를들면, 당신은 그 힘을 파손(breakage)용 트리거로 사용할 수 있으며, 적절한 충돌 효과음을 재생하는데 사용할 수 있다.
tangent force
tangent force는 contact solver의 마찰력 추산치이다.
contact IDs
Box2D는 한 time step에서 다음 time step을 위해 초기 추측치로 contact force 결과들을 제사용하려고 시도한다. Box2D는 time step동안 contact point를 대조하기 위해 contact ID들을 사용한다. 그 ID들은 한 contact point와 다른것들을 구별하기에 도움을 주는 기하학적 특징 색인들을 포함한다.

contact들은 두 shape의 AABB가 겹칠때 생성된다. 때로는 충돌 필터링이 contact들의 생성을 방지할 것이다. 때로는 Box2D는 충돌 필터링 되었음에도 불구하고 contact를 생성하는것을 필요로 한다. 이런 경우에는 발생시부터 충돌을 방지해주는 b2NullContact를 사용한다. 겹쳐있던 AABB들이 떨어지면 contact들은 삭제된다.

그래서, 당신은 (그들의 AABB가)닿지않은 shape들을 위해 성성된 contact들을 모으게 될지도 모른다. 자, 이는 정확하다. 이는 "닭 또는 달걀" 문제이다. 우리는 충돌을 측정하기 위해서 하나가 생성될떄 까지 생성된 contact 물체가 필요한지 모른다. shape들이 서로 닿지 않았다면 그 contact는 당장 지울수도 있고 또는 AABB들이 겹치는것이 끝날 떄까지 그냥 기다릴 수도 있다. Box2D는 후자를 취한다.

9.2. Contact Listener
b2ContactListener를 구현 함으로써 contact 데이터를 얻을 수 있다. listener는 contact point가 생성되었을 때, 한 time step이상 지속될 떄, 그리고 삭제될 때 알려준다. 두 shape는 여러개의 contact point를 가질 수 있다는 것을 명심하자.
01 public class MyContactListener extends b2ContactListener
02  
03 {
04     public override function Add(point:b2ContactPoint) : void
05     {
06         // handle add point
07  
08     }
09   
10     public override function Persist(point:b2ContactPoint) : void
11     {
12         // handle persist point
13  
14     }
15   
16     public override function Remove(point:b2ContactPoint) : void
17     {
18         // handle remove point
19  
20     }
21   
22     public override function Result(point:b2ContactResult) : void
23     {
24         // handle results
25  
26     }
27 };
주의

b2ContactListener 에 되돌려 받은 contact point들의 참조 포인터를 가지고 있지 않아야 한다. 대신에 당신의 buffer에 contact point 데이터의 사본을 만들어라. 아래 소개된 예제는 그 사용 예들 중 한 가지 방법이다.

계속 이어지는 physics는 sub-stepping을 사용하기 때문에 동일한 time step 안에서도 contact point는 생성되고, 사라지고를 반복한다. 이는 문제점이나 버그는 아니지만, 당신의 코드에서 이를 우아하게 활용 해야 한다.

contact point는 그것이 생성되고, 지속되고, 삭제될 때 즈각ㄱ적으로 이벤트가 발생한다. 이는 solver가 호출되기 이전에 발생하므로, b2ContactPoint 객체는 계산된 impulse를 포함하지 않는다. 그러나 그 contact point에 관련된 velocity 는 제공되므로, 그 contact point의 impulse를 측정할 수 있다. result listener 함수를 구현하면, solver가 호출된 이후 solid contact point를 위한 b2ContactResult 객체를 받을 것이다. 이러한 result 구조체는 sub-step impulse를 포함한다. 반복해서 말하지만, 반복적인 physics 로 한번의 b2World.Step 에서 각 contact point 당 다수의 result를 받을지도 모른다.

그것은 contact 콜백 안에서 physics world를 수정하는 게임 로직을 구현하도록 유도하는 것이다. 예들들면, 데미지를 받는 충돌과 관련된 캐릭터와 ridig body를 파괴하도록 시도하는 것들을 하는 것이다. 그러나 Box2D는 콜백에서 physics world를 수정하도록 허락하지 않는다. 왜냐하면 Box2D가 연산중인 객체를 삭제하여 잘못된 null 포인터를 만들어 낼 수 있기 때문이다.

contact point를 연산하기 위한 권장하는 연습법은, time step 이후에 당신이 사용하고 연산했던 모든 contact point를 buffer 하는것이다. time step 이후 contact point의 연산을 즉시 해야만 한다. 그렇지 않으면 클라이언트 코드의 다른 부분에서 contact buffer를 검증 하지 않고 physics world를 변경할지도 모른다. contact point buffer를 수정 할 수 있지만, contact point buffer에서 잘못된 포인터를 두지 않도록 조심해야 할 필요가 있다.

이 예제는 CollisionProcessing test 에서 발췌한것이고, contact buffer 연산시 잘못된 body들을 어떻게 처리할지를 보여주고 있다.

01 // We are going to destroy some bodies according to contact
02 // points. We must buffer the bodies that should be destroyed
03 // because they may belong to multiple contact points.
04 var k_maxNuke:int 6
05  
06 var nuke:Array new Array();
07 var nukeCount:int 0
08  
09   
10 // Traverse the contact results. Destroy bodies that
11 // are touching heavier bodies.
12 for each(var point:ContactPoint in contactPoints){
13     var body1:b2Body = point.body1.GetBody();
14     var body2:b2Body = point.body2.GetBody();
15     var mass1:Number = body1.GetMass();
16     var mass2:Number = body2.GetMass();
17  
18   
19     if(mass1 > 0.0 and mass2 > 0.0){
20         if(mass2 > mass1){
21  
22             nuke_body = body1
23         else {
24             nuke_body = body2
25         }
26         if (nuke.indexOf(nuke_body) == -1){
27  
28             nuke.push(nuke_body);
29             if(nuke.length == k_maxNuke)
30                 break
31         }
32     }
33  
34 }
35 // Destroy the bodies, skipping duplicates.
36 for each(var b:b2Body in nuke){
37     trace("Nuking:", b);
38     self.world.DestroyBody(b);
39  
40 }

9.3. Contact Filtering
게임에선 가끔 모든 물체들이 충돌하지 않도록 원할 것이다. 예를들면, 당신이 특정 캐릭터만 통과할 수 있는 문을 만들기를 원할지도 모른다. 이를 contact 필터링이라고 부른다. 왜냐하면 어떤 인터렉션은 필터에걸리기 때문이다.

Box2D는 b2ContactFilter 클래스를 구현함으로써 튜닝된 커스텀 contact 필터링을 보관하도록 허가한다. 그 클래스는 두 b2Shapepointer를 받는 ShouldCollide 함수를 구현하기를 권장한다. 그 함수는 shape들이 충돌해야 한다면 true를 반환한다.

ShouldCollide의 기본 구현은 shape 챕터의 필터링장에서 정의된 b2FilterData를 사용한다.
?
01 public class b2ContactFilter
02 {
03   
04  
05     /// Return true if contact calculations should be performed between these two shapes.
06     /// @warning for performance reasons this is only called when the AABBs begin to overlap.
07     public virtual function ShouldCollide(shape1:b2Shape, shape2:b2Shape) : Boolean{
08  
09         var filter1:b2FilterData = shape1.GetFilterData();
10         var filter2:b2FilterData = shape2.GetFilterData();
11   
12         if (filter1.groupIndex == filter2.groupIndex && filter1.groupIndex != 0)
13  
14         {
15             return filter1.groupIndex > 0;
16         }
17   
18         var collide:Boolean = (filter1.maskBits & filter2.categoryBits) != 0 && (filter1.categoryBits & filter2.maskBits) != 0;
19         return collide;
20     }
21  
22   
23     static public var b2_defaultFilter:b2ContactFilter = newb2ContactFilter();
24   
25 };

10. Loose Ends
10.1. World Boundary
당신은 body가 world의 AABB 밖으로 나갔을때  b2World가 당신에게 알려주는  b2BoundaryListener를 구현할 수 있다. 그 콜백을 받았을때, 당신은 body를 삭제하는것을 시도하지 말하야 하며 대신에 삭제와 에러 핸들링을 위해서 부모 actor에 표시를 해야한다. physics time step 이후엔, 당신은 그 이벤트를 처리해야한다.
01 public class b2BoundaryListener
02 {
03  
04   
05     /// This is called for each body that leaves the world boundary.
06     /// @warning you can't modify the world inside this callback.
07     public virtual function Violation(body:b2Body) : void{};
08  
09   
10 };
그럼 당신의 boundary listener의 객체를 world 객체에 등록할 수 있다. world의 초기화 도중에 당신은 이런 것들을 해야한다.
1 myWorld.SetListener(myBoundaryListener);
2 // Use SetBoundaryListener() in newer versions of Box2D

10.2. Implicit Destruction
Box2D는 참조의 숫자를를 세지 않는다. 그래서 body를 삭제하면 실제로 삭제되어버린다. 삭제된 body에 접근하는 포인터는 정의되지 않은 기능이다. 즉, 당신의 프로그램은 깨지거나 불타버릴 것이다. 이런 문제점들을 고치도록 돕기 위해서, debug build memory manager가 그 삭제된 항목에 FDFDFDFD로 채운다. 이는 어떤경우 좀 더 쉽게 문제접을 찾는데 도울 수 있다.

Box2D 항목을 삭제한다면, 그 삭제된 물체의 모든 참조들을 없애는것을 확실히 만드는데 도움이 될 것이다. 이는 항목에 단일 참조만 가지고 있을때 쉽다. 여러 참조들이 있다면, 그 원래 포인터를 감싸는 handle 클래스를 구현하길 고려할지도 모른다.

Box2D를 사용할때 빈번하게 많은 body, shape, joint들을 만들고 삭제할 것이다. 이런것들을 관리하는것은 Box2D에 의해 어느정도 자동화되어있다. body를 삭제하면 관련된 모든 shape, joint들도 자동적으로 삭제된다. 이를 implicit destruction이라고 부른다.

body를 삭제할때 모든 첨부된 shape, joint, contact들도 삭제된다. 이를 implicit destruction이라고 부른다. 저런 joint들과 contact에 연결된 어떤 body라도 깨어난다. 이런 절차는 보통 편리하다. 그러나 한가지 커다란 이슈를 눈치했어야 한다.

주의

body가 삭제될때, 그것에 첨부된 모든 shape, joint들은 자동적으로 삭제된다. 당신은 해당 shape, joint들을 가리키는 어떤 포인터들이라도 비워야 한다. 그렇지않으면, 그 프로그램은 그런 shape, joint들을 나중에 사용하러 접근을 시도하면 끔찍하게 죽을것이다.

joint 포인터들을 비우는것을 도와주기 위해서 Box2D는 당신이 구현하고 제공할 수 있는 b2WorldListener라는 listener 클래스를 당신의 world 객체에 제공한다. 그러면 그 world 객체는 joint가 완전히 삭제되려고 할 때 당신에게 알려줄 것이다.

implicit destruction은 많은 경우에 큰 편의를 제공한다. 또한 당신의 프로그램을 완전히 종료시키도록 만들어 줄 수도 있다. 당신의 코드 한곳에 shape와 joint를 가리키는 포인터들을 저장해둘지도 모른다. 이러한 포인터들은 관련된 body가 삭제되면 깨져버린다. 그런 상황은 종종 당신이 joint가 삭제된 body의 관리자에서 관리하지 않는 코드의 일부에 의해서 생성되기를 고려할 때 악화될 것이다. 예를들면, 테스트베드는 화면위의 인터렉티브한 body의 조작을 위해서 b2MouseJoint를 만들어 낸다.

Box2D은 implicit destruction이 발생할 때 어플리케이션에 알리기 위한 콜백 매카니즘을 제공한다. 이는 어플리케이션에 뒤엉킨 포인터들을 비워줄 기회를 준다. 이 콜백 메카니즘은 나중에 이 메뉴얼에서 다룰것이다.

b2World가 관련된 body가 섹제되어서 shape와 joint가 완전히 삭제될때 알리도록 해주는 b2DestructionListener를 구현할 수 있다. 이는 당신의 코드가 잘못된 포인터에 접근하는것을 막아주도록 도와줄 것이다.
01 public class b2DestructionListener
02  
03 {
04   
05     /// Called when any joint is about to be destroyed due
06     /// to the destruction of one of its attached bodies.
07     public virtual function SayGoodbyeJoint(joint:b2Joint) : void{};
08  
09   
10     /// Called when any shape is about to be destroyed due
11     /// to the destruction of its parent body.
12     public virtual function SayGoodbyeShape(shape:b2Shape) : void{};
13  
14   
15 };
destruction listener의 인스턴스를 world 객체에 등록할 수 있다. 이는 world 초기화동안에 해야한다.
1 myWorld.SetListener(myDestructionListener);
2 // Use SetDestructionListener() in newer versions of Box2D

11 Settings
11.1. About
Box2D에는 사용자 커스텀을 위한 b2Settings.as 라 불리는 소스파일이 포함되어있다.

Box2D는 소주점 숫자들로 작동한다, 그래서 Box2D가 잘 동작하도록 만들기 위해서 tolerance(저항력)들이 사용되어져야 한다.

11.2. Tolerances
MKS 단위계를 사용하는것에 의존적인 많은 tolerance 셋팅들이 있다. 단위 시스템의 깊은 설명을 위해선 Units를 보라. 개개의 tolerance의 설명을 위해 doxygen 문서들을 보라.

11.3. Memory Allocation
모든 Box2D에서의 메모리 할당은 아래의 경우들을 제외하고 b2Alloc 와 b2Free 를 통해 모아서 처리한다.
  • b2World는 stack이나 당신이 좋아하는 어떠한 allocator를 사용하여 생성되어질수 있다.
  • 당신이 factory 함수들을 사용하지 않고 생성한 Box2D 클래스들. 이런것들은 콜백 클래스들이나 contack point buffer들을 포함하고 있다.
b2Settings.as를 변형함으로써 메모리 할당이 전향되는것을 어렵게 생각하지 마라.

12. Rendering
12.1. Debug Drawing
메모: 디버그 드로잉은 최종 게임에서 사용되지 않는다. 그것의 목적은 이름에서처럼 디버깅을 위한 것이다.

physics world의 세세한 드로잉을 얻기 위해서 b2DebugDraw 클래스를 구현 할 수 있다. 아래는 가능한 항목들이다.
  • shape outlines
  • joint connectivity
  • core shape (지속적인 충돌을 위한)
  • world의 AABB를 포함한 넓은 단계의 AABB(axis-aligned bounding box)들
  • 폴리곤 형태의 OBB(polygon oriented bounding box)
  • 넓은 단계의 짝(잠제적인 contact들)
  • 질량의 중심
이런 디버깅을 위한 물리적 항목들을 그리는 것들 중 데이터를 직접적으로 접근하는 것 대신 선호하는 방법이다. 그 이유는 필요한 많은 데이터는 내부적 용도이며 바뀌기 위한 것이기 때문이다.

테스트베드는 debug draw 기능과 contact linstener를 이용하여 물리적 항목들을 그린다, 그래서 그것은 디버그 드로잉을 어떻게 구현하는지 뿐만 아니라 어떻게 contact point를 그릴 것인가에 대한 주요 예제를 제공한다.
01 //Assuming a world is set up, under variable m_world
02  
03   
04 //debugSprite is some sprite that we want to draw our debug shapes into.
05 var debugSprite = new Sprite();
06 addChild(debugSprite);
07   
08 // set debug draw
09 var dbgDraw:b2DebugDraw = new b2DebugDraw();
10  
11   
12 dbgDraw.m_sprite = debugSprite;
13 dbgDraw.m_drawScale = 30.0;
14 dbgDraw.m_fillAlpha = 0.3;
15 dbgDraw.m_lineThickness = 1.0;
16 dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
17 m_world.SetDebugDraw(dbgDraw);
18  
19 }}

12.2 Drawing Sprites
각각의 캐릭터를 통해서 반복하며, body를 측정하기 위해서 소속된 각각의 shape들을 찾고, shape의 지역 포지션을 마음에 두고, 지정된 포지션과 성향으로 sprite를 그린다.

12.3 What to do now


WRITTEN BY
buzzler

,
2. Box2DFlash에 대하여
Box2DFlash는 게임을 위한 2차원 rigid body simulation (강체 시뮬레이션) 라이브러리이다. 프로그레머는 게임내의 물체들이 자연스럽게 움직이거나 게임을 더 인터렉티브하도록 보이게 하기 위해서 이 라이브러리를 사용할 수 있다. 게임 플레이어의 시각으로, 물리엔진은 단지 절차적인 애니메이션을 위한 시스템으로 밖에 보이지 않는다. Box2DFlash는 액션스크립트 3.0으로 작성되었고, Box2D의 네임스페이스를 그대로 유지하고 있다. C++버전과의 매칭을 위해서 엔진에 정의된 대부분의 type들은 b2 라는 접두사를 가진다.

2.1. 필수조건
매뉴얼에서는 이 글을 읽는 사람이 mass(질랑), force(힘), torque(회전력),  impulse(충격)등의 기본 물리지식에 익숙하다고 가정하고 있다. 만약 그렇지 않다면 Chris Hecker 와 David Baraff가 제공하는 수많은 교본을 먼저 읽어보길 권장한다(이름을 구글링하면 된다). 그들의 교본을 상세하게 파악하고 있을 필요는 없다. 단지 그들은 Box2D를 쓸 수 있도록 기본 컨셉만 나열하는 수준이다.

Wikipedia 또한 물리와 수학 지식의 좋은 참조자료이다. 어떤면에서는 더 신중하게 작성된 내용이라서 구글링보다 더 유용하기도 하다.

이건 필수조건은 아니지만,  만약 당신이 Box2D의 내부 동작에 대해서 호감을 가지고 있다면 이런 기사들을 읽어볼 수도 있다.

Box2Flash는 액션스크립트 3.0 으로 써진덕에 당신은 액션스크립트 3.0 으로만 사용할 수 있다. 만약 당신이 액션스크립트 2.0 지식을 바탕으로 Box2D를 사용하기 전에 간단한 프로젝트를 더 쉽게 시작해볼 수 있는 방법을 찾았다면, 그것은 불가능하다.

2.2. 핵심 컨셉
Box2D는 몇가지 기본적인 핵심 오브젝트들로 동작한다.  우리는 간략하게 이러한 오브젝트를 정의하고 자세한 내용은 이 문서의 뒷부분에 서술할 것이다.

rigid body
강체. 즉 크고 단단한 물질. 
shape
충돌가능한, body에 단단하게 할당된 2D 오브젝트. Shape은 마찰계수나, 반발계수같은 속성을 가지고 있다. 
constraint
constraint는 물체들의 자유로운 각도를 제한하는 물리적 결합이다. 2D 에서 물체는 3가지의 각도를 가지고 있다. 만약 우리가 물체를 벽에 핀으로 고정시킨다면(진자운동을 하는 추처럼) 그 물체는 벽에 contrained 된다. 여기서 물체는 고정된 핀을 기준으로밖에 회전할 수 없게 되므로 나머지 2가지의 각도가 제한 받는것이다.
contact constraint
강체가 서로 관통하거나, 마찰 또는 반발을 시뮬레이션하기 위해서 고안된 특수한 contraint이다. 이것은 Box2D에 의해서 자동적으로 생성되므로 당신이 직접 만들일은 없다.
joint
이것은 둘 이상의 물체를 서로 고정시키는 용도의 constraint이다. Box2D는 revolute, prismatic, distance 그외 여러종류의 joint를 지원한다. joint는 또한 limit, motor 같은 기능을 제공한다.
joint limit
joint limit은 joint의 모션 범위를 제한한다. 예를들면 인간의 팔꿈치가 움직일 수 있는 각도를 제한하는 것.
joint motor
joint motor는 여러 joint의 자유로운 각도를 제어할 수 있도록 한다. 예를들면 당신은 motor를 사용해서 팔꿈치의 모션을 제어할 수 있다.
world
world는 body, shape, constraint 들이 서로 상호작용할 수 있는 집합체이다. Box2D는 여러개의 world를 지원하지만 필수도 아닐뿐더러 바람직하지 않다.

3. Hello Box2D
이것는 커다란 바닥 물체와 작은 움직이는 박스를 생성하는 단순한 예제이다. 이 코드에는 어떠한 그래픽 요소도 담고 있지 않기 때문에 큰 감흥은 없을것이다. : )

3.1. World 생성하기
모든 Box2D 프로그램은 world를 생성하는것으로 시작한다. 이것은 물체와 시뮬레이션을 관리하는 물리 허브(physics hub)역할을 한다.

world를 생성하기 위해서는, 첫째로 world의 바운딩 박스를 선언할 필요가 있다. Box2D는 충돌검사를 더욱 신속하게 하기 위해서 바운딩 박스를 사용한다.  그 크기는 치명적이지 않지만 그보다는 딱맞는 핏감이 성능 향상에 도움이 된다. 매우 작은것보단 매우 큰것이 더 낫다.

1 var worldAABB:b2AABB = new b2AABB();
2 worldAABB.lowerBound.Set(-100.0, -100.0);
3 worldAABB.upperBound.Set(100.0100.0);

주의

worldAABB는 body들이 놓일 공간들보다 항상 더 커야한다. worldAABB를 너무 작게 설정하는것보단 크게 설정하는것이 더 낫다. 만약 body가 바운딩박스에 닿는다면(또는 넘어간다면) worldAABB는 시뮬레이션이 멈추게 될 것이다.

다음은 중력 벡터를 선언한다. 그렇다 당신은 중력을 마음대로 설정할 수가 있다. 또한 우리는 world가 더이상 시뮬레이션 할 필요가 없다면 sleep상태로 되도록 설정한다. 잠자는 body는 어떠한 시뮬레이션도 요구하지 않는다.
1 var gravity:b2Vec2 = new b2Vec2 (0.0, -10.0);
2 var doSleep:Boolean true;
자 그럼 world를 만들어보자.
1 var world:b2World = new b2World(worldAABB, gravity, doSleep);
이렇게 해서 우린 physics world를 갖게 되었다. 이제 이곳에 몇가지를 더 추가해 보자.

3.2. Ground Box 생성하기
body는 다음과같은 절차를 통해서 생성된다.
  1. position, damping 과 함께 body를 정의한다.
  2. body를 생성하기 위해서 world 오브젝트를 사용한다.
  3. geometry(기하), friction(마찰), density(밀도) 로 shape를 정의한다.
  4. body위에 shape들을 생성한다.
  5. body에 붙여진 shape에 따라서 부가적으로 mass(질량)를 할당하기도 한다.
첫번째로 ground body를 생성한다. 그렇게 하기 위해서 body의 정의가 필요하다. 이 정의와 함께 초기 위치를 ground body에 할당한다.
1 var groundBodyDef:b2BodyDef = new b2BodyDef();
2 groundBodyDef.position.Set(0.0, -10.0);
두번째,  ground body를 생성하기 위해서 b2BodyDef (body 정의) 를 world 객체에 넘긴다. world 객체는 해당 b2BodyDef의 참조를 유지시키지 않는다. ground body는 static body로 생성되어진다. static body는 다른 static body들과 충돌하지도 않으며 움직일 수도 없다. Box2D는 body의 질량이 0인 경우 이 body를 static body라고 가정한다.  body의 질량 기본값이 0 이므로 static body가 기본값인셈이다.
1 var groundBody:b2Body = world.CreateBody(groundBodyDef);
세번째,  ground polygon 정의를 생성한다. 우리는 SetAsBox 함수를 통해서 ground polygon을 박스로 만드는데, 박스는 부모 body의 원점을 중심으로 박스형태를 가지게 된다. 
1 var groundShapeDef:b2PolygonDef = new b2PolygonDef();
2 agroundShapeDef.SetAsBox(50.010.0);
SetAsBox 함수의 매개변수들은 각각 half-width, half-height 이다.  그래서 이 경우 가로는 100, 세로는 20을 가지게 된다. Box2D는 기본적으로 meter (미터), kilograms (킬로그램), second (초) 단위를 사용한다. 그러나, 나중에 이 문서에서 거론하겠지만, 이러한 단위 시스템은 변경할 수 있다.

마지막으로, ground body에 ground polygon shape을 생성한다.
1 groundBody.CreateShape(groundShapeDef);
반복하지만, Box2D는 shape 이나 body 정의를 내부에 사본으로 따로 담고, 직접 참조를 유지시키진 않는다.

모든 shape는 부모 body를 가지고 있다. static shape이더라도. 그러나 당신은 모든 static shape를 하나의 body에 생성할 수 있다. 이렇게 하여 잠재적 오류 발생을 억제하고 코드가 내부적으로 다형성을 가질 수 있게 한다.

당신은 여기서 한가지 패턴을 알아차렸을것이다. 모든 Box2D의 type들은 b2 라고 하는 접두사를 가진다. 이것은 당신의 코드와 네이밍 충돌을 피하기 위함이다.

3.3. Dynamic Body 생성하기
자 이렇게 해서 우린 ground body를 생성하였다. 위와 동일한 방법으로 우리는 dynamic body 또한 생성할 수 있다. 주요한 차이점이라면 mass(질량)을 설정해야 한다는 것이다.

처음으로, CreateBody 함수로 body를 생성한다.
1 var bodyDef:b2BodyDef = new b2BodyDef();
2 bodyDef.position.Set(0.04.0);
3  
4 var body:b2Body = world.CreateBody(bodyDef);
다음으로, polygon shape을 하나 만들고 body에 붙인다. density (밀도) 를 1로 설정함을 주목하라. 기본 질량값은 0이다. 또한 shape의 마찰계수는 0.3 으로 세팅되었다. shape을 body에 붙인후 우리는 body의 새로운 mass값을 계산하도록 SetMassFromShapes 함수를 통해 시킬 것이다. 이것으로 당신은 아마 한개의 body에 여러 shape을 붙일 수 있다는 것을 알았을 것이다. 만약 위의 함수를 통해서 계산된 결과 mass가 0이라면 해당 body는 static body가 된다. ground body를 SetMassFromShapes함수를 호출 할 필요가 없도록 하기 위해서 기본적으로 body들은 기본 질량값 0을 가진다.
1 var shapeDef:b2PolygonDef = new b2PolygonDef();
2 shapeDef.SetAsBox(1.01.0);
3 shapeDef.density = 1.0;
4 shapeDef.friction = 0.3;
5 body.CreateShape(shapeDef);
6 body.SetMassFromShapes();
초기화가 모두 끝났다. 이제 실제 시뮬레이션을 해보자.

3.4. World 시뮬레이션하기
이제 ground box와 dynamic box 모두 초기화 하였다. 이제 뉴튼을 엿먹일 차례다. 우리가 고려할 것들이 몇가지 남아있지 않다.

Box2D는 intergrator라고 불리는 산술 코드를 조금 사용하고 있다. intergrator는 매 시간 물리공식을 시뮬레이션한다. 그래서 우리는 Box2D의 time step을 고를 필요가 있다. 일반적으로 게임에 쓰이는 물리엔진은 1/60 초 단위의 물리 연산을 한다. 이런 time step은 큰 숫자로 그냥 둬도 상관없지만 , 너무 자주 바뀌거나 플래시의 frame rate와는 동일하지 않게 하기를 권장하므로 신중히 선택해야한다. time step은 다음과 같다.
1 var timeStep:Number 1.0 60.0;
intergrator 이 외에 Box2D에는 contraint solver라고 불리는 좀더 큰 코드를 사용한다. 말그대로 매시간 시뮬레이션 하는 동안 contraint들을 풀어낸다. 단일 constraint들은 완벽하게 풀어진다. 하지만,  한 constraint가 풀리는 동안 다른 constraint까지 신경쓰는것이 쉽지 않다. 그래서 모든 시간에 걸쳐 사용할 constraint들을 통합시킬 필요가 있다. 우리는 그 반복 횟수를 10으로 사용할 것을 권장한다. 당신은 이 횟수를 속도와 정확도를 맞바꿔가며 조절할 수 있다.  반복획수가 적으면 성능은 향상되지만 그만큼 정확도가 떨어지고, 반복횟수를 늘이면 성능은 저하되지만 시뮬레이션 퀄러티는 향상된다. 이것이 우리가 선택한 반복횟수이다.
1 var iterations:Number 10;
time step 과 iteration(반복 횟수)은 완전히 무관한 점을 명심하라. 한번의 반복은 sub-step이 아니다. 하나의 iteration은 한 time step에서의 모든 constraint들을 통틀어 싱글패스다. 이를 당신은 멀티패스로 처리하게 할 수도 있다.

우리는 지금 시뮬레이션 루프를 시작할 준비가 되었다. 당신의 게임 시뮬레이션 루프는 게임의 메인 루프에 통합될 수도 있다. 매 게임 루프마다 b2World.Step을 호출하여 각 패스를 통과한다.  게임의 frame rate와 설정한 물리적 time step에 의존하여 보통 한번의 호출로 충분하다.

Hello Wolrd 프로그램은 매우 단순하게 종료되도록 고안되었다. 그래서  그래픽적인 출력이 존재하지 않는다. 출력 아웃풋이 없이 만들어져 있어서 많이 지루하지만 이 코드는 body의 위치와 회전량을 출력 하여 준다. 자! 여기에 시뮬레이셔닝 되는 1초 동안 총 60번의 step으로 시뮬레이팅 되는 시뮬레이션 루프가 있다.
1 for (var i:Number 0; i < 60; ++i)
2  
3 {
4     world.Step(timeStep, iterations);
5     var position:b2Vec2 = body.GetPosition();
6     var angle:Number = body.GetAngle();
7     trace(position.x +','+ position.y +','+ angle);
8  
9 }

3.5. Testbed
당신이 Hello World 예제를 정복했다면,  Box2DFlash의 테스트베드를 보기 보기 시작해야 한다. testbed는 단위 테스트 프레임워크이고 데모 환경이다. 아래와 같은 기능을 가지고 있다.
  • dynamic body에 첨부된 shape들의 마우스 선택
  • 테스트의 확장 세트
testbed는 테스트케이스와 프레임워크 스스로 Box2D의 많은 예제를 가지고 있다.나는 당신이 Box2D를 배우듯 testbed를 탐색하고 만지작거리길 장려한다.


4. API 디자인
4.1. 메모리 관리
Box2DFlash는 C++ 라이브러리인 Box2D의 직 포팅 버전이다. 그렇게 때문에 메모리 관리를 gabage collector에 의존한것보다 철저하게 관리된다. 이는 버벅임을 줄일 수 있다. 그리하여 수많은 생성, 삭제 메소드에서 객체들은 재활용되어진다.

4.2. Factory 와 정의
위에서 말한대로 메모리관리는 Box2D API 설계상의 중심적 역할이 된다. 그래서 당신이 b2Body나 b2Joint를 생성할때 당신은 b3World의 factory 함수들을 호출할 필요가 있는 것이다.

생성함수들은 이런한 것들이 있다.
1 b2World.CreateBody(def:b2BodyDef):b2Body
2 b2World.CreateJoint(def:b2JointDef):b2Joint
그리고 그에 대응되는 삭재함수들이 있다.
1 b2World.DestroyBody(body:b2Body):void
2  
3 b2World.DestroyJoint(joint:b2Joint):void
당신이 body나 joint를 생성할때 당신은 definition또는 짧게 def 를 재공할 필요가 있다. 이러한 정의들은 body나 joint를 만들기 위한 모든 정보들은 포함하고 있다. 이러한 어프로치를 사용함으로써 우리는 생성자 오류들을 예방할 수 있고, 수많은 함수의 매개변수들을 작게 유지시킬 수 있고, 기본값들을 제공할 수 있으며 접근수를 줄일 수 있다.

body처럼 shape도 b2Body라는 factory method를 사용하여 생성하고 삭제된다.
1 b2Body.CreateShape(def:b2ShapeDef):b2Shape
2 b2Body.DestroyShape(shape:b2Shape):void
factory들은 definition의 참조를 가지고 있지 않는다. 그래서 당신은 한번 생성된 definition들을 스택에 임시로 유지시켜놓고  다시 사용할 수 있는것이다.

4.3. 단위값
Box2D는 소수점 연산과 함께 동작 한다 그래서 조금의 오차가 있을 수 있다. 이러한 오차는 meters-kilogram-second 단위값들과 잘 동작하게 하기 위해서 개선되어지고 있다. 

2D 물리 엔진이 pixels을 단위값으로 사용하는것을 시도하는 사람도 있으나 이는 불행히도 엉터리 시뮬레이션으로 망칠뿐만 아니라 괴상한 기능을 보여줄 수도 있다. 200픽셀 길이의 한 객체는 Box2D에서는 45층 건물로 보일 수 있다.

주의
Box2D는 MKS 단위계에 맞춰져있다. 움직이는 물체의 크기를 대략 0.1~10 미터 사이로 유지하라. 당신은 게임 배경과 캐릭터들을 렌더링할때 스케일링 시스템이 필요할 것이다. DebugDraw 안에는 스케일링 시스템이 이미 내장되어있다.

4.4. 사용자 데이터
b2Shape, b2Body, b2Joint 클래스들은 당신이 어떠한 유져 데이타라도 담을 수 있도록 하고 있다. 당신이 Box2D 데이타 구조체들 탐색하거나 당신의 게임 엔진에서의 데이타 구조체들과의 관계를 결정할 때 이것은 간편한 방법이 되어준다.

캐릭터를 rigid body에 붙이는 전형적인 예를 들어보겠다. 이것은 순환 참조(circular reference)가 구성된다. 만약 당신이 캐릭터를 가지고 있다면 body를 참조할 수 있고, body를 가지고 있다면 캐릭터를 참조할 수 있게된다.
1 actor:GameActor = new GameCreateActor();
2 bodyDef:b2BodyDef = new b2BodyDef();
3 bodyDef.userData = actor;
4 actor.body = box2Dworld.CreateBody(bodyDef);
여기 몇가지 당신이 유져 데이타들을 필요로 하는 경우의 예들이 있다.
  • 캐릭터가 충돌 검사 결과, 충돌된 데미지를 캐릭터에 적용하는것
  • 플레이어가 AABB 안으로 들어가서 스크립팅된 이벤트를 수행해야 하는것
  • Box2D가 joint의 파괴를 당신에게 알려서 게임 구조체에 접근하는것
잊지 말아야할 것은, 유져 데이타는 옵션이고, 당신은 그곳에 어떠한 것이라도 담을 수 있다. 그러나 당신은 일관성을 지켜야 한다. 예를들면 당신이 만약 하나의 body에 하나의 캐릭터 데이타를 저장해놓고자 한다면, 당신은 아마 모든 body들에도 그렇게 해야할것이다.

4.5. Strawman
만약 당신이 이런 API 디자인이 맘에 들지 않는다고해도 걱정하지 마라. 당신은 소스 코드를 가지고 있다. 당신이Box2D에 관련된 어떠한것이라도 피드백 남길것이 있다면 포럼에 코멘트를 달아주길 진심으로 바란다.

5. The World
5.1. world 란?
b2World 클래스는 body와 joint를 포함하고 있다. b2World는 시뮬레이션의 모든 측면을 관리하고 비동기 질의(AABB 질의와 같은)들을 허용한다. Box2D와 관련된 대부분의 상호작용은 b2World 객체에서 이루어진다고 할 수 있다.

5.2. world를 생성하고 삭재하기
world를 생성하는것은 상당히 간편하다. 당신은 바운딩 박스와 중력 벡터만 제공하면 된다.

AABB는 world를 감싸야한다. 당신은 바운딩 박스를 world보다 살짝 더 크게 만듬으로써(안전하게 두배로 설정해도된다) 성능향상시킬 수 있다. 만약 구렁에 빠지는 수많은 body들을 구현해야 한다면 당신의 어플리케이션은 이것들을 모두 감지하여 body들을 삭제해야한다. 이렇게 함으로써 floating point overflow를 예방하고 성능향상을 시킬 수 있을 것이다.

1 var myWorld:b2World = new b2World(aabb, gravity, doSleep)

myWorld가 쓸모없어지면 garbage collector에 의해서 자동적으로 삭제될 것이다.

주의
world의 AABB는 항상 body들이 차지할 영역보다 크게 설정되어있어야함을 기억하라. 만약 body들이 world의 AABB 밖으로 나간다면 그것들은 모두 멈춰버릴것이다. 이것은 버그가 아니다.

5.3. world 사용하기
world 클래스는 body와 joint의 생성에 필요한 factory들을 포함하고 있다. 이러한 factory는 body와 joint 섹션을 다룰때 더 자세히 거론할 것이다. b2World에는 지금부터 설명할 만한 또다른 인터렉션들이 있다.

5.4. 시뮬레이션
world 클래스는 시뮬레이션을 진행시키기 위해서 사용되어진다. 당신은 time step과 iteration count를 설정해야한다. 예를들자면
1 var timeStep:Number 1.0 60.;
2 var iterationCount:Number 10;
3 myWorld.Step(timeStep, iterationCount);
time step 이후 정보를 얻기위해서 body들과 joint들을 측정할 수 있다. 아마도 필수적으로 당신은 body들을 부동작하도록 잡아둘 것이다. 그렇게 함으로써 게임 캐릭터들을 업데이트하고 렌더링할 수 있다. game loop의 어디에서든 time step을 실행시킬 수 있지만 그것들의 순서를 알아두어야 할 것이다. 예를들자면, 새로운 body들이 현제 프레임에서 충돌된 결과를 얻기 원하는 경우, 그 body들을 time step의 전에 body들을 생성해야만 한다.

위의 HelloWorld 튜토리얼 에서도 언급했듯이, 당신은 고정된 time step을 사용해야 한다.  큰 time step을 사용하는것은 당신의 작은 fram rate에서 좋은 성능을 가져올 수 있다. 그러나 일반적으로 당신은 1/30 보다 큰 time step을 써서는 안된다. 1/60 의 time step이 일반적으로 질좋은 시뮬레이션 결과를 가져온다.

iteration count는 world에 있는 모든 contact과 joint들의 constraint를 얼마나 많이 계산할 것인지를 제어한다. 더 많은 iteration는 항상 더 좋은 시뮬레이션 결과를 낸다. 그러나 큰 iteration count를 위해서 작은 time step과 타협하지 마라. 60Hz 그리고 10 iteration 은 30Hz 와 20 iteration보다 훨씬 좋다. 

5.5. world 탐험하기
이전에 말했듯, world는 body들과 joint들을 포함하고 있다. 당신은 world에 리스팅되어있는 body와 joint의 리스트를 지속적으로 건드려야 할 것이다. 예를들면, 이 코드는 world에 있는 모든 body를 깨워준다(역자주: 시뮬레이션 되지 않는 body들은 잠든다).
1 for (var b:b2Body = myWorld.GetBodyList(); b; b = b.GetNext())
2  
3 {
4     b.WakeUp();
5 }
불행하게도 삶은 더 복잡해질 수도 있다. 예를들면, 아래의 코드는 오류가 난다

01 for (var b:b2Body = myWorld.GetBodyList(); b; b = b.GetNext())
02  
03 {
04     var myActor:GameActor = b->GetUserData() as GameActor;
05     if (myActor.IsDead())
06     {
07         myWorld.DestroyBody(b); // ERROR: now GetNext returns garbage.
08  
09     }
10 }

body가 삭제되기 전까지 모든것이 문제 없었다. body가 삭제되는순간 그 다음 포인터가 엉뚱한곳을 가리키게 된다. 그래서 b2Body.getNext() 를 호출하면 쓰레기를 되돌려줄 것이다. 해결책은 body를 삭제하기 전에 미리 다음 body의 참조를 챙겨두는 것이다.

01 var node:b2Body = myWorld.GetBodyList();
02 while (node)
03  
04 {
05     var b:b2Body = node;
06     node = node.GetNext();
07   
08     var myActor:GameActor = b->GetUserData() as GameActor;
09     if (myActor.IsDead())
10  
11     {
12         myWorld.DestroyBody(b);
13     }
14

이 코드는 안전하게 현제 body를 삭제한다. 그러나 당신은 여러 body들을 한꺼번에 삭제하는 기능을 호출 하고 싶을지도 모른다. 이런 경우는 매우 조심할 필요가 있다. 해결책은 어플리케이션마다 다를것이다. 하지만 편의를 위해서 이 문제를 해결하는 한가지 방법을 보여줄 것이다.
01 var node:b2Body = myWorld.GetBodyList();
02 while (node)
03  
04 {
05     var b:b2Body = node;
06     node = node.GetNext();
07   
08     var myActor:GameActor = b.GetUserData() as GameActor;
09     if (myActor.IsDead())
10  
11     {
12         var otherBodiesDestroyed:Boolean = GameCrazyBodyDestroyer(b);
13         if (otherBodiesDestroyed)
14         {
15  
16             node = myWorld.GetBodyList();
17         }
18     }
19 }
당연하게도 이것을 동작시키기 위해서는 GameCrazyBodyDestroyer가 정확해야 한다.

5.6. AABB(Axis-aligned Bounding Box) 질의
가끔은 당신은 지역내의 모든 shape들을 알아보고 싶을 것이다. b2World 클래스는 broad-phase data structure(광역 데이타 구조)에 사용하기위한 빠른 log(N) 함수를 가지고 있다. 당신이 world 좌표계에 에 AABB를 제공하면 b2World는 그 AABB와 잠제적으로 교차되는 모든 shape 배열을 돌려준다. 이것은 제공한 AABB와 교차되는 모든 shape들의 AABB와 교차를 찾는것이기 때문에 정확하진 않다.  예를들면, 아래의 코드는 특정 AABB와 잠재적으로 교차되는 모든 shape들을 찾고 관련된 모든 body들을 깨운다.
01 var aabb:b2AABB = new b2AABB();
02 aabb.lowerBound.Set(-1.0, -1.0);
03 aabb.upperBound.Set(1.01.0);
04  
05 var k_bufferSize:Number 10;
06 var buffer:Array = [];
07 var count:Number = myWorld.Query(aabb, buffer, k_bufferSize);
08  
09 for (var i:Number 0; i < count; ++i)
10 {
11     buffer[i].GetBody().WakeUp();
12  
13 }
Note: 전형적인 AS3의 사용보단 좀 모호한 문법을 사용한다. 이것은 Box2DAS3가 C++ 용 라이브러리에서 유례되었기 때문인데 애초에 C++에서는 사이즈 변경이 가능한 배열을 손쉽게 만들 수 없다.

6. Bodies
6.1. About
body들은 포지션과 속도 정보를 가지고 있다. 당신은 body에 force나 torque, impulse를 적용할 수 있다. body들은 static일수도 dynamic일 수도 있다. static body는 움직이지 않도록 고정되어 있으며 다른 static body들과 충돌하지 않는다.

body들은 shape을 위한 근간(backbone)이다. body들은 world에서 shape을 실어 나르고 이동시킨다. Box2D에서 body들은 항상 강체(rigid body)이다. 즉, 하나의 강체에 첨부된 두 shape은 서로서로 간섭을 일으키지 않는다.

당신은 보통 당신이 만든 모든 body들의 포인터를 유지시킬 것이다. 이런 방법으로 당신의 그래픽적인 엔티티들의 업데이트를 위해서 body 포지션들을 질의할 수 있다. 당신은 또한 body 포인터를 유지시켜야 한다. 그래야 당신은 필요없어졌을 때에 body를 삭제할 수 있다.

6.2. Body Definition
body가 생성되기 전에 당신은 body definition (b2BodyDef)를 생성해야만 한다. 당신은 스택이나 당신의 게임의 데이타 구조체들 안에 내장시켜서 body definition을 생성할 수 있다. 선택은 당신에게 달려있다.

Box2D 는 body definition을 가리키는 포인터를 따로 유지시키지 않고 body definition의 밖에 데이타 따로 사본을 만든다.  이 말은 당신은 여러 body들을 생성하기 위해서 하나의 body definition을 재활용 할 수 있다는 의미이기도 하다.

자 그럼 body definition의 중요 요소들을 들여다 보자.

6.3. Mass Properties
하나의 body에 mass(질량) 속성을 수립하는 방법은 다음과 같이 몇가지가 있다.
  1. body definition안에 질량 속성을 명시한다.
  2. 이미 만들어진 body에 질량 속성을 새롭게 명시한다.
  3. body에 첨부된 shape들의 밀도에 기반하여 질량 속성을 설정한다.
shape들의 밀도에 기반한 질량을 계산하는 것은 많은 게임 시나리오상으로 이해하기 쉽다. 이것은 body들이 합리적이고 일관된 질량값을 갖도록 하는것에 도움이 된다.

그러나, 다른 게임 시나리오들은 더 명시적인 질량값을 요구할지도 모른다. 예를들면 당신가진 메카니즘이 명확한 질량값들 필요로 하는 scale 같은것을 사용할때 이다.

당신은 다음과같이 body definition 에 질량 속성을 명시할 수 있다.
1 bodyDef.massData.mass = 2.0;             // the body's mass in kg
2  
3 bodyDef.massData.center.SetZero();       // the center of mass in local coordinates
4 bodyDef.massData.I = 3.0;                // the rotational inertia in kg*m^2.
질량 속성을 설정하기위한 다른 방법들은 이 문서의 다른 곳에서 또 여러차례 다뤄질 것이다.

6.4. Position and Angle
body definition은 당신에게 body 생성시의 포지션을 초기활 할 기회를 제공한다. 이는 생성한 이후 포지션을 변경하는것보다 좋은 성능을 낸다.

하나의 body는 두 개의 흥미로운 요점을 가지고 있다. 첫번째는 body의 원형이다. shape과 joint들은 그 body 원형을 참조하여 첨부된다. 두번째 흥미로운 점은 '질량의 중심'이다. 질량의 중심은 첨부된 shape들의 질량 분포로 부터 측정되어지거나 b2MassData에 명쾌하게 설정된다. 수많은 Box2D의 내부 연산들은 질량의 중심점을 사용한다. 그 예로 b2Body 는 질량 중심의 선속도(linear velocity)를 저장하고 있다.

당신이 body definition을 만들때 당신은 질량의 중심이 어디에 위치해 있는지 아마도 모를것이다. 그런이유로 당신은 body 원형의 포지션을 형시한다. 당신은 또한 질량의 중심에 영향받지 않는 body의 라디안 각도를 명시할지도 모른다. body의 질량 속성들을 바꿀 기회가 오면 질량 중심은 body로 옮겨질지도 모른다. 하지만 그 원래 포지션은 바뀌지 않고 첨부된 shape과 joint들은 움직이지 않는다.
1 bodyDef.position.Set(0.02.0);     // the body's origin position.
2 bodyDef.angle = 0.25 * b2Settings.b2_pi;       // the body's angle in radians.
6.5. Damping
damping(제동)은 body들의 속도(world velocity)를 감소시키는 때에 사용된다. damping은 두 면들이 접해서만 발생되는 friction(마찰)과는 다르다. 또한 damping은 마찰보다 시뮬레이션 하는 것에 있어서 더 가볍다. 그렇지만 요점은 damping은 마찰을 대체하기 위한 방법이 아니고 함께 사용되어지는 효과라는 점이다.

damping 매개변수는 '0' 부터 '무한대'까지 될 수 있다. 0은 damping이 없다는 의미이고 무한대는 최대 제동력을 의미한다. 일반적으로 당신은 damping값을 0부터 1 사이에서 사용할 것이다. 나는 일반적으로 linear damping는 body를 가볍게 보이도록 하기 때문에 사용하지 않는다. 
1 bodyDef.linearDamping = 0.0;
2 bodyDef.angularDamping = 0.1;
damping은 안정성과 성능을 위해 근사치를 사용한다. 작은 damping 값에서 damping 효과는 대게 time step에서 독립적이다. 큰 damping값에서 damping 효과는 time step에 의존적으로 달라질 것이다. 당신이 time step을 내가 제안했던것처럼 '고정값'으로 사용한다면 이런 것들은 이슈가 되지 않는다.

6.6. Sleep Parameters
sleep은 어떤 의미일까? body들을 시뮬레이팅 하는 것은 비싼(성능을 낭비하는) 작업이다 그래서 더 나은 시뮬레이팅을 해야 한다. body가 쉬어야 할 때 우리는 시뮬레이션을 중단해야 한다.

Box2D는 body(또는 body들 그룹)가 쉬어야 하는지는 결정했을때, body는 CPU로드를 매우 조금만 사용하는 sleep 상태로 들어간다. 만약 sleep상태의 body가 다른 body와 충돌하면 그 body는 sleep상태에서 깨어난다. 만약 그 body에 첨부된 joint 또는 contact 가 body에서 삭제될 때에도 body들은 또한 깨어날 것이다. 또한 당신이 직접 깨어나도록 할 수도 있다.

body definition은 생성된 body가 sleep할수있는지와 sleep상태로 생성될지를 당신에게 명시하도록 한다.
1 bodyDef.allowSleep = true;
2 bodyDef.isSleeping = false;

6.7. Bullets
게임 시뮬레이션은 보통 몇 프레임 레이트의 제생될 이미지 시퀀스를 만들어낸다. 이런 환경에서 rigid body(강체)는 한 time step동안 큰 양의 이동을 할 수 있다. 만약 물리엔진이 큰 모션을 위한 관리를 하지 않는 경우, 당신은 일부 잘못된 제각각의 물체들을 볼 수 있을지도 모른다. 이런 현상을 tunneling이라고 부른다.

기본적으로, Box2D는 static body들로 인한 터널링으로부터 dynamic body를 막기위해 Continuous Collision Detection(CCD)를 사용한다. 이는 shape을 이전 포지션에서 새로운 그 포지션으로 쓸어냄으로써 이루어 진다. 엔진은 sweep시의 새로운 충돌들과 이러한 충돌들을 위한 Time of Impact (TOI)를 계산을 구한다. body들은 그들의 첫 TOI로 움직여지고 그런 다음에는 원래 time step의 끝까지 시뮬레이트된다. 이러한 프로세스는 필요한만큼 계속 반복된다.

일반적으로 CCD는 dynamic body에서는 사용되지 않는다. 이것은 성능을 아끼기 위한 조치이다. 어떤 게임 시나이오상에서는 당신은 dynamic body가 CCD를 사용하길 필요로 할것이다. 예를들면, 당신이 빠른 속도의 총알이 얇은 벽을 향해 쏘는것일지도 모른다. CCD가 없이는 그 총알은 벽을 통과해버린다.

Box2Ddp서는 빠르게 움직이는 물체를 bullets라고 부른다. 당신은 어떠한 body들이 당신의 게임 디자인에 기반하여 bullet이 되는지를 결정해야 한다. 만약 당신이 한 body를 bullet이라고 조치해야한다고 결정했다면 아래와 같은 설정을 사용해라.
1 bodyDef.isBullet = true;
그 bullet 플래그는 dynamic body에서만 효력이 있다.

CCD는 값비싼 연산이라서 당신은 아마 모든 움직이는 body가 bullet이 되는것을 원치 않을 것이다. 그래서 기본적으로 Box2D는 CCD를 움직이는 body와 static body들에게만 사용한다. 이것은 당신의 world에서 body들이 탈출해버리는것을 예방할 매우 효율적인 접근법이다. 그러나 언젠가 당신은 어떤 빠른 body에서 CCD를 필요로 할지도 모른다.

6.8. Body Factory
body들은 world 클래스에서 주어진 body factory를 사용하여 생성되고 삭제된다. 이는 body가 world 구조에 추가하고 효율적인 할당량으로 생성되도록 한다.

body들은 질량 속성에 의존하여 dynamic과 static이 될수 있다. 두 body 타잎은 동일한 생성, 삭제함수를 사용한다.
1 var dynamicBody:b2Body = myWorld.CreateBody(bodyDef);
2  
3 //... do stuff ...
4 myWorld.DestroyBody(dynamicBody);
5 dynamicBody = null;
static body는 다른 body들의 영향에 의해 절대 움직이지 않는다. 당신은 수동적으로 static body를 움직일지도 모른다 하지만 당신은 두개 이상의 static body 사이에 dynamic body를 밀어넣지 않도록 주의해야 할 것이다. 당신이 static body를 임의대로 움직여도 friction(마찰)은 정상적으로 작용하지 않을것이다. static body는 절대 스스로 시뮬레이셔닝 되지 않고 다른 static body들과 충돌도 일어나지 않는다. 하나의 static body에 몇개의 shape들을 첨부하는것이 각각 따로 static body들에 첨부하는것보다 (시뮬레이션 속도가)빠르다. 내부적으로, Box2D 는 static body에 대해서 질량이나 역-질량(inverse mass)을 0으로 설정한다. 이것은 static body를 특별한 케이스로 간주하여 대부분의 알고리즘의 수학연산을 피하기 위한 조치이다.

Box2D는 body definition이나 다른 어떤 데이타들의 참조를 유지하지 않는다(유저 데이타 포인터는 제외). 그래서 당신은 임시 body definition 을 만들어서 제사용할 수 있다.

Box2D는 작업 데이타를 말끔히 청소하기 위한 방법으로 b2World 인스턴스를 제거하기만 하면 포함된 모든 body들을 삭제하는 수고를 덜 수 있도록 해준다. 그러나 당신은 게임 엔진에서 body포인터들을 무력화 하기위해 따로 참조를 유지하는것을 염두해 두어야 한다.

body를 삭제할때엔, 첨부된 shape들과 joint들이 자동적으로 함께 삭제된다. 이는 어떻게 당신이 shape과 joint의 포인터들을 관리할 것인가에 대해 중요한 의미가 담겨있다.

당신이 한 dynamic body를 땅위의 joint에 연결하길 원한다고 가정하자. 당신은 그 joint를 static body에 연결 할 필요가 있을것이다. 당신이 static body를 가지지 않았다면 당신은 world 객체로부터 ground body를 공유받을 수 있다. 또한 static shape들을 ground body에 첨부할수도 있다.
1 var ground:b2Body = myWorld.GetGroundBody();
2 //... build a joint using the ground body ...

6.9. Using a Body
body를 생성한 이후엔, 당신이 body에 행할 수 있는 많은 운용들이 있다. 질량 속성을 할당하는것이나, 포지션이나 속도에 접근하는것, 힘을 적용하는것, 포인터와 벡터들의 변형들이 이것에 포함된다.

6.10. Mass Data
당신은 런타임시 body의 질량을 적용할 수 있다. 질량값은 보통 생성할때나 body의 shape들을 삭제할때 결정된다. 당신은 현제 첨부된 shape들을 기준으로 body의 질량을 적용하길 원할 수도 있다.
1 b2Body.SetMassFromShapes():void;
당신은 질량 속성을 직접적으로 설정하길 원할 수도 있다. 예를들면 당신이 shape을 변경해도 당신의 질량 공식을 사용하기 원할지도 모른다.
1 b2Body.SetMass(massData:b2MassData):void;
body의 질량 데이타는 다음과 같은 함수들을 통해서 이용할 수있다.
1 b2Body.GetMass():Number;
2 b2Body.GetInertia():Number;
3 b2Body.GetLocalCenter():b2Vec;

6.11. State Infomation
Box2D는 body의 상태변화를 로깅한다. 당신은 다음과 같은 함수들을 통해서 이런 상태값에 효율적으로 접근할 수 있다.
01 b2Body.IsBullet():Boolean;
02 b2Body.SetBullet(flag:Boolean):void;
03  
04   
05 b2Body.IsStatic():Boolean
06 b2Body.IsDynamic():Boolean;
07   
08 b2Body.IsFrozen():Boolean;
09  
10   
11 b2Body.IsSleeping():Boolean;
12 b2Body.AllowSleeping(flag:Boolean):void;
13 b2Body.WakeUp():void;
bullet 상태는 Bullets에 서술되어 있다. frozen 상태는 World Boundary에 서술되어있다.

6.12. Position and Velocity
당신은 body의 포지션과 로테이션에 접근한다. 게임에 나오는 주인공을 그릴때 이는 공통적인 부분이다. 비록 '이동'을 시뮬레이션 하기위해 Box2D를 사용할 때까지 이는 덜 공통적이겠지만,  포지션을 셋팅할 수도 있다.
1 SetXForm(position:b2Vec2, angle:Number):Boolean;
2 GetXForm():b2XForm;
3 GetPosition():b2Vec2;
4 GetAngle():Number;
world 좌료계에서 당신은 질량의 중심 위치에 접근할 수도 있다. Box2D의 내부적인 시뮬레이션 대부분은 질량 중심을 사용한다. 그러나 당신은 일반적으로 그것에 접근할 필요는 없을 것이다. 그 대신에 당신은 보통 body transform 으로 작업하게 될것이다.
1 GetWorldCenter():b2Vec2;
당신은 linear veocity(선속도) 와 angular velocity(각속도) 에 접근할 수 있다. linear velocity는 질량 중심에 작용한다.
?
1 function SetLinearVelocity(v:b2Vec2):void;
2  
3 function GetLinearVelocity():b2Vec2;
4 function SetAngularVelocity(omega:Number):void;
5 function GetAngularVelocity():Number;

6.13. Forces and Impulses
body에 force나 torque, impulse를 적용할 수 있다. force나 impulse를 적용할때, 적용될 지점을 지정해줘야 한다. torque는 보통 질량 중심에 작용된다.
1 ApplyForce(force:b2Vec2, point:b2Vec2):void;
2 ApplyTorque(float32 torque):void;
3 ApplyImpulse(impulse:b2Vec2, point:b2Vec2):void;
force와 torque, impulse를 지정하면 body는 깨어난다. 때때로 이는 달갑지 않을것이다. 예를들면 지속적인 force를 적용하고 성능을 유지시키기 위해서 body를 sleep상태로 만들길 원할때이다. 이런 경우에 아래와 같이 사용할 수 있다.
1 if (myBody.IsSleeping() == false)
2 {
3     myBody.ApplyForce(myForce, myPoint);
4 }

6.14. Coordinates Transformations
body 클래스는 점이나 벡터들을 local과 world 좌표계 사이에서 변환하도록 도와주는 함수들을 제공한다. 만약 이런 컨셉을 이해 못한다면 Jim Van Verth 와 Lars Bishop이 저술한 "Essential Mathematics for Games and Interactive Applications"를 읽어보길 권장한다. 이러한 함수들은 효율적이며 큰 도움이 될것이다.
1 GetWorldPoint(localPoint:b2Vec2):b2Vec2;
2 GetWorldVector(localVector:b2Vec2):b2Vec2;
3 GetLocalPoint(worldPoint:b2Vec2):b2Vec2;
4 GetLocalVector(worldVector:b2Vec2):b2Vec2;

6.15. Lists
body의 shape들에 반복적인 접근이 있을때 활용할 수 있다. 이는 shape에 포함된 유저 데이타에 접근을 필요로할때 유용하다.
1 for (var s:b2Shape = body.GetShapeList(); s; s = s.GetNext())
2 {
3     var data:MyShapeData = s.GetUserData() as MyShapeData;
4     ... do something with data ...
5  
6 }
비슷한 방법으로 body의 joint list도 사용할 수 있다.

7. Shapes
7.1. About
shape은 body에 첨부된 충돌검사용 기하체이다. body의 질량을 정의하는데에 사용되기도 한다. 질량은 직접 지정할 수도 있고 Box2D가 질량 속성들을 계산할 수도 있다.

shape에는 friction(마찰력)과 restitution(반발계수) 이라는 속성을 가지고 있다. 게임내의 물체들과 충돌들을 방지하기 위한 '충돌 필터 정보'도 가지고 있다.

shape은 항상 body가 가지고 있다. 한 개의 body에 여러 shape들을 첨부할 수 있다. shape은 추상클래스들이라서 Box2D에는 많은 종류의 shape들이 구현될 수 있다. 용기가 있다면 직접 shape 타잎(그리고 충돌 알고리즘)을 구현할 수도 있다.

7.2. The Shape Definition
shape definition은 shape을 생성하기 위해 사용된다. b2ShapeDef 에 의해 유지되는 common shape data 와 파생 클래스들에 의해서 유지되는 specific shape data가 있다.

7.3. Friction and Restitution
마찰은 물체가 각각 현실적인 미끄러짐을 하도록 만들때 사용된다. Box2D는 static friction과 dynamic friction을 제공하지만 매개변수는 동일하게 사용한다. Box2D에서 마찰은 정교하게 시뮬레이딩 되고 그 강도는 일반적인 force에 정비례한다(이를 Coulomb friction이라고 부른다). 마찰계수는 보통 0에서 1 사이값이다. 0은 마찰이 없는것을 의미하며 그 반대는 마찰의 강도를 나타낸다. 두 shape 사이에서 마찰을 계산할때, Box2D는 두 shape의 마찰계수를 합해야 한다. 아래의 공식으로 처리한다.
1 var friction : Number = Math.sqrt(shape1.friction * shape2.friction);
반발력은 물체가 튕길때 사용한다. 반발값은 보통 0에서 1 사이의 값으로 셋팅한다. 공을 테이블 위에 떨어트린다고 가정해보자. 반발값이 0이면 그 공은 튀어 오르지 않는다는 의미이다. 이를 inelastic collision(비탄성 충돌)이라고 부른다.  그 값이 1이면 공의 속도는 정확히 반사된 값이된다. 이를 perfectly elastic collision이라고 부른다. 반발은 아래와 같은 공식을 사용하여 합산되어진다.
1 var restitution: Number = Math.Max(shape1.restitution, shape2.restitution);
shape이 여러 접점을 가질때 반발력은 근사치로 시뮬레이팅 된다. 왜냐하면 Box2D는 인터렉티브 해결사를 사용하기 위해서다. 또한 충돌 속도가 작을 때 Box2D는 비탄성 충돌을 사용한다. 이는 jitter(덜덜 떨리는것)를 예방하기 위한 작업이다.

7.4. Density
Box2D는 부가적으로 body의 질량과 회전 관성량을 첨부된 shape들에 의해 함축된 질량 분포를 사용하여 계산한다. 질량을 직접적으로 명시하는것은 종종 형편없는 시뮬레이션 결과를 가져올 수 있다. 그러므로 body 질량을 명시하는것의 권장 방법은 shape들의 밀도들을 셋팅함으로써 지정하는것과, b2Body.SetMassFromShape 를 body에 첨가될 모든 shape이 첨부된 이후 한번 호출하는것이다.

7.5. Filtering
충돌 필터링은 shape들 사이에서의 충돌을 방지하기위한 시스템이다. 예를들면 자전거를 타는 캐릭터를 만들때이다. 자전거가 지형의 충돌하도록 원할것이고 캐릭터가 지형과 충돌하도록 만들고 싶겠지만 캐릭터가 자전거와 충돌되길 원하진 않을 것이다(왜냐하면 그 둘은 서로 오버랩되어야 하니까). Box2D는 category와 group들을 이용하여 충돌 필터링과 같은 것을 지원한다.

Box2D는 16개의 충돌 범주를 제공한다. shape 각각 속한 카테고리를 지정한다.  그리고 이 shape가 어떤 카테고리의 것들과 충돌하는지도 지정할 수 있다. 예를들면 멀티플레이 게임에서 모든 플레이어가 서로 충돌하지 않고 몬스터들이 서로 충돌하지 않지만 플레이어와 몬스터들은 서로 충돌하도록 지정하는것이다. 이것은 마스킹 비트(masking bit)로 이루어진 것이다. 예를들어보자.
1 playerShapeDef.filter.categoryBits   = 0x0002;
2 monsterShapeDef.filter.categoryBits  = 0x0004;
3 playerShapeDef.filter.maskBits       = 0x0004;
4 monsterShapeDef.filter.maskBits      = 0x0002;
충돌 그룹은 그룹 인덱스를 설정할 수 있도록 한다. 같은 그룹의 모든 shaoe들을 항상 충돌하도록(양수 인덱스) 또록 항상 그렇지 않도록(음수 인텍스) 할 수 있다. 그룹 인덱스들은 자전거의 부품처럼 보통 어떻게든 연관된 것들을 위해서 사용되어진다. 아래의 예제에서는 shape1와 shape2가 항상 충돌하지만 shape3와 shape4는 절대 충돌하지 않는다.
1 shape1Def.filter.groupIndex = 2;
2 shape2Def.filter.groupIndex = 2;
3 shape3Def.filter.groupIndex = -8;
4 shape4Def.filter.groupIndex = -8;
서로 다른 그룹 인덱스를 가진 shape끼리의 충돌은 카테고리와 마스크 비트에 따라서 필터링된다. 다시 말하면, 그룹 필터링은 카테고리 필터링보다 더 연산 우선순위(precendence)가 높다.

Box2D에서 발생하는 추가적인 충돌 필터링을 메모해두자.
  • static body에 첨부된 shape은 다른 static body에 첨부된 shape와 절대 충돌하지 않는다.
  • 같은 body에 첨부된 shape은 절대 서로 충돌하지 않는다.
  • 하나의 joint에 의해서 연결된 body들에 첨부된 shape사이의 충돌은 추가적으로 켤수도, 끌수도 있다.
가끔 당신은 shape가 생성된 이후에 충돌 필터링을 변경하는것을 필요로 할지도 모른다. 이미 생성된 shape에서 b2Shape.GetFilterData 그리고 b2Shape.SetFilterData 함수를 이용해서 b2FilterData 구조체를 설정할 수도, 받아올 수도 있다. Box2D는 결과값은 캐싱하므로 당신은 수동으로 다시 shape를 b2World.Refilter를 사용해서 필터링 해야한다.

7.6. Sensors
때때로 게임 로직은 두 shape가 충돌하기 전에 겹치는지 알아보길 필요로 한다. 이는 센서(sensor)를 사용한다. 센서는 충돌을 감지하는 하나의 shape이지만 충돌에 대한 응답을 만들지는 않는다.

어떤 shape라도 센서가 되도록 깃발을 꼽아둘 수 있다. 센서들은 static 이나 dynamic이 될것이다. 당신은 body에 여러개의 shape를 담을 수 있고 센서인 shape와 그렇지 않은 shape들을 섷어둘 수 있다는 것을 기억해라.
1 myShapeDef.isSensor = true;

7.7. Circle Definitions
b2CircleDef 는 b2ShapeDef 를 상속받았고 반지름과 로컬 포지션을 추가한 것이다.
1 var def:b2CircleDef;
2 def.radius = 1.5;
3 def.localPosition.Set(1.00.0);

7.8. Polygon Definitions
b2PolyDef는 convex polygon(역자주: 모든 내각이 180도 미만인 다각형)을 구현하기 위해서 사용된다. 정확하게 사용하기 위해 비트 꼼수가 있으므로 침착하게 읽어보라. 최대 vertex(꼭지점) 숫자는 초기값이 8인 b2_maxPolyVertices에 의해서 정의된다. 만약 더 많은 vertex가 필요하다면 b2Settings 파일 안에있는 b2_maxPolyVertices 를 수정해야 한다.

polygon definition을 생성할때 당신은 사용할 vertex의 숫자를 명시해야 한다. vertex들은 오른손 좌표계의 z축에 대해서 반시계방향으로 정의되어야 한다. 게임에서 사용하는 좌표계 변환에 의존해서 이는 화면에 시계방향으로 바뀔지도 모른다.

다각형은 convex여야 한다. 즉, 각 꼭지점은 내각이 180 미만이어야 한다. 마지막으로 vertex는 겹치지 않도록 해야한다. Box2D는 자동적으로 반복을 종료한다. (역자주: OpenGL에서의 다각형 지정 방식과 동일하다)

여기에 삼각형의 polygon definition의 예제가 있다.
1 var triangleDef:b2PolygonDef = b2PolygonDef()
2 triangleDef.vertexCount = 3
3  
4 triangleDef.vertices[0].Set(-1.00.0)
5 triangleDef.vertices[1].Set(1.00.0)
6  
7 triangleDef.vertices[2].Set(0.02.0)
vertex들은 부모 body의 좌표계 안에서 정의되어진다. 부모 body에서 한 다각형에 offset을 필요로 한다면 그 다각형의 모든 vertex에 offset을 주면 된다.

편의를 위해서 box같은 다각형은 다각형 초기화를 위한 함수들이 있다. body의 원점 가운데 축정렬된 box나 body의 원점으로 부터 offset을 가진 oriented box를 생성하기 위한 함수들도 있다.
01 var alignedBoxDef:b2PolygonDef = new b2PolygonDef();
02  
03 var hx:Number 1.0// half-width
04 var hy:Number 2.0// half-height
05  
06 alignedBoxDef.SetAsBox(hx, hy);
07   
08 var orientedBoxDef:b2PolygonDef = new b2PolygonDef();
09 var center:b2Vec2 = new b2Vec2(-1.50.0);
10  
11 var angle:Number 0.5 * b2Settings.b2_pi;
12 orientedBoxDef.SetAsOrientedBox(hx, hy, center, angle);

7.9. Shape Factory
shape들은 shape definition을 초기화하는것으로 생성되어지고 그런다음엔 그 definition이 그 부모 body로 전달된다.
1 var circleDef:b2CircleDef = new b2CircleDef();
2 circleDef.radius = 3.0;
3 circleDef.density = 2.5;
4  
5 var myShape:b2Shape = myBody.CreateShape(circleDef);
6 //[optionally store shape object somewhere]
이는 shape를 생성하고 body에 첨부한다.  body가 삭제되면서 shape가 자동적으로 삭제될때까지 shape의 포인터를 저장해둘 필요가 없다.

body에 shape들을 추가가 모두 끝난 이후에는 첨부된 shape에 기반한 질량값을 다시 계산하는것을 원할지도 모른다.
1 myBody.SetMassFromShapes()
이 함수는 비싼 연산이므로 꼭 필요할 때만 사용하라.

당신은 부모 body에서 shape를 쉬게 삭제할 수 있다. 깨질수있는 모델로 설정하면 된다. 반면에 당신은 shape를 내버려두고 body가 첨부된 모든 shape들의 삭제를 돌보도록 할 수있다.
1 myBody.DestroyShape(myShape)
body에서 shape들이 삭제된 이후 당신은 SetMassFromShapes를 다시 호출하길 원할지도 모르겠다.

7.10. Using a Shape
여기에 할말은 그닥 많지 않다. shape들의 형식과 그들의 부모 body를 받을 수 있다. 또한 한 지점이 shape에 포함되는지를 확인하기위해 테스트할 수도 있다. 자세한것은 b2Shape.as 를 들여다 보라.

WRITTEN BY
buzzler

,