순서가 중요하고 충돌이 객체 그룹을 기준으로 조건부 인 충돌 엔진을 어떻게 최적화 할 수 있습니까?


14

이 질문에 대해 처음 인 경우에는 먼저 아래의 사전 업데이트 부분을 읽은 다음이 부분을 읽는 것이 좋습니다. 그래도 문제의 종합은 다음과 같습니다.

기본적으로 충돌 순서 및 충돌 그룹이 중요한 그리드 공간 분할 시스템이있는 충돌 감지 및 해결 엔진이 있습니다. 한 번에 한 몸이 움직 인 다음 충돌을 감지 한 다음 충돌을 해결해야합니다. 모든 바디를 한 번에 이동 한 다음 가능한 충돌 쌍을 생성하면 충돌 속도가 빨라지지만 충돌 순서를 준수하지 않기 때문에 해상도가 떨어집니다. 한 번에 한 몸을 움직이면 몸이 충돌을 확인하도록 강요받으며 이는 2 문제입니다. 그룹을 혼합하여 사용하면 많은 바디로 인해 그룹이 매우 느리게 진행되는 이유를 상상할 수 있습니다.


업데이트 : 나는 정말로 열심히 노력했지만 아무것도 최적화 할 수 없었습니다.

나는 Will이 묘사 한 "그림"을 성공적으로 구현하고 그룹을 비트 세트로 변경했지만 매우 작은 속도 향상입니다.

또한 엔진이 충돌 순서에 따라 문제를 발견했습니다 .

나는 독특한 충돌 쌍 생성의 구현을 시도했는데 , 이는 확실히 모든 것을 크게 가속화하지만 충돌 순서를 위반했습니다 .

설명하겠습니다 :

  • 내 원래 디자인 (쌍을 생성하지 않음)에서 이런 일이 발생합니다.

    1. 한 몸이 움직이다
    2. 이동 한 후 셀을 새로 고치고 충돌하는 바디를 가져옵니다.
    3. 본체와 겹치면 해결해야하며 충돌을 해결하십시오.

    즉, 바디가 움직이고 벽 (또는 다른 바디)에 닿으면 이동 한 바디 만 충돌을 해결하고 다른 바디는 영향을받지 않습니다.

    이것이 내가 원하는 행동 입니다.

    물리 엔진에는 흔하지 않지만 레트로 스타일 게임 에는 많은 이점이 있습니다.

  • 일반적인 그리드 디자인 (고유 쌍 생성)에서 다음과 같은 상황이 발생합니다.

    1. 모든 몸이 움직인다
    2. 모든 몸체가 이동 한 후 모든 셀을 새로 고칩니다.
    3. 고유 한 충돌 쌍 생성
    4. 각 쌍에 대해 충돌 감지 및 해결 처리

    이 경우 동시 이동으로 인해 두 바디가 겹치게되며 동시에 해결됩니다. 이렇게하면 바디가 효과적으로 "서로 밀려"여러 바디와의 충돌 안정성이 깨집니다

    이 동작은 물리 엔진에서 일반적 이지만 내 경우에는 허용되지 않습니다 .

나는 또 다른 문제를 발견했다.이 문제는 실제 상황에서 일어날 가능성이없는 경우에도 중요하다.

  • 그룹 A, B 및 W의 시체를 고려하십시오.
  • A가 W와 A에 대해 충돌하고 해결
  • B가 W 및 B에 대해 충돌 및 해결
  • A는 B에 대해 아무것도하지 않습니다
  • B는 A에 대해 아무 것도하지 않습니다

많은 A 바디와 B 바디가 동일한 셀을 차지하는 상황이있을 수 있습니다.이 경우 서로 반응하지 않아야하는 바디간에 불필요한 반복이 많이 발생합니다 (또는 충돌 만 감지하고 해결하지는 않음). .

동일한 셀을 점유하는 100 개의 바디에 대해 100 ^ 100 반복입니다! 이것은 고유 쌍이 생성되지 않기 때문에 발생하지만 고유 쌍을 생성 할 수는 없습니다 . 그렇지 않으면 원하지 않는 동작이 발생합니다 .

이런 종류의 충돌 엔진을 최적화하는 방법이 있습니까?

다음은 준수해야 할 지침입니다.

  • 충돌 순서는 매우 중요합니다!

    • 시체는 한 번에 하나씩 이동 다음 한 번에 하나씩 충돌을 확인하고 한 번에 하나씩 이동 후 해결해야 합니다 .
  • 바디는 3 개의 그룹 비트 셋을 가져야합니다

    • 그룹 : 신체가 속한 그룹
    • GroupsToCheck : 신체가 충돌을 감지 해야하는 그룹
    • GroupsNoResolve : 바디가 충돌을 해결 해서는 안되는 그룹
    • 충돌 만 감지하고 해결하지 않으려는 상황이있을 수 있습니다.



사전 업데이트 :

서문 :이 병목 현상을 최적화 할 필요는 없다는 것을 알고 있습니다. 엔진은 이미 매우 빠릅니다. 그러나 재미 있고 교육적인 목적으로 엔진을 더 빠르게 만드는 방법을 찾고 싶습니다.


유연성과 속도에 중점을 둔 범용 C ++ 2D 충돌 감지 / 응답 엔진을 만들고 있습니다.

아키텍처의 매우 기본적인 다이어그램은 다음과 같습니다.

기본 엔진 아키텍처

기본적으로 메인 클래스는 Worlda 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).
  1. 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 ++ 최적화가 있습니까?

더 많은 성능을 위해 아키텍처를 재 설계 할 수 있습니까?


최신 버전의 Callgrind 출력 : http://txtup.co/rLJgz


병목 현상을 프로파일하고 식별하십시오. 그들이 어디에 있는지 알려 주면 우리가해야 할 일이 있습니다.
Maik Semder 2016 년

@MaikSemder : 나는 그것을하고 게시물에 썼습니다. 병목 현상이있는 유일한 코드 스 니펫입니다. 길고 상세하면 미안하지만,이 병목 현상은 엔진 디자인에서 무언가를 변경해야만 해결할 수 있기 때문에 문제의 일부입니다.
Vittorio Romeo

죄송합니다. 찾기가 어려웠습니다. 우리에게 숫자를 줄 수 있습니까? 해당 기능에서 처리 된 기능 시간 및 개체 수는?
Maik Semder 2016 년

@MaikSemder : Clang 3.4 SVN으로 컴파일 된 바이너리에서 Callgrind로 테스트 SVN -O3 : 10000 동적 바디-이 함수 getBodiesToCheck()는 5462334 회 호출되었으며 전체 프로파일 링 시간의 35,1 % (명령 읽기 액세스 시간)
Vittorio Romeo

2
@Quonux : 위반이 없습니다. 나는 단지 "바람을 재발견"하는 것을 좋아 합니다. Bullet 또는 Box2D를 사용하여 해당 라이브러리로 게임을 만들 수는 있지만 실제로 내 목표는 아닙니다. 나는 좌절감을 느끼고 도움을 요청하더라도, 처음부터 물건을 만들고 장애물을 극복하려고 노력함으로써 훨씬 더 성취감을 느끼고 더 많은 것을 배웁니다. 처음부터 코딩하는 것이 학습 목적으로 매우 중요하다는 나의 생각 외에, 나는 또한 많은 시간을 할애하고 즐거운 시간을 보내는 것이 매우 즐겁습니다.
비토리오 로미오

답변:


14

getBodiesToCheck()

기능에는 두 가지 문제가있을 수 있습니다 getBodiesToCheck(). 먼저:

if(!contains(bodiesToCheck, b)) bodiesToCheck.push_back(b);

이 부분은 O (n 2 ) 아닌가요?

본문이 이미 목록에 있는지 확인하는 대신 페인팅을 사용하십시오.

loop_count++;
if(!loop_count) { // if loop_count can wrap,
    // you just need to iterate all bodies to reset it here
}
bodiesToCheck.clear();
for(const auto& q : queries)
    for(const auto& b : *q)
        if(b->paint != loop_count) {
            bodiesToCheck.push_back(b);
            b->paint = loop_count;
        }
return bodiesToCheck;

수집 단계에서 포인터를 역 참조하고 있지만 테스트 단계에서 포인터를 역 참조하고 있으므로 L1이 충분하지 않으면 큰 문제가되지 않습니다. 컴파일러에 프리 페치 힌트를 추가하여 성능을 향상시킬 수 있습니다 (예 : __builtin_prefetch클래식 for(int i=q->length; i-->0; )루프 등에서 는 더 쉬움) .

그것은 간단한 조정이지만, 나의 두 번째 생각은 이것을 구성하는 더 빠른 방법이있을 수 있다는 것입니다.

대신 비트 맵 을 사용 하고 전체 bodiesToCheck벡터를 피하는 것으로 이동할 수 있습니다 . 접근 방식은 다음과 같습니다.

이미 몸통에 정수 키를 사용하고 있지만지도와 사물을 살펴보고 목록을 유지합니다. 기본적으로 배열 또는 벡터 인 슬롯 할당기로 이동할 수 있습니다. 예 :

class TBodyImpl {
   public:
       virtual ~TBodyImpl() {}
       virtual void onHit(int other) {}
       virtual ....
       const int slot;
   protected:
      TBodyImpl(int slot): slot(slot_) {}
};

struct TBodyBase {
    enum ... type;
    ...
    rect_t rect;
    TQuadTreeNode *quadTreeNode; // see below
    TBodyImpl* imp; // often null
};

std::vector<TBodyBase> bodies; // not pointers to them

이것이 의미하는 것은 실제 충돌을 수행하는 데 필요한 모든 작업이 선형 캐시 친화적 메모리에 있으며 구현 관련 비트로 이동하여 필요한 경우이 슬롯 중 하나에 만 연결한다는 것입니다.

몸이 벡터에 할당을 추적하기 위해 당신은 같은 정수의 배열을 사용하여 비트 맵 사용 비트 만지작 또는 __builtin_ffs이 현재 점유 슬롯으로 이동하거나 배열의 비어있는 슬롯을 찾기 위해 매우 효율적이다 등. 간격이 불필요하게 커지도록 끝 부분을 이동하여 배열이 불필요하게 커지면 로트가 삭제 된 것으로 표시되는 경우가 있습니다.

각 충돌을 한 번만 확인하십시오.

당신이 경우 검사 한 경우 와 충돌 B , 당신은 있는지 확인 할 필요가 없습니다 과 충돌을 도.

정수 if를 사용하면 간단한 if 문으로 이러한 검사를 피할 수 있습니다. 잠재적 충돌의 ID가 현재 확인중인 ID보다 작거나 같으면 건너 뛸 수 있습니다! 이런 식으로, 당신은 한 번만 가능한 각 페어링을 확인합니다; 충돌 검사 횟수의 절반 이상이됩니다.

unsigned * bitmap;
int bitmap_len;
...

for(int i=0; i<bitmap_len; i++) {
  unsigned mask = bitmap[i];
  while(mask) {
      const int j = __builtin_ffs(mask);
      const int slot = i*sizeof(unsigned)*8+j;
      for(int neighbour: get_neighbours(slot))
          if(neighbour > slot)
              check_for_collision(slot,neighbour);
      mask >>= j;
  }

충돌 순서를 존중

쌍을 찾 자마자 충돌을 평가하는 대신 거리를 계산하여 이진 힙 에 저장하십시오 . 이러한 힙은 일반적으로 경로 찾기에서 우선 순위 큐를 수행하는 방법이므로 매우 유용한 유틸리티 코드입니다.

각 노드에 시퀀스 번호를 표시하여 다음과 같이 말할 수 있습니다.

  • A 10이 6에서 B 12 를 명중
  • A 10 은 3에서 C 12 를 명중합니다

분명히 모든 충돌을 수집 한 후 우선 순위 대기열에서 가장 먼저 충돌을 시작합니다. 따라서 A 10 은 3에서 C 12 에 충돌 합니다. 각 객체의 시퀀스 번호 ( 10 비트)를 증가 시키고 충돌을 평가하며 새로운 경로를 계산하고 새로운 충돌을 동일한 대기열에 저장합니다. 새로운 충돌은 A 11이 7에서 B 12 를 때리는 것입니다 . 대기열에는 이제 다음이 있습니다.

  • A 10이 6에서 B 12 를 명중
  • A 11이 7에서 B 12 를 명중

그런 다음 우선 순위 대기열에서 팝하고 A 10이 6에서 B 12에 도달합니다. 그러나 A 10오래 되었다는 것을 알 수 있습니다 . A는 현재 11에 있습니다. 따라서이 충돌을 버릴 수 있습니다.

트리에서 모든 부실 충돌을 삭제하려고 시도하지 않는 것이 중요합니다. 힙에서 제거하는 것은 비싸다. 튀어 나오면 그냥 버립니다.

그리드

대신 쿼드 트리 사용을 고려해야합니다. 구현하기 매우 간단한 데이터 구조입니다. 종종 포인트를 저장하는 구현을 볼 수 있지만 rect를 저장하고이를 포함하는 노드에 요소를 저장하는 것을 선호합니다. 즉, 충돌을 확인하려면 모든 바디에 대해 반복해야하며 각각에 대해 동일한 쿼드 트리 노드 (위에 설명 된 정렬 트릭 사용)와 부모 쿼드 트리 노드의 바디에 대해 확인해야합니다. 쿼드 트리 자체가 가능한 충돌 목록입니다.

간단한 쿼드 트리는 다음과 같습니다.

struct Object {
    Rect bounds;
    Point pos;
    Object * prev, * next;
    QuadTreeNode * parent;
};

struct QuadTreeNode {
    Rect bounds;
    Point centre;
    Object * staticObjects;
    Object * movableObjects;
    QuadTreeNode * parent; // null if root
    QuadTreeNode * children[4]; // null for unallocated children
};

정적 객체가 어떤 것과 충돌하는지 확인할 필요가 없기 때문에 이동 가능한 객체를 별도로 저장합니다.

모든 객체를 축 정렬 경계 상자 (AABB)로 모델링하고 있으며 객체를 포함하는 가장 작은 QuadTreeNode에 배치합니다. QuadTreeNode가 많은 하위 항목 인 경우 하위 항목을 세분화 할 수 있습니다 (해당 개체가 하위 항목으로 원활하게 배포되는 경우).

각 게임 틱마다 쿼드 트리로 재귀를 이동하고 각 이동 가능한 객체의 이동 및 충돌을 계산해야합니다. 다음과의 충돌이 있는지 확인해야합니다.

  • 노드의 모든 정적 객체
  • 움직일 수있는 객체 목록에서 노드의 이전 (또는 그 이후의 방향 만 선택)에있는 모든 이동 가능한 객체
  • 모든 부모 노드의 모든 이동 가능하고 정적 객체

순서가 정해지지 않은 모든 충돌이 발생합니다. 그런 다음 이동합니다. 이 동작을 거리와 '누가 먼저 이동'(특별한 요구 사항)으로 우선 순위를 정하고 순서대로 실행해야합니다. 이를 위해 힙을 사용하십시오.

이 쿼드 트리 템플릿을 최적화 할 수 있습니다. 실제로 경계와 중심점을 저장할 필요는 없습니다. 그것은 나무를 걸을 때 완전히 파생됩니다. 모델이 경계 내에 있는지 확인할 필요는 없으며 모델이 어느 쪽이 중심점인지 확인하십시오 ( "분리 축"테스트).

발사체와 같이 빠르게 비행하는 물체를 각 단계로 이동하거나 항상 확인하는 별도의 '글 머리 기호'목록을 갖기보다는 몇 가지 게임 단계를 위해 비행의 정점으로 쿼드 트리에 배치하십시오. 이것은 쿼드 트리에서 훨씬 더 드물게 이동한다는 것을 의미하지만 멀리 떨어진 벽에 대해 총알을 확인하지 않으므로 좋은 절충안입니다.

큰 정적 객체는 구성 요소 부분으로 분할해야합니다. 예를 들어 큰 큐브에는 각면이 별도로 저장되어 있어야합니다.


"Painting"은 좋은 소리가납니다. 가능한 빨리 시험해보고 결과를 알려 드리겠습니다. 그래도 대답의 두 번째 부분을 이해하지 못합니다. 프리 페치에 대한 내용을 읽으려고합니다.
Vittorio Romeo

나는 QuadTree를 권장하지 않을 것입니다. 그리는 그리드를 수행하는 것보다 복잡하며 제대로 수행되지 않으면 정확하게 작동하지 않으며 노드를 너무 자주 만들거나 제거합니다.
ClickerMonkey 2016 년

당신의 힙에 대해 : 운동 순서가 존중됩니까? 본문 A 와 본문 B를 고려하십시오 . 이제 A. 향해 오른쪽의 B 향해 오른쪽으로 이동하고와 B의 이동 - 그들은 동시에 충돌 할 때, 한 최초의 이동 을하셔야합니다 먼저 해결 하고, 다른 하나는 영향을받지 않을 것입니다.
Vittorio Romeo

@VittorioRomeo 만약 A가 B를 향해 움직이고 B가 같은 진드기로 A를 향해 움직이면 같은 속도로 중간에서 만나는가? 아니면 먼저 움직이는 A가 B가 시작되는 B를 만나는가?
Will


3

본문을 반복 할 때 많은 캐시 누락이 발생했습니다. 데이터 지향 디자인 방식을 사용하여 모든 신체를 모으고 있습니까? N ^ 2 브로드 페이즈 를 사용하면 프레임 속도가 네 더리 영역 (60 미만)으로 떨어지지 않고 바디를 플랩으로 기록하면서 수백 , 수백시뮬레이션 할 수 있으며, 이는 모두 커스텀 할당자가없는 것입니다. 적절한 캐시 사용으로 수행 할 수있는 작업을 상상해보십시오.

실마리는 여기 있습니다 :

const std::vector<Body *>

이것은 즉시 큰 붉은 깃발을 일으킨다. 이 시체들을 새로운 전화로 할당하고 있습니까? 사용중인 사용자 지정 할당자가 있습니까? 모든 몸을 선형으로 가로 지르는 거대한 배열로 만드는 것이 가장 중요합니다 . 메모리를 선형으로 순회하는 것이 아닌 것 같으면 구현할 수있는 링크 된 목록을 대신 사용해보십시오.

또한 std :: map을 사용하고있는 것 같습니다. std :: map 내의 메모리가 어떻게 할당되는지 알고 있습니까? 각 맵 쿼리마다 O (lg (N)) 복잡성이 있으며 해시 테이블을 사용하여 O (1)로 증가시킬 수 있습니다. 이 외에도 std :: map에 의해 할당 된 메모리는 캐시를 끔찍하게 스 래싱합니다.

내 솔루션은 std :: map 대신 침입 해시 테이블을 사용하는 것입니다. intrusively linked list와 intrusive hash 해시 테이블의 좋은 예는 그의 공동 프로젝트에서 Patrick Wyatt의 기반이다 : https://github.com/webcoyote/coho

간단히 말해 할당 자 및 침입 컨테이너와 같은 사용자 지정 도구를 만들어야 할 것입니다. 이것은 나 자신을 위해 코드를 프로파일 링하지 않고 할 수있는 최선입니다.


"이 시체들을 새로운 전화로 할당하고 있습니까?" new바디를 getBodiesToCheck벡터로 밀 때 명시 적으로 호출하지 않습니다. 내부적으로 발생한다는 의미입니까? 동적 크기의 바디 컬렉션을 유지하면서이를 방지 할 수있는 방법이 있습니까?
Vittorio Romeo

std::map병목 현상이 아닙니다-나는 또한 dense_hash_set어떤 종류의 성능을 얻으 려고 노력 하지 않았 음을 기억 합니다.
Vittorio Romeo

@Vittorio 다음의 일부 getBodiesToCheck 병목인가? 도움이 되려면 정보가 필요합니다.
Maik Semder 2016 년

@MaikSemder : 프로파일 러는 함수 자체보다 더 깊이 가지 않습니다. 전체 기능은 병목 현상입니다. 바디 당 프레임 당 한 번 호출되기 때문입니다. 10000 바디 = getBodiesToCheck프레임 당 10000 호출. 벡터의 지속적인 청소 / 푸시는 함수 자체의 병목 현상이라고 생각합니다. 이 contains방법은 또한 속도 저하의 일부이지만 bodiesToCheck8-10 개를 초과하는 몸체가 없기 때문에 속도가 느려 야합니다.
Vittorio Romeo

@Vittorio는이 정보를 질문에 넣으면 좋을 것입니다. 게임 체인저입니다.) 특히 getBodiesToCheck가 모든 바디에 대해 호출되는 부분을 의미 하므로 각 프레임마다 10000 배입니다. 나는 그들이 그룹에 있다고 말했기 때문에 그룹 정보를 이미 가지고 있다면 왜 그것들을 bodyToCheck-array에 넣었는지 궁금합니다. 그 부분을 자세히 설명하고 나에게 매우 적합한 최적화 후보처럼 보입니다.
Maik Semder 12

1

각 프레임을 확인하기 위해 바디 수를 줄입니다.

실제로 움직일 수있는 물체 만 확인하십시오. 정적 오브젝트는 생성 후 충돌 셀에 한 번만 할당하면됩니다. 이제 하나 이상의 동적 객체가 포함 된 그룹의 충돌 만 확인하십시오. 이것은 각 프레임의 검사 횟수를 줄여야합니다.

쿼드 트리를 사용하십시오. 내 자세한 답변을 보려면 여기를 클릭하십시오

물리 코드에서 모든 할당을 제거하십시오. 이를 위해 프로파일 러를 사용할 수 있습니다. 그러나 C #의 메모리 할당 만 분석 했으므로 C ++을 도울 수 없습니다.

행운을 빕니다!


0

병목 현상 기능에 두 가지 문제 후보가 있습니다.

첫 번째는 "포함"부분입니다. 병목 현상의 주요 원인 일 수 있습니다. 모든 신체에 대해 이미 발견 된 신체를 반복합니다. 어쩌면 벡터 대신 일종의 hash_table / hash_map을 사용해야 할 수도 있습니다. 그런 다음 삽입 속도가 빠릅니다 (중복도 검색). 그러나 나는 특정 숫자를 모른다. 여기서 얼마나 많은 몸체가 반복되는지 전혀 모른다.

두 번째 문제는 vector :: clear 및 push_back 일 수 있습니다. Clear는 재 할당을 유발하거나 유발하지 않을 수 있습니다. 그러나 당신은 그것을 피하고 싶을 수도 있습니다. 해결책은 일부 플래그 배열 일 수 있습니다. 그러나 아마도 많은 객체가있을 수 있으므로 모든 객체에 대한 모든 객체 목록을 갖는 것은 비효율적입니다. 다른 접근 방식은 좋을지 모르지만 어떤 접근 방식이 있는지 모르겠습니다.


첫 번째 문제에 대해 : vector + contains 대신 dense_hash_set을 사용하려고 시도했지만 속도가 느 렸습니다. 벡터를 채우고 모든 복제본을 제거하려고 시도했지만 속도가 느 렸습니다.
Vittorio Romeo

0

참고 : 나는 C ++, Java 만 알지 못하지만 코드를 알아낼 수 있어야합니다. 물리학은 보편적 언어입니까? 또한이 게시물은 1 년 전의 게시물임을 알고 있지만 모든 사람과 공유하고 싶었습니다.

기본적으로 엔티티가 이동 한 후 NULL 객체를 포함하여 충돌 한 객체를 반환하는 관찰자 패턴이 있습니다. 간단히 말해서:

( 나는 마인 크래프트를 만들고있다 )

public Block collided(){
   return World.getBlock(getLocation());
}

당신이 당신의 세계에서 방황하고 있다고 가정하십시오. 전화 move(1)할 때마다 전화하십시오 collided(). 원하는 블록을 얻는다면 파티클이 날아갈 수 있으며 좌우로 움직일 수는 있지만 앞으로 이동할 수는 없습니다.

예를 들어 마인 크래프트보다 더 일반적으로 이것을 사용하십시오.

public Object collided(){
   return threeDarray[3][2][3];
}

간단히 말해서, Java가 문자를 사용하는 방식으로 포인터를 사용하는 좌표를 가리 키도록 배열을 작성하십시오.

이 방법을 사용하려면 여전히 충돌 사전 탐지 방법 이외의 방법이 필요합니다 . 이것을 반복 할 수는 있지만 목적을 무효화합니다. 이것을 광범위, 중간 및 좁은 충돌 기법에 적용 할 수 있지만, 특히 3D 및 2D 게임에서 잘 작동 할 때 가장 큰 장점입니다.

이제 한 번 더 살펴보면 내 마인 크래프트 collide () 메서드에 따르면 블록 안에 들어가서 플레이어를 외부로 옮겨야한다는 것을 의미합니다. 플레이어를 확인하는 대신 상자의 양쪽에 어떤 블록이 충돌하는지 확인하는 경계 상자를 추가해야합니다. 문제가 해결되었습니다.

위의 단락은 정확도를 원한다면 다각형으로는 쉽지 않을 수 있습니다. 정확성을 위해 사각형이 아니지만 테셀레이션되지 않은 다각형 경계 상자를 정의하는 것이 좋습니다. 그렇지 않다면 사각형은 괜찮습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.