2014년 3월 5일 수요일

[일기]Box2D, Revolute Joint 연습.

http://www.iforce2d.net/b2dtut/joints-revolute에서 참고했다.

이것은

 바퀴, 롤러, 사슬, 선개교, 봉제 인형, 회전문, 투석기, 레버

등에 이용된다.

localAnchorA - 강체 A가 그 주변에서 회전할 정점
localAnchorB - 강체 B가 그 주변에서 회전할 정점
referenceAngle - 처음의 강체간 각도를 0으로(변경 가능) 하는 접합점의 회전각
enableLimit - 접합점의 회전제한 여부
lowerAngle - 하한각
upperAngle - 상한각
enableMotor - 접합점의 모터의 활성화 여부
motorSpeed - 모터의 목적 속도
maxMotorTorque - 모터가 사용가능한 최대 토크의 크기

을 속성으로 가진다.

 - 지역 거점


  revoluteJointDef.localAnchorA.Set(1,1);
  revoluteJointDef.localAnchorB.Set(0,0);

  사각형이 A, 원이 B

이 값은 GetAnchorA() 와 GetAnchorB() 메서드를 통해 얻을 수 있다.

 주의할 것은 고정체를 연결하는 것이 아닌 강체를 연결한다는 것이다.

 항상 Box2D에서 위 두 강체를 접합시킬 수 있는 것은 아니며, 지속적으로 우측의 그림과 같이 형상을 유지하도록 힘을 가한다.

 - 참조 각


  revoluteJointDef.referenceAngle = 0;  //기본값이기도 하다.

 이 각은 GetJointAngle() 메서드를 통해 가능한데 얻는 값은 referenceAngle의 초기 설정값에 따라 그림의 우측과 같이 다르게 출력된다.



 이 각은 강체간 사이의 각도에 관계되어 있으므로 위와 같이 저 전체가 회전하였다고 하여도 GetJointAngle() 메서드의 출력값은 변하지 않는다.

 - 회전각 제한

  revoluteJointDef.enableLimit = true;
  revoluteJointDef.lowerAngle = -45 * DEGTORAD;
  revoluteJointDef.upperAngle =  45 * DEGTORAD;

 제한을 걸게 되면, 상한, 하한 모두 같이 제한이 설정된다. 즉, 하나만 제한을 걸 수 없다. 따라서 상한만 제한을 걸고자 한다면, 하한의 제한값을 크게 낮게 잡아야 한다.

  void EnableLimit(bool enabled);
  void SetLimits( float lower, float upper );
  
  bool IsLimitEnabled();
  float GetLowerLimit();
  float GetUpperLimit();

 이 메서드로 실행 중 값을 얻어올 수 있다.

 물론 이 또한 큰 속도로 강체가 회전하고 있었을 경우, 제한을 넘겼을 때, 잠깐 초과된 모습이 화면에 그려질 수 있으나 곧 Box2D가 제한점으로 위치시킨다.

 현재 접합점이 제한점 상에 있는지를 확인하는 방법은 간단하며 다음과 같다.

bool atLowerLimit = joint->GetJointAngle() <= joint->GetLowerLimit();
bool atUpperLimit = joint->GetJointAngle() >= joint->GetUpperLimit();

 - 모터

 기본 접합점의 행동방식은 저항 없이 회전하는 것이다. 허나 모터를 적용하여 이를 회전시킬 수 있다. 회전은 각속도를 명시함으로 이루어진다. 단 이는 최대 도달가능한 각속도를 명시하는 것이며 즉시 속도를 설정하는 것이 아니다.

 회전을 시키는 것은 토크의 크기를 지정함으로써 이루어진다. 이 말은 반드시 최대 각속도에 도달할 수 있다는 보장이 없다는 것이다.

  revoluteJointDef.enableMotor = true;
  revoluteJointDef.maxMotorTorque = 20;
  revoluteJointDef.motorSpeed = 360 * DEGTORAD;


 //alter joint motor
  void EnableMotor(bool enabled);
  void SetMotorSpeed(float speed);
  void SetMaxMotorTorque(float torque);
  
  //query joint motor
  bool IsMotorEnabled();
  float GetMotorSpeed();
  float GetMotorTorque();

최대 각속도를 0으로 지정할 경우 모터는 브레이크와 같은 역할을 하게 된다.

Testbed에는 0, 0에 정적 강체가 멤버로 존재한다. 그 이름은 m_goundBody  이다.

다음은 맨 상위 주소에서 제공한 튜토리얼의 결과이다.

접합점을 연결한 강체 두개와, 체인을 하나 생성해 본다.

#ifndef __MYREVOLUTEJOINTTEST_H__
#define __MYREVOLUTEJOINTTEST_H__

class MyRevoluteJointTest : public Test {
public:
//각각 강체와 접합점의 포인터.
b2Body* m_bodyA, *m_bodyB;
b2RevoluteJoint* m_joint;
MyRevoluteJointTest(){

//이하는 맵을 정의
//body definition
b2BodyDef myBodyDef;
myBodyDef.type = b2_dynamicBody;

//shape definition
b2PolygonShape polygonShape;
polygonShape.SetAsBox(1, 1); //a 2x2 rectangle

//fixture definition
b2FixtureDef myFixtureDef;
myFixtureDef.shape = &polygonShape;
myFixtureDef.density = 1;

//create dynamic body
myBodyDef.position.Set(0, 10);
//pBody1 = m_world->CreateBody(&myBodyDef);
//pBody1->CreateFixture(&myFixtureDef);

//a static body
myBodyDef.type = b2_staticBody;
myBodyDef.position.Set(0, 0);
b2Body* staticBody = m_world->CreateBody(&myBodyDef);

//add four walls to the static body
polygonShape.SetAsBox( 20, 1, b2Vec2(0, 0), 0);//ground
staticBody->CreateFixture(&myFixtureDef);
polygonShape.SetAsBox( 20, 1, b2Vec2(0, 40), 0);//ceiling
staticBody->CreateFixture(&myFixtureDef);
polygonShape.SetAsBox( 1, 20, b2Vec2(-20, 20), 0);//left wall
staticBody->CreateFixture(&myFixtureDef);
polygonShape.SetAsBox( 1, 20, b2Vec2(20, 20), 0);//right wall
staticBody->CreateFixture(&myFixtureDef);

//이하는 두 강체를 연결하는 단계,


//body and fixture defs - the common parts
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
b2FixtureDef fixtureDef;
fixtureDef.density = 1;

//two shapes
b2PolygonShape boxShape;
boxShape.SetAsBox(2,2);
b2CircleShape circleShape;
circleShape.m_radius = 2;     

//make box a little to the left
bodyDef.position.Set(-3, 10);
fixtureDef.shape = &boxShape;
m_bodyA = m_world->CreateBody( &bodyDef );
m_bodyA->CreateFixture( &fixtureDef );

//and circle a little to the right
bodyDef.position.Set( 3, 10);
fixtureDef.shape = &circleShape;
fixtureDef.friction = 0.5;
m_bodyB = m_world->CreateBody( &bodyDef );
m_bodyB->CreateFixture( &fixtureDef );

b2RevoluteJointDef revoluteJointDef;
revoluteJointDef.bodyA = m_bodyA;
revoluteJointDef.bodyB = m_bodyB;
revoluteJointDef.collideConnected = false;
revoluteJointDef.localAnchorA.Set(2,2);//the top right corner of the box
revoluteJointDef.localAnchorB.Set(0,0);//center of the circle

//revoluteJointDef.localAnchorA.Set(4,4);//the top right corner of the box
//revoluteJointDef.localAnchorB.Set(-2,0);//center of the circle



//revoluteJointDef.enableLimit = true;
//revoluteJointDef.lowerAngle = -45 * DEGTORAD;
//revoluteJointDef.upperAngle =  45 * DEGTORAD;

revoluteJointDef.enableMotor = true;
revoluteJointDef.maxMotorTorque = 225;
revoluteJointDef.motorSpeed = 1390 * DEGTORAD;//90 degrees per second

m_joint = (b2RevoluteJoint*)m_world->CreateJoint( &revoluteJointDef );

{//이하는 체인 생성을 위한 단계
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(5,10);
b2FixtureDef fixtureDef;
fixtureDef.density = 1;
b2PolygonShape polygonShape;
polygonShape.SetAsBox(1,0.25);
fixtureDef.shape = &polygonShape;

//create first link
b2Body* link = m_world->CreateBody( &bodyDef );
link->CreateFixture( &fixtureDef );

//set up the common properties of the joint before entering the loop
b2RevoluteJointDef revoluteJointDef;
revoluteJointDef.localAnchorA.Set( 0.75,0);
revoluteJointDef.localAnchorB.Set(-0.75,0);


//use same definitions to create multiple bodies
for (int i = 0; i < 10; i++) {
b2Body* newLink = m_world->CreateBody( &bodyDef );
newLink->CreateFixture( &fixtureDef );

//...joint creation will go here...
//inside the loop, only need to change the bodies to be joined
revoluteJointDef.bodyA = link;
revoluteJointDef.bodyB = newLink;
m_world->CreateJoint( &revoluteJointDef );

link = newLink;//prepare for next iteration
}

//body with circle fixture
b2CircleShape circleShape;
circleShape.m_radius = 2;
fixtureDef.shape = &circleShape;
b2Body* chainBase = m_world->CreateBody( &bodyDef );
chainBase->CreateFixture( &fixtureDef );

//a revolute joint to connect the circle to the ground
revoluteJointDef.bodyA = m_groundBody;//provided by testbed
revoluteJointDef.bodyB = chainBase;
revoluteJointDef.localAnchorA.Set(4,20);//world coords, because m_groundBody is at (0,0)
revoluteJointDef.localAnchorB.Set(0,0);//center of circle
m_world->CreateJoint( &revoluteJointDef );

//another revolute joint to connect the chain to the circle
revoluteJointDef.bodyA = link;//the last added link of the chain
revoluteJointDef.bodyB = chainBase;
revoluteJointDef.localAnchorA.Set(0.75,0);//the regular position for chain link joints, as above
revoluteJointDef.localAnchorB.Set(1.75,0);//a little in from the edge of the circle
m_world->CreateJoint( &revoluteJointDef );
}
}

void Step(Settings* settings){
Test::Step(settings);
}

void MouseDown(const b2Vec2& p){
b2Vec2 pp = p;
pp.y = pp.y -1;

Test::MouseDown(pp);
}

static Test* Create(){
return new MyRevoluteJointTest();
}
};

#endif

이상. 기록 끝.

이거 만든 사람은 천재여?

댓글 없음:

댓글 쓰기