이 질문에 대해 처음 인 경우에는 먼저 아래의 사전 업데이트 부분을 읽은 다음이 부분을 읽는 것이 좋습니다. 그래도 문제의 종합은 다음과 같습니다.
기본적으로 충돌 순서 및 충돌 그룹이 중요한 그리드 공간 분할 시스템이있는 충돌 감지 및 해결 엔진이 있습니다. 한 번에 한 몸이 움직 인 다음 충돌을 감지 한 다음 충돌을 해결해야합니다. 모든 바디를 한 번에 이동 한 다음 가능한 충돌 쌍을 생성하면 충돌 속도가 빨라지지만 충돌 순서를 준수하지 않기 때문에 해상도가 떨어집니다. 한 번에 한 몸을 움직이면 몸이 충돌을 확인하도록 강요받으며 이는 2 문제입니다. 그룹을 혼합하여 사용하면 많은 바디로 인해 그룹이 매우 느리게 진행되는 이유를 상상할 수 있습니다.
업데이트 : 나는 정말로 열심히 노력했지만 아무것도 최적화 할 수 없었습니다.
나는 Will이 묘사 한 "그림"을 성공적으로 구현하고 그룹을 비트 세트로 변경했지만 매우 작은 속도 향상입니다.
또한 엔진이 충돌 순서에 따라 큰 문제를 발견했습니다 .
나는 독특한 충돌 쌍 생성의 구현을 시도했는데 , 이는 확실히 모든 것을 크게 가속화하지만 충돌 순서를 위반했습니다 .
설명하겠습니다 :
내 원래 디자인 (쌍을 생성하지 않음)에서 이런 일이 발생합니다.
- 한 몸이 움직이다
- 이동 한 후 셀을 새로 고치고 충돌하는 바디를 가져옵니다.
- 본체와 겹치면 해결해야하며 충돌을 해결하십시오.
즉, 바디가 움직이고 벽 (또는 다른 바디)에 닿으면 이동 한 바디 만 충돌을 해결하고 다른 바디는 영향을받지 않습니다.
이것이 내가 원하는 행동 입니다.
물리 엔진에는 흔하지 않지만 레트로 스타일 게임 에는 많은 이점이 있습니다.
일반적인 그리드 디자인 (고유 쌍 생성)에서 다음과 같은 상황이 발생합니다.
- 모든 몸이 움직인다
- 모든 몸체가 이동 한 후 모든 셀을 새로 고칩니다.
- 고유 한 충돌 쌍 생성
- 각 쌍에 대해 충돌 감지 및 해결 처리
이 경우 동시 이동으로 인해 두 바디가 겹치게되며 동시에 해결됩니다. 이렇게하면 바디가 효과적으로 "서로 밀려"여러 바디와의 충돌 안정성이 깨집니다
이 동작은 물리 엔진에서 일반적 이지만 내 경우에는 허용되지 않습니다 .
나는 또 다른 문제를 발견했다.이 문제는 실제 상황에서 일어날 가능성이없는 경우에도 중요하다.
- 그룹 A, B 및 W의 시체를 고려하십시오.
- A가 W와 A에 대해 충돌하고 해결
- B가 W 및 B에 대해 충돌 및 해결
- A는 B에 대해 아무것도하지 않습니다
- B는 A에 대해 아무 것도하지 않습니다
많은 A 바디와 B 바디가 동일한 셀을 차지하는 상황이있을 수 있습니다.이 경우 서로 반응하지 않아야하는 바디간에 불필요한 반복이 많이 발생합니다 (또는 충돌 만 감지하고 해결하지는 않음). .
동일한 셀을 점유하는 100 개의 바디에 대해 100 ^ 100 반복입니다! 이것은 고유 쌍이 생성되지 않기 때문에 발생하지만 고유 쌍을 생성 할 수는 없습니다 . 그렇지 않으면 원하지 않는 동작이 발생합니다 .
이런 종류의 충돌 엔진을 최적화하는 방법이 있습니까?
다음은 준수해야 할 지침입니다.
충돌 순서는 매우 중요합니다!
- 시체는 한 번에 하나씩 이동 한 다음 한 번에 하나씩 충돌을 확인하고 한 번에 하나씩 이동 후 해결해야 합니다 .
바디는 3 개의 그룹 비트 셋을 가져야합니다
- 그룹 : 신체가 속한 그룹
- GroupsToCheck : 신체가 충돌을 감지 해야하는 그룹
- GroupsNoResolve : 바디가 충돌을 해결 해서는 안되는 그룹
- 충돌 만 감지하고 해결하지 않으려는 상황이있을 수 있습니다.
사전 업데이트 :
서문 :이 병목 현상을 최적화 할 필요는 없다는 것을 알고 있습니다. 엔진은 이미 매우 빠릅니다. 그러나 재미 있고 교육적인 목적으로 엔진을 더 빠르게 만드는 방법을 찾고 싶습니다.
유연성과 속도에 중점을 둔 범용 C ++ 2D 충돌 감지 / 응답 엔진을 만들고 있습니다.
아키텍처의 매우 기본적인 다이어그램은 다음과 같습니다.
기본적으로 메인 클래스는 World
a ResolverBase*
, a SpatialBase*
및 a 의 메모리를 소유 (관리) 합니다 vector<Body*>
.
SpatialBase
넓은 위상 충돌 탐지를 처리하는 순수한 가상 클래스입니다.
ResolverBase
충돌 해결을 처리하는 순수한 가상 클래스입니다.
시체 는 시체 자체가 소유 한 개체 World::SpatialBase*
와 통신 SpatialInfo
합니다.
현재 하나의 공간 클래스가 있습니다 : Grid : SpatialBase
. 기본 고정 2D 그리드입니다. 자체 정보 클래스가 GridInfo : SpatialInfo
있습니다.
아키텍처는 다음과 같습니다.
이 Grid
클래스는의 2D 배열을 소유합니다 Cell*
. 이 Cell
클래스에는 (소유되지 않은) Body*
: a vector<Body*>
셀에있는 모든 본문이 들어 있습니다.
GridInfo
또한 개체에는 몸이 속한 셀에 대한 비 소유 포인터가 있습니다.
앞서 말했듯이 엔진은 그룹을 기반으로합니다.
Body::getGroups()
std::bitset
본문이 속한 모든 그룹 중 a 를 반환합니다 .Body::getGroupsToCheck()
std::bitset
신체가 충돌을 확인해야하는 모든 그룹 중 a 를 반환합니다 .
바디는 단일 셀 이상을 차지할 수 있습니다. GridInfo는 항상 점유 된 셀에 대한 비 소유 포인터를 저장합니다.
단일 몸체가 이동하면 충돌 감지가 발생합니다. 모든 바디가 축 정렬 경계 상자라고 가정합니다.
광역 충돌 감지 작동 방식 :
1 부 : 공간 정보 업데이트
각각 Body
body
:
- 가장 왼쪽에있는 점유 된 셀과 가장 오른쪽에있는 점유 된 셀이 계산됩니다.
- 이전 셀과 다르면
body.gridInfo.cells
지워지고 몸이 차지하는 모든 셀로 채워집니다 (가장 왼쪽 상단 셀에서 맨 오른쪽 하단 셀까지 루프 용 2D).
body
이제 어떤 셀을 차지하는지 알 수 있습니다.
2 부 : 실제 충돌 점검
각각 Body
body
:
body.gridInfo.handleCollisions
라고합니다 :
void GridInfo::handleCollisions(float mFrameTime)
{
static int paint{-1};
++paint;
for(const auto& c : cells)
for(const auto& b : c->getBodies())
{
if(b->paint == paint) continue;
base.handleCollision(mFrameTime, b);
b->paint = paint;
}
}
void Body::handleCollision(float mFrameTime, Body* mBody)
{
if(mBody == this || !mustCheck(*mBody) || !shape.isOverlapping(mBody->getShape())) return;
auto intersection(getMinIntersection(shape, mBody->getShape()));
onDetection({*mBody, mFrameTime, mBody->getUserData(), intersection});
mBody->onDetection({*this, mFrameTime, userData, -intersection});
if(!resolve || mustIgnoreResolution(*mBody)) return;
bodiesToResolve.push_back(mBody);
}
그런 다음의 모든 신체에 대해 충돌이 해결됩니다
bodiesToResolve
.그게 다야.
그래서 저는이 넓은 위상 충돌 탐지를 꽤 오랫동안 최적화하려고 노력했습니다. 현재 아키텍처 / 설정 이외의 다른 것을 시도 할 때마다 계획된대로 진행되지 않거나 나중에 거짓으로 입증 된 시뮬레이션에 대해 가정합니다.
내 질문은 : 어떻게 내 충돌 엔진의 넓은 단계를 최적화 할 수 있습니까?
여기에 적용 할 수있는 매직 C ++ 최적화가 있습니까?
더 많은 성능을 위해 아키텍처를 재 설계 할 수 있습니까?
- 실제 구현 : SSVSCollsion
- Body.h , Body.cpp
- World.h , World.cpp
- Grid.h , Grid.cpp
- Cell.h , Cell.cpp
- GridInfo.h , GridInfo.cpp
최신 버전의 Callgrind 출력 : http://txtup.co/rLJgz
getBodiesToCheck()
는 5462334 회 호출되었으며 전체 프로파일 링 시간의 35,1 % (명령 읽기 액세스 시간)