'충돌처리'에 해당되는 글 1건
- 2010.07.07 캐릭터 충돌처리 1
캐릭터들간 뚫고 들어가지 않게끔 하는 충돌처리..
이것도 날 너무나도 재밌게 해주는구나...
처음.
각 캐릭터들을 OBB & OBB로 처리하다.
회전할때 부딪히는 경우가 발생. 회전할때는 자유롭게 해주고 싶었음.. 따라서 회전할때 충돌처리를 해주지 않았더니.. OBB특성상 끼는 경우가 발생. 그 결과 회전을 하고 난 후 캐릭터가 안움직였다.(충돌상태가 아닌데도) 당연하지. 끼었으니까
소스는 없다. 아쉽게도.
두번째.
각 캐릭터들을 Sphere & Sphere로 처리하다.
처음의 문제를 알고 다방면의 길이가 똑같은 충돌체가 필요하다고 생각되었다. 그때 생각난 것이 Sphere와 Cylinder. 우선 Sphere로 구현하기로 했다. 그 결과 회전을 하고 난 후 캐릭터가 안움직였던 점은 고쳐졌다. 미끄러짐 벡터를 활용해서 옆으로 비껴나가게끔도 만들어놓고.
하지만 여전히 문제점은 발생했다. 비교적 빠르게 이동할때 몬스터들의 Sphere가 너무 작다면 그걸 뚫고 지나가는 상황이 발생한 것. 그리고 말도 안되는 공간으로 미끄러지는 것도 발견. 몬스터가 가리고 있지만, 가리는 공간의 굵기가 현저히 작아서 마치 뚫고가는 것 같은 데, 몬스터의 Sphere에 비해 속도가 빨랐던 모양이다. 그리고 여러 사이클을 처리하지 않아 겹치는 현상도 생겼다. 마지막으로 나중에 눈치채게 된거지만, 정적인 Sphere로 처리하면 이후 움직임처리에서 어긋나버리는 경우가 있다. 예를 들어 대상은 도망가는 입장인데 따라오는 오브젝트가 속도가 빨라 부딪혀 멈춰진다.
if( deltaPosition.x == 0.0f &&
deltaPosition.y == 0.0f &&
deltaPosition.z == 0.0f ) return;
BOOL isAffectCollision = FALSE;
Sphere src((*m_pSceneObject->GetPosition()+deltaPosition)+m_collisionSphere.center,
m_collisionSphere.radius );
deltaPosition.y == 0.0f &&
deltaPosition.z == 0.0f ) return;
BOOL isAffectCollision = FALSE;
Sphere src((*m_pSceneObject->GetPosition()+deltaPosition)+m_collisionSphere.center,
m_collisionSphere.radius );
std::vector<CCharacter*>* pCharList = m_pObjectManager->GetAllCharacterList();
for( std::vector<CCharacter*>::iterator it = pCharList->begin() ;
it != pCharList->end() ;
it++ )
{
if( *it == this ) continue;
for( std::vector<CCharacter*>::iterator it = pCharList->begin() ;
it != pCharList->end() ;
it++ )
{
if( *it == this ) continue;
CCharacter* pDestCharacter = *it;
// 경계구를 가져온 후, 미리 이동시켜준다.
Sphere dest( pDestCharacter->GetPosition()+pDestCharacter->GetCollisionSphere().center,
pDestCharacter->GetCollisionSphere().radius );
// 경계구를 가져온 후, 미리 이동시켜준다.
Sphere dest( pDestCharacter->GetPosition()+pDestCharacter->GetCollisionSphere().center,
pDestCharacter->GetCollisionSphere().radius );
D3DXVECTOR3 contactNormal; // 충돌법선
FLOAT penetration; // 관통값
if( CCollisionDetector::IsIntersectSphereAndSphere( src, dest, &contactNormal, &penetration ) )
{
// 관통했다면 그만큼 복구시켜준다.
m_pSceneObject->AddPosition( deltaPosition+(contactNormal*penetration) );
FLOAT penetration; // 관통값
if( CCollisionDetector::IsIntersectSphereAndSphere( src, dest, &contactNormal, &penetration ) )
{
// 관통했다면 그만큼 복구시켜준다.
m_pSceneObject->AddPosition( deltaPosition+(contactNormal*penetration) );
// 미끄러짐 벡터로 만들어준다.
D3DXVECTOR3 slidingPos = deltaPosition-D3DXVec3Dot( &deltaPosition, &contactNormal )*contactNormal;
//m_pSceneObject->AddPosition( slidingPos );
D3DXVECTOR3 slidingPos = deltaPosition-D3DXVec3Dot( &deltaPosition, &contactNormal )*contactNormal;
//m_pSceneObject->AddPosition( slidingPos );
// 캡슐을 정의한다.(관통이 복구된 부분부터 미끄러지는 부분까지)
// 미끄러짐벡터로 이동된 부분까지를 캡슐로 정의한다.
// 미끄러졌을때 어떤 오브젝트와 부딪힌경우 움직임을 취소시키기 위함이다.
// 2로 계속 나누어줘서 보간할수도 있지만 그것은 생략했다.
saengine::Capsule capsule( *m_pSceneObject->GetPosition(),
*m_pSceneObject->GetPosition()+slidingPos,
m_collisionSphere.radius );
// 미끄러짐벡터로 이동된 부분까지를 캡슐로 정의한다.
// 미끄러졌을때 어떤 오브젝트와 부딪힌경우 움직임을 취소시키기 위함이다.
// 2로 계속 나누어줘서 보간할수도 있지만 그것은 생략했다.
saengine::Capsule capsule( *m_pSceneObject->GetPosition(),
*m_pSceneObject->GetPosition()+slidingPos,
m_collisionSphere.radius );
std::vector<CCharacter*>::iterator it2;
for( it2 = pCharList->begin() ;
it2 != pCharList->end() ;
it2++ )
{
if( *it2 == this ) continue;
for( it2 = pCharList->begin() ;
it2 != pCharList->end() ;
it2++ )
{
if( *it2 == this ) continue;
CCharacter* pDestCharacter2 = *it2;
// 경계구를 가져온 후, 미리 이동시켜준다.
Sphere dest2( pDestCharacter2->GetPosition()+pDestCharacter2->GetCollisionSphere().center,
pDestCharacter2->GetCollisionSphere().radius );
// 경계구를 가져온 후, 미리 이동시켜준다.
Sphere dest2( pDestCharacter2->GetPosition()+pDestCharacter2->GetCollisionSphere().center,
pDestCharacter2->GetCollisionSphere().radius );
if( CCollisionDetector::IsIntersectSphereAndCapsule( dest2, capsule ) )
{
break;
}
}
{
break;
}
}
// 캡슐을 체크했는데 아무도 부딪히지 않았다면 미끄러짐 벡터로 이동시켜준다.
if( it2 != pCharList->end() )
m_pSceneObject->AddPosition( slidingPos );
if( it2 != pCharList->end() )
m_pSceneObject->AddPosition( slidingPos );
isAffectCollision = TRUE;
if( m_pBrain ) m_pBrain->HandleMessage( MSG_COLLISION_WITH_CHARACTER, pDestCharacter, 4 );
}
}
// 충돌하지 않았다면 그냥 이동시켜주면 된다.
if( !isAffectCollision )
{
m_vecPrevPosition = *m_pSceneObject->GetPosition();
m_pSceneObject->AddPosition( deltaPosition );
}
if( !isAffectCollision )
{
m_vecPrevPosition = *m_pSceneObject->GetPosition();
m_pSceneObject->AddPosition( deltaPosition );
}
세번째.
각 캐릭터들을 Capsule & Sphere로 처리하다.
이동 행위자 캐릭터의 처음위치, 이동 완료된 위치, 반지름을 가지고 캡슐을 생성한다. 그리고 그 캡슐과 몬스터의 Sphere로 처리했다. 마치 지나간 궤적같은 느낌의 캡슐. 첨에 이 방식을 기획했을 때는 완벽할줄 알았다. 하지만 그것도 잠시, 충돌후 미끄러지는 방향을 계산해야했는데 충돌하는 케이스가 너무 많았다. 미끄러지는 방향을 구하기 위해 여러가지 계산을 해야하는데, 결과가 부동소수점의 오차때문에 어쩔때는 0.001... 어쩔때는 0.002... 이런식으로 오차가 남아서 툭하면 안움직였었다.(끼어버려서) 밑에 진한 글씨로 되어있는 부분이 충돌후 방향을 정하는 부분이다. 여러번의 곱셈으로 인한 오차가 미세하게 존재했다.
if( deltaPosition.x == 0.0f &&
deltaPosition.y == 0.0f &&
deltaPosition.z == 0.0f ) return;
deltaPosition.y == 0.0f &&
deltaPosition.z == 0.0f ) return;
BOOL isAffectCollision = FALSE;
// 충돌된 캐릭터는 이곳에 적재된다.
std::vector<IntersectedCharData> alignedIntersectedCharList;
std::vector<IntersectedCharData> alignedIntersectedCharList;
// 처음위치와 이동한후의 위치를 이어서 캡슐로 만든다.
D3DXVECTOR3 desiredPosition = *m_pSceneObject->GetPosition()+deltaPosition;
saengine::Capsule srcBound( *m_pSceneObject->GetPosition(), desiredPosition, m_collisionSphere.radius );
D3DXVECTOR3 desiredPosition = *m_pSceneObject->GetPosition()+deltaPosition;
saengine::Capsule srcBound( *m_pSceneObject->GetPosition(), desiredPosition, m_collisionSphere.radius );
std::vector<CCharacter*>* pCharList = m_pObjectManager->GetAllCharacterList();
std::vector<CCharacter*>::iterator it = pCharList->begin();
while( it != pCharList->end() )
{
CCharacter* pDestChar = *it++;
std::vector<CCharacter*>::iterator it = pCharList->begin();
while( it != pCharList->end() )
{
CCharacter* pDestChar = *it++;
if( pDestChar == this ) continue;
// 충돌을 원하는 구를 지정한다.
Sphere destBound( pDestChar->GetPosition()+pDestChar->GetCollisionSphere().center,
pDestChar->GetCollisionSphere().radius );
Sphere destBound( pDestChar->GetPosition()+pDestChar->GetCollisionSphere().center,
pDestChar->GetCollisionSphere().radius );
D3DXVECTOR3 contactPoint;
D3DXVECTOR3 contactNormal; // 충돌법선
FLOAT penetration; // 관통값
if( CCollisionDetector::TestIntersectSphereAndCapsule( destBound, srcBound, &contactPoint, &contactNormal, &penetration ) )
{
if( penetration > 0.2f )
{
// TestIntersectSphereAndCapsule은 캡슐에서부터 구로 충돌법선이 생성된다.
// 하지만 이 경우는 구에서부터 캡슐에서의 충돌법선이 필요하기 때문에 반전시켜준다.
// 이 경우는 제 소스가 이렇기 때문입니다.
contactNormal = -contactNormal;
D3DXVECTOR3 contactNormal; // 충돌법선
FLOAT penetration; // 관통값
if( CCollisionDetector::TestIntersectSphereAndCapsule( destBound, srcBound, &contactPoint, &contactNormal, &penetration ) )
{
if( penetration > 0.2f )
{
// TestIntersectSphereAndCapsule은 캡슐에서부터 구로 충돌법선이 생성된다.
// 하지만 이 경우는 구에서부터 캡슐에서의 충돌법선이 필요하기 때문에 반전시켜준다.
// 이 경우는 제 소스가 이렇기 때문입니다.
contactNormal = -contactNormal;
// 충돌점과 거리를 측정한다.(가까운곳으로 정렬하여 먼저 처리하기 위함)
// 충돌점은 캡슐내부의 세그먼트에 정의된다.
alignedIntersectedCharList.push_back(
IntersectedCharData( pDestChar,
contactPoint,
contactNormal,
penetration,
D3DXVec3Length( &( contactPoint-destBound.center ) ) ) );
}
}
}
// 비어있다면 그냥 종료
if( alignedIntersectedCharList.empty() )
{
m_vecPrevPosition = *m_pSceneObject->GetPosition();
m_pSceneObject->AddPosition( deltaPosition );
return;
}
// 충돌 캐릭터들을 정렬한다. 정렬한후 제일처음의 오브젝트로 처리했다.
sort( alignedIntersectedCharList.begin(), alignedIntersectedCharList.end(), AlignIntersectedCharList );
const IntersectedCharData& interData = *alignedIntersectedCharList.begin();
if( alignedIntersectedCharList.begin()->penetration > 0 )
{
if( interData.contactPoint == srcBound.b )
{
D3DXVECTOR3 dir;
D3DXVec3Normalize( &dir, &deltaPosition );
desiredPosition = interData.contactPoint+
(-dir*interData.penetration);
// 충돌점은 캡슐내부의 세그먼트에 정의된다.
alignedIntersectedCharList.push_back(
IntersectedCharData( pDestChar,
contactPoint,
contactNormal,
penetration,
D3DXVec3Length( &( contactPoint-destBound.center ) ) ) );
}
}
}
// 비어있다면 그냥 종료
if( alignedIntersectedCharList.empty() )
{
m_vecPrevPosition = *m_pSceneObject->GetPosition();
m_pSceneObject->AddPosition( deltaPosition );
return;
}
// 충돌 캐릭터들을 정렬한다. 정렬한후 제일처음의 오브젝트로 처리했다.
sort( alignedIntersectedCharList.begin(), alignedIntersectedCharList.end(), AlignIntersectedCharList );
const IntersectedCharData& interData = *alignedIntersectedCharList.begin();
if( alignedIntersectedCharList.begin()->penetration > 0 )
{
if( interData.contactPoint == srcBound.b )
{
D3DXVECTOR3 dir;
D3DXVec3Normalize( &dir, &deltaPosition );
desiredPosition = interData.contactPoint+
(-dir*interData.penetration);
m_pSceneObject->SetPosition( desiredPosition );
}
else if( interData.contactPoint == srcBound.a )
{
D3DXVECTOR3 dir;
D3DXVec3Normalize( &dir, &deltaPosition );
desiredPosition = interData.contactPoint+
(dir*interData.penetration);
}
else if( interData.contactPoint == srcBound.a )
{
D3DXVECTOR3 dir;
D3DXVec3Normalize( &dir, &deltaPosition );
desiredPosition = interData.contactPoint+
(dir*interData.penetration);
m_pSceneObject->SetPosition( desiredPosition );
}
else
{
D3DXVECTOR3 dir;
D3DXVec3Normalize( &dir, &deltaPosition );
desiredPosition = interData.contactPoint+
(-dir*(interData.pCharacter->GetCollisionSphere().radius+
srcBound.radius) );
}
else
{
D3DXVECTOR3 dir;
D3DXVec3Normalize( &dir, &deltaPosition );
desiredPosition = interData.contactPoint+
(-dir*(interData.pCharacter->GetCollisionSphere().radius+
srcBound.radius) );
m_pSceneObject->SetPosition( desiredPosition );
}
}
}
}
// 미끄러짐 벡터로 이동시킨다. (V-(V*N)N)을 속도가 아닌 포지션으로 응용
D3DXVECTOR3 slidingPos =
deltaPosition-
D3DXVec3Dot( &deltaPosition,
&alignedIntersectedCharList.begin()->contactNormal ) *
alignedIntersectedCharList.begin()->contactNormal;
D3DXVECTOR3 slidingPos =
deltaPosition-
D3DXVec3Dot( &deltaPosition,
&alignedIntersectedCharList.begin()->contactNormal ) *
alignedIntersectedCharList.begin()->contactNormal;
// 캡슐을 미끄러짐 벡터를 더한것으로 재정의한다.
saengine::Capsule src( *m_pSceneObject->GetPosition(),
*m_pSceneObject->GetPosition()+slidingPos,
m_collisionSphere.radius );
...
...
네번째.
각 캐릭터들을 Sphere & Sphere로 처리하고 속도 성분을 투입하였다.
바로 이것이 나의 종착지 였다. 양쪽 Sphere에 속도 성분을 각각 투입해 속도를 고려하는 충돌처리를 한 것.
그리고 물리엔진을 만들어봤던 나로써는 큰 실수를 했었는데 한번 충돌했던 캐릭터는 다시 충돌처리를 하지 않을것이다. 라고 생각했던 것이다. 충돌하더라도 다시 처음부터 충돌처리를 해야한다. 때문에 무한루프로 빠질 수 있는데, 특수한 변수를 넣어서 방지해야한다.
const INT MAX_PROCESSING_COUNT = 5;
INT currProcessingCount = 0;
D3DXVECTOR3 currPosition = *m_pSceneObject->GetPosition();
D3DXVECTOR3 currDeltaPosition = deltaPosition;
INT currProcessingCount = 0;
D3DXVECTOR3 currPosition = *m_pSceneObject->GetPosition();
D3DXVECTOR3 currDeltaPosition = deltaPosition;
BOOL isCollision = FALSE;
// 행위자의 충돌체
saengine::Sphere srcBound(currPosition+m_collisionSphere.center,
m_collisionSphere.radius );
saengine::Sphere srcBound(currPosition+m_collisionSphere.center,
m_collisionSphere.radius );
// 시간간격
FLOAT elapsed = static_cast<CGameStage*>(g_pSAMgr->GetSceneStage())->GetCurrElapsed();
FLOAT elapsed = static_cast<CGameStage*>(g_pSAMgr->GetSceneStage())->GetCurrElapsed();
// MAX_PROCESSING_COUNT만큼의 사이클을 돌린다.
// 중요하다. 자칫 무한루프로 빠질수도 있기 때문에 최대횟수를 제한해야 한다.
while( currProcessingCount < MAX_PROCESSING_COUNT )
{
BOOL isCollisionThisProcess = FALSE;
// 중요하다. 자칫 무한루프로 빠질수도 있기 때문에 최대횟수를 제한해야 한다.
while( currProcessingCount < MAX_PROCESSING_COUNT )
{
BOOL isCollisionThisProcess = FALSE;
std::vector<CCharacter*>* pCharList = m_pObjectManager->GetAllCharacterList();
std::vector<CCharacter*>::iterator it = pCharList->begin();
std::vector<CCharacter*>::iterator it = pCharList->begin();
while( it != pCharList->end() )
{
CCharacter* pDestChar = *it++;
{
CCharacter* pDestChar = *it++;
if( pDestChar == this ) continue;
// 대상의 충돌체
saengine::Sphere destBound( pDestChar->GetPosition()+pDestChar->GetCollisionSphere().center,
pDestChar->GetCollisionSphere().radius );
// 대상의 충돌체
saengine::Sphere destBound( pDestChar->GetPosition()+pDestChar->GetCollisionSphere().center,
pDestChar->GetCollisionSphere().radius );
D3DXVECTOR3 destDeltaPosition = pDestChar->GetVelocity()*elapsed;
D3DXVECTOR3 contactNormal;
FLOAT dist;
FLOAT pen;
if( CCollisionDetector::TestMovingIntersectSphereAndSphere(
srcBound, destBound, currDeltaPosition, destDeltaPosition, &contactNormal, &dist, &pen ) )
{
if( m_pBrain ) m_pBrain->HandleMessage( MSG_COLLISION_WITH_CHARACTER, pDestCharacter, 4 );
isCollisionThisProcess = TRUE;
// 속도의 방향을 미끄러지는 방향으로 간단히 바꿔준다.
// (V-(V*N)N)
m_vecVelocity = m_vecVelocity-
D3DXVec3Dot( &m_vecVelocity, &contactNormal ) * contactNormal;
FLOAT dist;
FLOAT pen;
if( CCollisionDetector::TestMovingIntersectSphereAndSphere(
srcBound, destBound, currDeltaPosition, destDeltaPosition, &contactNormal, &dist, &pen ) )
{
if( m_pBrain ) m_pBrain->HandleMessage( MSG_COLLISION_WITH_CHARACTER, pDestCharacter, 4 );
isCollisionThisProcess = TRUE;
// 속도의 방향을 미끄러지는 방향으로 간단히 바꿔준다.
// (V-(V*N)N)
m_vecVelocity = m_vecVelocity-
D3DXVec3Dot( &m_vecVelocity, &contactNormal ) * contactNormal;
// 부딪혔으니 튕겨나가야한다.
D3DXVECTOR3 resolveDeltaPos = currDeltaPosition+(contactNormal*pen);
D3DXVECTOR3 resolveDeltaPos = currDeltaPosition+(contactNormal*pen);
// 부딪히고 남은 거리를 계산한다.
FLOAT remainingLength = D3DXVec3Length( &resolveDeltaPos )-dist;
if( remainingLength <= 0.0f )
{
m_pSceneObject->SetPosition( currPosition );
return;
}
FLOAT remainingLength = D3DXVec3Length( &resolveDeltaPos )-dist;
if( remainingLength <= 0.0f )
{
m_pSceneObject->SetPosition( currPosition );
return;
}
// 앞으로 전진할 델타포지션을 계산한다.
currDeltaPosition = resolveDeltaPos+(m_vecVelocity*elapsed);
currPosition += currDeltaPosition;
currDeltaPosition = resolveDeltaPos+(m_vecVelocity*elapsed);
currPosition += currDeltaPosition;
// 행위자의 충돌체를 업데이트시킨다.
srcBound.center = currPosition+m_collisionSphere.center;
}
}
srcBound.center = currPosition+m_collisionSphere.center;
}
}
if( !isCollisionThisProcess )
{
m_vecPrevPosition = *m_pSceneObject->GetPosition();
m_pSceneObject->AddPosition( currDeltaPosition );
break;
}
{
m_vecPrevPosition = *m_pSceneObject->GetPosition();
m_pSceneObject->AddPosition( currDeltaPosition );
break;
}
currProcessingCount++;
}
}
'작업일지' 카테고리의 다른 글
모델이 깨지는경우 (0) | 2010.07.16 |
---|---|
오리엔테이션 생성중 실수. (0) | 2010.07.05 |
그림자 처리.. (0) | 2010.06.26 |
Perspective Shadow Map.. (0) | 2010.06.22 |