2014년 3월 2일 일요일

[일기]Box2D. Sensor. 감지기.

Box2D에서 충돌 거르기를 하는 경우 충돌을 하지 않는다. 이것은 다음을 의미한다.

 어떠한 충돌 콜백도 호출되지 않는다.

 그러면 겹쳤을 때 하고자 하는 처리가 있을 때 직접 감지하지 않는다면 아무것도 할 수가 없다. 가령 방사능 입자가 몸을 뚫고 지나갈 때는 피폭당해야 한다.

 이러한 단점(?)을 보완하고자 나온 개념이 감지기이다.

한 고정체가 감지기로 설정되면, 타 고정체들과 겹치게 되어도 충돌을 하지 않도록 설정한 경우에도, 겹쳤을 때 무조건 충돌이 일어난 것과 같이 충돌 콜백을 제공한다. 충돌 반응만 없을 뿐이다.

(감지기도 고정체들과 마찬가지로 질량을 가지고 있다. 충돌을 하지 않을 뿐이다.)

 고정체에 isSensor 값에 true 값만 넣어주면 감지기와 같이 동작한다.

 프로그램에서 고정체가 이미 생성되어 실행중일 상황(상호작용하고 있는 상황)에서는 고정체 포인터 변수를 통해 SetSensor(bool) 메서드만 호출하면 끝난다.

 그런데 BeginContact, EndContact에서만 감지될 수 있다.

 사용 예에 관련한 Testbed 상의 코드를 예제를 따라 작성하여 보았다.

#ifndef __MYSENSORTEST_H__
#define __MYSENSORTEST_H__

enum _entityCategory {
BOUNDARY = 0x0001,
FRIENDLY_SHIP = 0x0002,
ENEMY_SHIP    = 0x0004,
FRIENDLY_AIRCRAFT = 0x0008,
ENEMY_AIRCRAFT    = 0x0010,
FRIENDLY_TOWER = 0x0020,
RADAR_SENSOR   = 0x0040,
};

//helper function to figure out if the collision was between
//a radar and an aircraft, and sort out which is which


class Ball5 {
public:
b2Body* m_body;
float m_radius;
b2Color m_color;
std::vector<Ball5*> visibleEnemies;

Ball5(b2World* world, float radius, b2Color color, uint16 categoryBits, uint16 maskBits) {
m_body = NULL;
m_radius = radius;
m_color = color;

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.filter.categoryBits = categoryBits;
myFixtureDef.filter.maskBits = maskBits;

m_body->CreateFixture(&myFixtureDef);

m_body->SetUserData( this );


}
~Ball5(){
}

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( visibleEnemies.size() > 0 ) {
glColor3f(1, 1, 0);
} else {
//glColor3f(red,1-red,1);//white
glColor3f(m_color.r, m_color.g, m_color.b);
}

//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();

//b2Vec2 pos = m_body->GetPosition(); //(existing code)
glColor3f(1,1,1);//white
glLineStipple( 1, 0xF0F0 ); //evenly dashed line
glEnable(GL_LINE_STIPPLE);
glBegin(GL_LINES);
for (int i = 0; i < visibleEnemies.size(); i++) {
b2Vec2 enemyPosition = visibleEnemies[i]->m_body->GetPosition();
glVertex2f(pos.x, pos.y);
glVertex2f(enemyPosition.x, enemyPosition.y);
}
glEnd();
glDisable(GL_LINE_STIPPLE);
}

void radarAcquiredEnemy(Ball5* enemy){
visibleEnemies.push_back(enemy);
}

void radarLostEnemy(Ball5* enemy){
visibleEnemies.erase( std::find(visibleEnemies.begin(), visibleEnemies.end(), enemy) );
}
};



std::vector<Ball5*> balls5;

bool getRadarAndAircraft(b2Contact* contact, Ball5*& radarEntity, Ball5*& aircraftEntity)
{
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();

//make sure only one of the fixtures was a sensor
bool sensorA = fixtureA->IsSensor();
bool sensorB = fixtureB->IsSensor();
if ( ! (sensorA ^ sensorB) )
return false;

Ball5* entityA = static_cast<Ball5*>( fixtureA->GetBody()->GetUserData() );
Ball5* entityB = static_cast<Ball5*>( fixtureB->GetBody()->GetUserData() );

if ( sensorA ) { //fixtureB must be an enemy aircraft
radarEntity = entityA;
aircraftEntity = entityB;
}
else { //fixtureA must be an enemy aircraft
radarEntity = entityB;
aircraftEntity = entityA;
}
return true;
};

//main collision call back function
class MyContactListener2 : public b2ContactListener
{
void BeginContact(b2Contact* contact) {
Ball5* radarEntity;
Ball5* aircraftEntity;
if ( getRadarAndAircraft(contact, radarEntity, aircraftEntity) )
radarEntity->radarAcquiredEnemy( aircraftEntity );
}

void EndContact(b2Contact* contact) {
Ball5* radarEntity;
Ball5* aircraftEntity;
if ( getRadarAndAircraft(contact, radarEntity, aircraftEntity) )
radarEntity->radarLostEnemy( aircraftEntity );
}
};

MyContactListener2 myContactListenerInstance2;

class MySensorTest : public Test {

public:
MySensorTest(){

b2Color red(1, 0, 0);
b2Color green(0, 1, 0);

Ball5* ship = new Ball5(m_world, 3, green, FRIENDLY_SHIP, BOUNDARY | FRIENDLY_TOWER);
//for(int i = 0; i < 3; i++)
balls5.push_back( ship );
//for(int i = 0; i < 3; i++)
//balls5.push_back(new Ball5(m_world, 3, red, ENEMY_SHIP, BOUNDARY | FRIENDLY_SHIP | ENEMY_SHIP) );
//for(int i = 0; i < 3; i++)
//balls5.push_back(new Ball5(m_world, 1, green, FRIENDLY_AIRCRAFT, BOUNDARY | ENEMY_AIRCRAFT) );
for(int i = 0; i < 3; i++)
balls5.push_back(new Ball5(m_world, 1, red, ENEMY_AIRCRAFT, BOUNDARY | RADAR_SENSOR) );

Ball5* tower = new Ball5(m_world, 1, green, FRIENDLY_TOWER, FRIENDLY_SHIP);
tower->m_body->SetType(b2_kinematicBody);
balls5.push_back( tower );



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

//shape definition
b2PolygonShape polygonShape;

//fixture definition
b2FixtureDef myFixtureDef;
myFixtureDef.shape = &polygonShape;
myFixtureDef.filter.groupIndex = -1;

//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);

//add rader sensor to ship
b2CircleShape b2CircleShape;
b2CircleShape.m_radius = 8;
myFixtureDef.shape = &b2CircleShape;
myFixtureDef.isSensor = true;
myFixtureDef.filter.categoryBits = RADAR_SENSOR;
myFixtureDef.filter.maskBits = ENEMY_AIRCRAFT;
ship->m_body->CreateFixture(&myFixtureDef);



//add semicircle radar sensor to tower
float radius = 8;
b2Vec2 vertices[8];
vertices[0].Set(0,0);
for (int i = 0; i < 7; i++) {
float angle = i / 6.0 * 90 * DEGTORAD;
vertices[i+1].Set( radius * cosf(angle), radius * sinf(angle) );
}
//b2PolygonShape polygonShape;
polygonShape.Set(vertices, 8);
myFixtureDef.shape = &polygonShape;
tower->m_body->CreateFixture(&myFixtureDef);

//make the tower rotate at 45 degrees per second
tower->m_body->SetAngularVelocity(45 * DEGTORAD);



m_world->SetGravity( b2Vec2(0, 0) );
m_world->SetContactListener(&myContactListenerInstance2);

}

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

for( int i = 0; i < balls5.size(); i++)
balls5[i]->renderAtBodyPosition();
}

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

Test::MouseDown(pp);
}

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

#endif

이상의 코드로 감지기에 대해 잘 이해할 수 있었다.

 코드는 중간에 감시탑이라 가정한 작은 원이 레이더를 돌리며, 커다란 녹색 원이 전투순양함이고 작은 빨강색의 원이 적기라고 가정하여, 센서로 설정된 고정체에 적기가 탐지되면 녹색 원을 노란색 원으로 변경하고 원으로부터 적기의 위치를 잇는 점선을 그리도록 한다.

댓글 없음:

댓글 쓰기