2014년 3월 1일 토요일

[일기]Box2D. 충돌 콜백.

#ifndef __MYCOLLISIONCALLBACKTEST_H__
#define __MYCOLLISIONCALLBACKTEST_H__

class Ball3 {
public:
b2Body* m_body;
float m_radius;
int m_contacting;
bool m_imIt;

Ball3(b2World* world, float radius) {

m_body = NULL;
m_radius = radius;
m_contacting = 0;
m_imIt = false;

b2BodyDef myBodyDef;
myBodyDef.type = b2_dynamicBody;
myBodyDef.position.Set(0, 20);
m_body = world->CreateBody(&myBodyDef);

b2CircleShape circleShape;
circleShape.m_p.Set(0, 0);
circleShape.m_radius = m_radius;

b2FixtureDef myFixtureDef;
myFixtureDef.shape = &circleShape;
myFixtureDef.density = 1;
myFixtureDef.restitution = 1;

m_body->CreateFixture(&myFixtureDef);

m_body->SetUserData( this );
}
~Ball3(){
}

void render(){

b2Vec2 vel = m_body->GetLinearVelocity();
float red = vel.Length() / 20.0;
red = b2Min( 1.0f, red );
glColor3f(red,0.5,0.5);

if( m_imIt )
glColor3f(1,1-red,red);//white
else {
glColor3f(1, 1, 1);
}

//nose and eyes
glPointSize(4);
glBegin(GL_POINTS);
glVertex2f( 0, 0 );
glVertex2f(-0.5, 0.5 );
glVertex2f( 0.5, 0.5 );
glEnd();

//mouth
glBegin(GL_LINES);
glVertex2f(-0.5,  -0.5 );
glVertex2f(-0.16, -0.6 );
glVertex2f( 0.16, -0.6 );
glVertex2f( 0.5,  -0.5 );
glEnd();

//circle outline
glBegin(GL_LINE_LOOP);
for (float a = 0; a < 360 * DEGTORAD; a += 30 * DEGTORAD)
glVertex2f( sinf(a), cosf(a) );
glEnd();
}

void renderAtBodyPosition() {
b2Vec2 pos = m_body->GetPosition();
float angle = m_body->GetAngle();

glPushMatrix();
glTranslatef( pos.x, pos.y, 0 );
glRotatef( angle * RADTODEG, 0, 0, 1 );
glScalef( m_radius, m_radius, 1 );
render();
glPopMatrix();
}

void startContact() { m_contacting++; }
void endContact() { m_contacting--; }
};

//색 교환
void handleContact(Ball3* b1, Ball3* b2){
bool temp = b1->m_imIt;
b1->m_imIt = b2->m_imIt;
b2->m_imIt = temp;
}

std::vector<Ball3*> balls3;

class MyCollisionCallback : public b2ContactListener {
void BeginContact(b2Contact* contact){
//check if both fixtures were balls
void* bodyAUserData = contact->GetFixtureA()->GetBody()->GetUserData();
void* bodyBUserData = contact->GetFixtureB()->GetBody()->GetUserData();
if ( bodyAUserData && bodyBUserData )
handleContact( static_cast<Ball3*>( bodyAUserData ),
static_cast<Ball3*>( bodyBUserData ) );
}
void EndContact(b2Contact* contact){

}
};

MyCollisionCallback myContactLinstenerInstance;

class MyCollisionCallBackTest : public Test {
public:
MyCollisionCallBackTest(){
for(int i = 0; i < 20; i++){
float radius = 1 + 2 * (rand()/(float)RAND_MAX); //random between 1 - 3
Ball3* ball = new Ball3(m_world, radius);
balls3.push_back(ball);
}
balls3[0]->m_imIt = true;
m_world->SetGravity( b2Vec2(0, 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);
//body = m_world->CreateBody(&myBodyDef);
//body->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);

m_world->SetContactListener(&myContactLinstenerInstance);
}

void Step(Settings* settings){
Test::Step(settings);
for( int i = 0; i < balls3.size(); i++)
balls3[i]->renderAtBodyPosition();
}

        //마우스로 물체를 이동하려 할 때 마우스 클릭 지점이 실제 눈에 보이는 곳과 달라 그 위치를 보정해서 다시 콜백을 호출하고 있다.
void MouseDown(const b2Vec2& p){
b2Vec2 pp = p;
pp.y = pp.y -1;

Test::MouseDown(pp);
}

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

#endif

무턱대고 나만 이해가능한 코드를 올린다.. 이러면 안 되는데..


흠, 충돌 콜백을 등록하기 위해 b2ContactListener를 상속한 클래스를 만들고 이를 계 클래스가 사용하도록 해야 하기에

 m_world->SetContactListener(&myContactLinstenerInstance); 를 이용했다.

 전에 언급하였듯이, 상호 강체간의 AABB가 겹치면 BeginContact가 호출되고, 겹칩이 사라지면 EndContact가 호출된다. 이 외 다른 가상 메서드들도 있었으나 이 둘만 오버라이드했다.

 이 콜백은 시연(스텝) 단계 중간에 호출된다. 호출이 끝나고 다시 원 시연(스텝) 단계의 흐름으 돌아가므로 이곳에서 조작할 수 있는 행위는 몇 가지로 제한된다. 가령 화면을 새로 그린다거나 하는 작업은, 해당 충돌은 처리되었어도 아직 다른 충돌은 처리되지 않았을 수도 있기 때문에 예상치 못한 화면을 그릴 수 있다.

 위 예제는 충돌시 색을 교환하는 내용을 담고 있다.

 충돌한 강체를 판별하기 위해 사용자 데이터를 이용하였다. Ball3 클래스를 그 데이터로 넣었는데, 실제 개발시에는 다형성을 이용하여 사용자 데이터를 넣은 후, 이후 typeid 매크로를 이용해서 어떤 포인터인지를 확인한 뒤 처리하는 것이 좋을 것 같다.

 http://en.wikipedia.org/wiki/Typeid 를 참고해야겠다.

댓글 없음:

댓글 쓰기