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

,