강체를 제거하는 것은 매우 간단하다.
m_world->DestroyBody( pBody );
그러나 시점이 중요하다.
전에 언급하였던 Box2D의 처리 과정 때문에 Step 메서드가 진행중일 때, 강체가 제거되면 계가 아직 처리해야 할 강체가 중간에 사라지는 경우가 생기기 때문에 문제가 될 수 있다.
타이머를 통해 제거하는 방식 또한 그 시점에 아직 Step 메서드가 진행중일 수도 있으므로 안전하지 못하다. 중간에 프로그램이 종료될 수 있다.
콜백 메서드를 통해 제거하는 것 또한 같은 이유로(Step 메서드가 진행중일 때 호출되는 메서드이므로) 예상치 못한 결과가 생길 수 있다.
해결 방법은 매우 간단하다. 콜백 메서드나 여러 방법을 통해 특정 시점에 제거되야 할 강체가 생겼다는 것이 확정되었을 때, 이를 바로 제거하는 것이 아니라 배열이나 리스트와 같은 자료형에 모아 두고서, Step 메서드가 수행되기 직전이나(메서드 극초반부), 수행되고 난 직후(메서드 극후반부)에 이 저장된 자료형을 순회하면서 하나하나 제거하는 것이다.
#include <set>
std::set<Ball6*> ballsScheduledForRemoval;
와 같이 집합을 선언하고,
void BeginContact(b2Contact* contact){
void* bodyAUserData = contact->GetFixtureA()->GetBody()->GetUserData();
void* bodyBUserData = contact->GetFixtureB()->GetBody()->GetUserData();
if ( bodyAUserData && bodyBUserData )
handleContact2( static_cast<Ball6*>( bodyAUserData ),
static_cast<Ball6*>( bodyBUserData ) );
}
}
와 같은 충돌 콜백이 있을 때,
void handleContact2(Ball6* b1, Ball6* b2){
if ( ! outbreak )
return;
if ( b1->m_imIt ) {
ballsScheduledForRemoval.insert(b1);
b2->m_imIt = true;
}
else if ( b2->m_imIt ) {
ballsScheduledForRemoval.insert(b2);
b1->m_imIt = true;
}
}
와 같이 집합에 집어 넣는다.
void Step(Settings* settings){
Test::Step(settings);
for( int i = 0; i < balls6.size(); i++)
balls6[i]->renderAtBodyPosition();
//process list for deletion
std::set<Ball6*>::iterator it = ballsScheduledForRemoval.begin();
std::set<Ball6*>::iterator end = ballsScheduledForRemoval.end();
for (; it!=end; ++it) {
Ball6* dyingBall = *it;
//delete ball... physics body is destroyed here
delete dyingBall;
//... and remove it from main list of balls
std::vector<Ball6*>::iterator it = std::find(balls6.begin(), balls6.end(), dyingBall);
if ( it != balls6.end() )
balls6.erase( it );
}
//clear this list for next time
ballsScheduledForRemoval.clear();
}
순회하며 Step 메서드 마지막 부분에(첫 부분도 관계없다) 모두 제거한다.
여기서 강체는 Ball6 클래스의 멤버로 묶었었다. 따라서 Ball6 클래스의 소멸자에 다음을 추가했다.
~Ball6(){
m_body->GetWorld()->DestroyBody( m_body );
}
이 결과 안전하게 강체를 제거하는 것이 가능해진다.
댓글 없음:
댓글 쓰기