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

,