2014년 3월 4일 화요일

[일기]Box2D. jump ability.

흠... http://www.iforce2d.net/b2dtut/jumpability 에서 참고한 점프 판단법.

첫째, 충돌 컴백을 이용, 사용자 데이터 및 충동 발향을 보고(발쪽인지 머리쪽인지) 점프 가능성을 판단.

 -> 충돌 콜백은 상당히 자주 일어나고, 방향 판단하고 다 하는게 골치 아픔.

둘째, 감지기를 이용. 사용자 데이터를 이용하고 감지기가 보통 발 밑에 위치하고 그 크기가 작다는 점을 이용. 감지기에 물체가 충돌한 경우만 점프가 가능하도록 판단.

 =>정수형 전역 변수를 선언해 놓고 감지기에 물체가 충돌한 경우, 이 변수의 값을 증가시키고 감지기와 물체가 충돌을 끝낸 경우 이 변수의 값을 감소시킴. 그리고 점프 조건 판단시, 변수의 값이 양수인 경우에만 가능하다 판단.


 물체의 굴곡에 따라 감지기의 모양을 변형시켜 자연스러운 모습을 연출시킬 수도 있다.

 정수형 변수를 이용하는 대신, 감지기에 닿은 물체를 리스트와 같은 자료구조에 넣어 이 자료의 크기가 0이 아닐 경우에만 점프하도록 조건을 달리 할 수 있다.

 이 경우, 밑에 있는 물체가 무엇인지 판단할 수 있기 때문에, 특정 물체 위에서는 점프를 못하거나, 높은 점프를 하거나, 점프를 할 때 밑에 있는 물체에 반작용이 가해지거나 하는 등의 처리가 가능해진다.

 참고로, 맵은 상당히 다양한 고정체들의 집합일 수 있으므로 저장하는 물체는 여기서 이 고정체를 의미한다.

 아래는 위 사이트에서 제공한 학습 과정에 따른 결과물.


#ifndef __MYJUMPINGQUESTIONTEST_H__
#define __MYJUMPINGQUESTIONTEST_H__

b2Body* pBody1;
int remainingJumpSteps;
//global scope
int numFootContacts;

std::set<b2Fixture*> fixturesUnderfoot;

class MyContactListener3 : public b2ContactListener
{
  //주석은 점프를 변수로 체크하기 위해 값을 더하고 빼는 부분
    /*void BeginContact(b2Contact* contact) {
        //check if fixture A was the foot sensor
        void* fixtureUserData = contact->GetFixtureA()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            numFootContacts++;
        //check if fixture B was the foot sensor
        fixtureUserData = contact->GetFixtureB()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            numFootContacts++;
    }

    void EndContact(b2Contact* contact) {
        //check if fixture A was the foot sensor
        void* fixtureUserData = contact->GetFixtureA()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            numFootContacts--;
        //check if fixture B was the foot sensor
        fixtureUserData = contact->GetFixtureB()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            numFootContacts--;
    }*/

  //집합에 객체를 넣고 빼는 부분, 점프를 변수가 아닌 자료형의 크기로 판단하기 위함
  void BeginContact(b2Contact* contact) {
        //check if fixture A was the foot sensor
        void* fixtureUserData = contact->GetFixtureA()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            fixturesUnderfoot.insert(contact->GetFixtureB());//A is foot so B is ground
        //check if fixture B was the foot sensor
        fixtureUserData = contact->GetFixtureB()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            fixturesUnderfoot.insert(contact->GetFixtureA());//B is foot so A is ground
    }

    void EndContact(b2Contact* contact) {
        //check if fixture A was the foot sensor
        void* fixtureUserData = contact->GetFixtureA()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            fixturesUnderfoot.erase(contact->GetFixtureB());//A is foot so B is ground
        //check if fixture B was the foot sensor
        fixtureUserData = contact->GetFixtureB()->GetUserData();
        if ( (int)fixtureUserData == 3 )
            fixturesUnderfoot.erase(contact->GetFixtureA());//B is foot so A is ground
    }
};

MyContactListener3 myContactListener3;

class MyJumpQuestionTest : public Test {
public:
int m_jumpTimeout;

        //맵 생성하고, 주변 물체 만들고 하는 부분
MyJumpQuestionTest(){
m_jumpTimeout = 0;

//in constructor
numFootContacts = 0;


//body definition
b2BodyDef myBodyDef;
myBodyDef.position.Set(-5,5);
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;

for (int i = 0; i < 5; i++)
{
b2Fixture* fixture = m_world->CreateBody(&myBodyDef)->CreateFixture(&myFixtureDef);
fixture->SetUserData( (void*)1 );//tag square boxes as 1
}

//change size
polygonShape.SetAsBox(0.5, 1); //a 1x2 rectangle

for (int i = 0; i < 5; i++)
{
b2Fixture* fixture = m_world->CreateBody(&myBodyDef)->CreateFixture(&myFixtureDef);
fixture->SetUserData( (void*)2 );//tag smaller rectangular boxes as 2
}





remainingJumpSteps = 0;

//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 definition
b2BodyDef myBodyDef;
myBodyDef.type = b2_dynamicBody;
myBodyDef.fixedRotation = true;

//shape definition for main fixture
b2PolygonShape polygonShape;
polygonShape.SetAsBox(1, 2); //a 2x4 rectangle

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

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

//add main fixture
pBody1->CreateFixture(&myFixtureDef);

//add foot sensor fixture
polygonShape.SetAsBox(0.3, 0.3, b2Vec2(0,-2), 0);
myFixtureDef.isSensor = true;
b2Fixture* footSensorFixture = pBody1->CreateFixture(&myFixtureDef);
footSensorFixture->SetUserData( (void*)3 );
}

m_world->SetContactListener(&myContactListener3);
}

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

                //remainingJumpSteps가 양인 경우 힘을 계속 가하여 점프의 효과를 냈다.
if(remainingJumpSteps > 0){
//to change velocity by 10 in one time step
float force = pBody1->GetMass() * 10 / (1/60.0); //f = mv/t
//spread this over 6 time steps
force /= 6.0;
pBody1->ApplyForce( b2Vec2(0,force), pBody1->GetWorldCenter(), true );
remainingJumpSteps--;
}

//in Step function
m_debugDraw.DrawString(5, m_textLine, "Can I jump here? %s", numFootContacts>0?"yes":"no");
m_textLine += 15;

m_jumpTimeout --;
}

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

Test::MouseDown(pp);
}

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

void Keyboard(unsigned char key){

switch(key) {

                        //속도를 넣어서 점프하게 함.
case 'j':
//if ( numFootContacts < 1 ) break;
if ( !CanJumpNow() ) break;
{
b2Vec2 vel = pBody1->GetLinearVelocity();
vel.y = 10;
pBody1->SetLinearVelocity( vel );
break;
}
                        //힘을 가하여 점프하게 함
case 'k': //jump
//if ( numFootContacts < 1 ) break;
if ( !CanJumpNow() ) break;
remainingJumpSteps = 6;// 1/10th of a second at 60Hz
break;
                         //충격을 가하여 점프하게 함
case 'i':
//if ( numFootContacts < 1 ) break;
if ( !CanJumpNow() ) break;
if (m_jumpTimeout > 0) break;
{
//to change velocity by 10
float impulse = pBody1->GetMass() * 10;
pBody1->ApplyLinearImpulse( b2Vec2(0,impulse), pBody1->GetWorldCenter(), true );
m_jumpTimeout = 15;


                          //이 경우 아래 물체들에게 반작용을 가하였다.
//kick the underfoot boxes downwards with an equal and opposite impulse
b2Vec2 locationOfImpulseInPlayerBodyCoords(0, -2);//middle of the foot sensor
b2Vec2 locationOfImpulseInWorldCoords =
pBody1->GetWorldPoint(locationOfImpulseInPlayerBodyCoords);
std::set<b2Fixture*>::iterator it = fixturesUnderfoot.begin();
std::set<b2Fixture*>::iterator end = fixturesUnderfoot.end();
while (it != end)
{
b2Fixture* fixture = *it;
fixture->GetBody()->ApplyLinearImpulse(  b2Vec2(0, -impulse), locationOfImpulseInWorldCoords, true );
++it;
}
}
break;
default:
Test::Keyboard(key);
return;
}

}

        //특정 물체 위에서만 점프가 가능하도록 처리. 큰 상자와, 정적 강체 위에서만 가능.
bool CanJumpNow()
{
if ( m_jumpTimeout > 0 )
return false;
std::set<b2Fixture*>::iterator it = fixturesUnderfoot.begin();
std::set<b2Fixture*>::iterator end = fixturesUnderfoot.end();
while (it != end)
{
b2Fixture* fixture = *it;
int userDataTag = (int)fixture->GetUserData();
if ( userDataTag == 0 || userDataTag == 1 ) //large box or static ground
return true;
++it;
}
return false;
}
};

#endif

이건 내가 왜 본거지...

댓글 없음:

댓글 쓰기