반경 내에서 모든 엔티티를 효율적으로 지속적으로 찾는 방법은 무엇입니까?


14

엔터티 (단위)가 매우 많습니다. 각 단계에서 각 단위는 근처에있는 모든 단위의 위치를 ​​알아야합니다 (거리는 상수 R 보다 적습니다 ). 모든 유닛이 계속 움직입니다. 이것은 3D입니다.

평균적으로 주어진 제약 조건을 가진 다른 주어진 단위 근처에 총 단위 수의 1 %가 있습니다.

무차별 적으로이 작업을 어떻게 효율적으로 수행 할 수 있습니까?


7
어떤 종류의 공간 분할 시스템이 필요합니다 : en.wikipedia.org/wiki/Space_partitioning
Tetrad

답변:


15

Quadtree, Octree, BSP 트리 또는 간단한 그리드 시스템과 같은 공통 공간 분할 알고리즘 중 하나를 사용하십시오. 각각은 특정 시나리오마다 장단점이 있습니다. 이 책들에서 그들에 대해 더 읽을 수 있습니다 .

일반적으로 (또는 내가 들었을 때, 이것의 이유에 너무 익숙하지 않다) Quadtree 또는 Octree는 야외 환경에 더 적합하고 BSP 트리는 실내 장면에 더 잘 맞습니다. 그리고 Quadtree 또는 Octree 사용 중에서 선택하는 것은 세상이 얼마나 평평한 지에 달려 있습니다. Octree를 사용하여 Y 축에 약간의 변형이 있으면 낭비입니다. Octree는 기본적으로 추가 차원이있는 Quadtree입니다.

마지막으로 그리드 솔루션의 단순성을 무시하지 마십시오. 많은 사람들이 간단한 그리드가 문제에 대해 충분하고 (더 효율적일 수 있음) 무시하고 대신 더 복잡한 솔루션으로 바로 넘어갑니다.

그리드를 사용하는 것은 단순히 세계를 균등 한 간격으로 나누고 엔티티를 세계의 적절한 지역에 저장하는 것입니다. 그런 다음 위치를 지정하면 주변 엔티티를 찾는 것이 검색 반경과 교차하는 영역을 반복하는 문제입니다.

당신의 세계가 XZ 평면에서 (-1000, -1000)에서 (1000, 1000)의 범위에 있다고 가정 해 봅시다. 예를 들어 다음과 같이 10x10 격자로 나눌 수 있습니다.

var grid = new List<Entity>[10, 10];

그런 다음 그리드의 해당 셀에 엔티티를 배치합니다. 예를 들어 XZ (-1000, -1000)가있는 엔터티는 셀 (0,0)에 속하고 XZ (1000, 1000)가있는 엔터티는 셀 (9, 9)에 속합니다. 그런 다음 세계의 위치와 반지름이 주어지면이 "원"이 교차하는 셀을 결정하고 그에 대해 두 배로 간단한 반복을 수행 할 수 있습니다.

어쨌든 모든 대안을 연구하고 게임에 더 적합한 것으로 보이는 것을 선택하십시오. 나는 어떤 알고리즘이 당신에게 가장 적합한지를 결정할 주제에 대해 아직 지식이 충분하지 않다는 것을 인정한다.

편집 다른 포럼 에서 이것을 발견 하면 결정에 도움이 될 수 있습니다.

그리드는 대부분의 개체가 그리드 사각형 안에 들어가고 분포가 상당히 균일 할 때 가장 잘 작동합니다. 반대로 쿼드 트리는 객체의 크기가 가변적이거나 작은 영역에 군집되어있을 때 작동합니다.

문제에 대한 모호한 설명을 감안할 때 그리드 솔루션도 기대하고 있습니다 (즉, 단위가 작고 균질하게 분포되어 있다고 가정).


자세한 답변 주셔서 감사합니다. 예, 간단한 그리드 솔루션으로 충분합니다.
OCyril

0

나는 이것을 얼마 전에 썼다 . 현재 상용 사이트에 있지만 개인용 소스를 무료로 얻을 수 있습니다. 과도하고 Java로 작성되었지만 잘 문서화되어 있으므로 다른 언어로 자르고 다시 쓰는 것이 너무 어렵지 않아야합니다. 기본적으로 Octree를 사용하여 실제로 큰 객체와 멀티 스레딩을 처리하기 위해 조정합니다.

Octree가 유연성과 효율성의 최상의 조합을 제공한다는 것을 알았습니다. 그리드로 시작했지만 정사각형의 크기를 적절하게 조정하는 것은 불가능했으며 빈 사각형의 큰 패치는 공간과 컴퓨팅 성능을 전혀 사용하지 않았습니다. (그리고 2 개 차원에서 단지였습니다.) 여러 스레드에서 내 코드 핸들 쿼리하는 추가하는 많은 복잡성하지만 문서는 당신이 주변에 당신이 경우에 필요하지 않는 것이 작업 할 수 있도록해야한다.


0

효율성을 높이려면 매우 저렴한 경계 상자 확인을 사용하여 대상 장치 근처에 있지 않은 "장치"의 99 %를 사소하게 거부하십시오. 그리고 데이터를 공간적으로 구성하지 않고도이 작업을 수행 할 수 있기를 바랍니다. 따라서 모든 단위가 플랫 데이터 구조에 저장되어 있으면 처음부터 끝까지 레이스를 시도하고 먼저 관심 단위의 경계 상자 외부에있는 현재 단위를 확인하십시오.

"가까운"것으로 간주되지 않는 항목을 안전하게 거부 할 수 있도록 관심있는 단위에 대해 대형 경계 상자를 정의하십시오. 바운딩 박스에서 제외 검사는 반경 검사보다 저렴할 수 있습니다. 그러나 이것이 테스트 된 일부 시스템에서는 그렇지 않은 것으로 밝혀졌습니다. 두 사람은 거의 똑같이 수행합니다. 이것은 아래의 많은 토론 후에 편집되었습니다.

첫째 : 2D 경계 상자 클립.

// returns true if the circle supplied is completely OUTSIDE the bounding box, rectClip
bool canTrivialRejectCircle(Vertex2D& vCentre, WorldUnit radius, Rect& rectClip) {
  if (vCentre.x + radius < rectClip.l ||
    vCentre.x - radius > rectClip.r ||
    vCentre.y + radius < rectClip.b ||
    vCentre.y - radius > rectClip.t)
    return true;
  else
    return false;
}

다음과 같은 것과 비교 (3D) :

BOOL bSphereTest(CObject3D* obj1, CObject3D* obj2 )
{
  D3DVECTOR relPos = obj1->prPosition - obj2->prPosition;
  float dist = relPos.x * relPos.x + relPos.y * relPos.y + relPos.z * relPos.z;
  float minDist = obj1->fRadius + obj2->fRadius;
  return dist <= minDist * minDist;
}.

객체가 사소하게 거부되지 않으면 더 비싸고 정확한 충돌 테스트를 수행합니다. 그러나 당신은 단지 근접성을 찾고 있기 때문에 구면 테스트는 그에 적합하지만 사소한 거부에서 살아남은 1 %의 물체에만 적합합니다.

이 기사는 사소한 거부에 대한 상자를 지원합니다. http://www.h3xed.com/programming/bounding-box-vs-bounding-circle-collision-detection-performance-as3

이 선형 접근 방식으로 필요한 성능을 얻지 못하면 다른 포스터가 말한 것처럼 계층 적 데이터 구조가 필요할 수 있습니다. R- 트리는 고려할 가치가 있습니다. 동적 변경을 지원합니다. 그들은 공간 세계의 BTrees입니다.

나는 당신이 그것을 피할 수 있다면 그러한 복잡성을 도입하는 모든 어려움에 당신이 가기를 원하지 않았습니다. 또한 객체가 초당 여러 번 움직일 때이 복잡한 데이터를 최신 상태로 유지하는 비용은 어떻습니까?

그리드는 한 수준의 심층 공간 데이터 구조입니다. 이 제한은 실제로 확장 할 수 없음을 의미합니다. 세계의 크기가 커짐에 따라 포함해야하는 셀의 수도 커집니다. 결국 해당 셀 수 자체는 성능 문제가됩니다. 그러나 특정 크기의 세계에서는 공간 분할이 아닌 성능을 크게 향상시킬 수 있습니다.


1
OP는 구체적으로 첫 번째 단락에서 설명하는 것과 같은 무차별 대입 접근 방식을 피하고 싶다고 말했다. 또한 경계 상자 검사가 경계 구체 검사보다 저렴하다는 것을 어떻게 알 수 있습니까?! 그건 잘못이야
notlesh

예. 애플리케이션에 계층 적 데이터 구조를 도입하려는 노력으로 피할 수있는 무차별적인 힘을 피하고 싶다는 것을 알고 있습니다. 그러나 그것은 많은 노력이 될 수 있습니다. 만약 그가 그렇게하고 싶지 않다면, 그는 무차별적인 선형 접근 방식을 시도 할 수 있지만 그의 목록이 그리 크지 않으면 그렇게 나쁘지 않을 수 있습니다. 위의 코드를 편집하여 2D 경계 상자 사소한 거부 기능에 넣으려고합니다. 내가 틀렸다고 생각하지 않습니다.
Ciaran

GDnet과의 연결이 끊어졌지만 표준 구 테스트는 매우 간단하고 저렴하며 분기되지 않습니다.inside = (dot(p-p0, p-p0) <= r*r)
Lars Viklund

대신 위에 코드를 붙여 넣었습니다. 바운딩 박스에 비해 저렴한 것 같습니다.
Ciaran

1
@Ciaran 솔직히 말해서, 그 기사는 정말 나빠 보입니다. 우선 현실적인 데이터로 테스트를 수행하지 않고 동일한 값을 반복해서 사용합니다. 실제 시나리오에서는 발생하지 않습니다. 그리고 기사에 따르면 BB는 충돌이 없을 때만 더 빠릅니다 (예 : 첫 번째 if진술 에서 검사가 실패 합니다). 또한 현실적이지 않습니다. 그러나 매우 솔직히, 만약 당신이 이와 같은 것들을 최적화하기 시작한다면, 당신은 분명히 잘못된 곳에서 시작하고 있습니다.
bummzack

0

나는 의견을 표명하거나 투표 할 근거가 없기 때문에 이것을 대답해야한다. 이 질문을하는 사람들의 99 %에게 Ciaran이 설명한대로 경계 상자가 해결책입니다. 컴파일 된 언어에서는 눈이 번쩍이면서 관련없는 10 만 단위를 거부합니다. 비 브 루트 포스 솔루션에는 많은 오버 헤드가 있습니다. 숫자가 적을수록 (예 : 1000 미만) 무차별 대입 검사보다 처리 시간이 더 비쌉니다. 그리고 훨씬 더 많은 프로그래밍 시간이 걸립니다.

질문에 "매우 큰 숫자"가 무엇을 의미하는지 또는 여기에 답을 찾는 다른 사람들이 무엇을 의미하는지 잘 모르겠습니다. 위의 숫자가 보수적이며 10을 곱한 것으로 생각됩니다. 나는 개인적으로 무차별 대입 기술에 대해 편견을 가지고 있으며 그들이 얼마나 잘 작동하는지 진지하게 고민하고 있습니다. 그러나 몇 줄의 빠른 코드가 트릭을 수행 할 때 멋진 솔루션으로 10,000 단위의 시간을 낭비하는 사람은 원하지 않습니다. 필요한 경우 언제든지 나중에 멋지게 얻을 수 있습니다.

또한 경계 영역 확인에는 경계 상자가 아닌 곳에 곱셈이 필요합니다. 곱셈은 ​​그 성질 상 덧셈과 비교에 몇 배나 걸린다. 언어, OS 및 하드웨어의 조합이 구체 검사가 상자 검사보다 빠르지 만 대부분의 장소와 시간에 구체가 관련없는 몇 가지 단위를 거부하더라도 상자 검사가 더 빨라야합니다. 상자는 받아들입니다. (구가 더 빠른 곳에서는 컴파일러 / 인터프리터 / 최적화 기의 새로운 릴리스가 변경 될 가능성이 높습니다.)


답변에 아무런 문제가 없지만 질문에 대답하지는 않습니다. 특히 "무차별 적"접근 방식을 요구했습니다. 또한 Ciaran이 이미 작성한 내용을 반복하는 것처럼 보이며 AABB 대 원형 테스트에 대한 긴 의견 토론이있었습니다. 성능 차이는 단순히 관련이 없습니다. 실제 협상 테스트의 양을 줄이므로 전체 성능에 더 큰 영향을 미치므로 대부분의 충돌 후보에 맞는 경계 볼륨을 선택하는 것이 좋습니다.
bummzack
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.