2014-12-16 2 views
0

충돌 감지 시스템을 구현하려고하는데 대부분의 경우 겹치지 않고 (또는 거의 겹치지 않고) 문자 충돌 및 벽 충돌이 발생합니다. 문제는 내가 선수 한 명을 따라 다니면서 그 선수 한 명을 만난다는 것인데, 15-20 명 정도의 선수가 선수를 밀 때 플레이어 나 다른 물건들이 벽을 통해 밀려날 수있다.C++ - 충돌 응답으로 클리핑이 발생합니다.

내 코드는 다음과 같이 작동합니다. 먼저 모든 문자를 업데이트하고 서로 충돌을 확인한 다음 벽과의 문자 충돌을 확인합니다. 문제가되는 것처럼 모든 문자를 밀어 넣으면 하나 이상의 문자를 먼 거리까지 밀어야하지만 문제를 해결하는 방법을 모르겠습니다. 필요한 경우 아래의 코드를 수정하는 방법에 대한 철저한 설명으로도 충분합니다.

문자 업데이트/충돌 :

void CharacterManager::updateAll(float elapsedTime) 
{ 
    for(std::vector<std::shared_ptr<Character>>::iterator i = _characters.begin(); i != _characters.end(); i++) { 
     (*i)->update(elapsedTime); 
    } 
    collisions(); 
} 

void CharacterManager::collisions() 
{ 
    for(std::vector<std::shared_ptr<Character>>::iterator i = _characters.begin(); i != _characters.end(); i++) { 
     for(std::vector<std::shared_ptr<Character>>::iterator j = _characters.begin(); j != _characters.end(); j++) { 
      if(i == j) continue; 
      float xi = (*i)->position().x; 
      float yi = (*i)->position().y; 
      float xj = (*j)->position().x; 
      float yj = (*j)->position().y; 
      float dx = xi - xj; 
      float dy = yi - yj; 
      float distSquared = dx * dx + dy * dy; 
      float ri = (*i)->xRect().width/2; 
      float rj = (*j)->xRect().width/2; 
      if(distSquared < (ri + rj) * (ri + rj)) { 
       // fix collisions 
       float angle = atan2f(dy,dx); 
       float overlap = (ri + rj) - sqrt(distSquared); 
       if(xi < xj) { 
        if(yi < yj) { 
         (*i)->position(xi - cosf(angle) * overlap/2, yi - sinf(angle) * overlap/2); 
         (*j)->position(xj + cosf(angle) * overlap/2, yj + sinf(angle) * overlap/2); 
        } else { 
         (*i)->position(xi - cosf(angle) * overlap/2, yi + sinf(angle) * overlap/2); 
         (*j)->position(xj + cosf(angle) * overlap/2, yj - sinf(angle) * overlap/2); 
        } 
       } else { 
        if(yi < yj) { 
         (*i)->position(xi + cosf(angle) * overlap/2, yi - sinf(angle) * overlap/2); 
         (*j)->position(xj - cosf(angle) * overlap/2, yj + sinf(angle) * overlap/2); 
        } else { 
         (*i)->position(xi + cosf(angle) * overlap/2, yi + sinf(angle) * overlap/2); 
         (*j)->position(xj - cosf(angle) * overlap/2, yj - sinf(angle) * overlap/2); 
        } 
       } 
       // calc new velocities 
       float vxi = (*i)->velocity().x; 
       float vyi = (*i)->velocity().y; 
       float vxj = (*j)->velocity().x; 
       float vyj = (*j)->velocity().y; 
       float vx = vxj - vxi; 
       float vy = vyj - vyi; 
       float dotProduct = dx * vx + dy * vy; 
       if(dotProduct >= 0) { 

        float collisionScale = dotProduct/distSquared; 
        float xCollision = dx * collisionScale; 
        float yCollision = dy * collisionScale; 
        float combinedMass = (*i)->weight() + (*j)->weight(); 
        float collisionWeightA = 2 * (*j)->weight()/combinedMass; 
        float collisionWeightB = 2 * (*i)->weight()/combinedMass; 
        (*i)->velocity(vxi + collisionWeightA * xCollision, vyi + collisionWeightA * yCollision); 
        (*j)->velocity(vxj - collisionWeightB * xCollision, vyj - collisionWeightB * yCollision); 
       } 
      } 
     } 
    } 
} 

벽 충돌 : 두 객체가 서로 교차 할 때

void Stage::characterCrossCollisions(std::shared_ptr<Character> character) 
{ 
    for(std::vector<std::shared_ptr<Tile>>::iterator tile = tiles.begin(); tile != tiles.end(); tile++) { 
     if(!(*tile)->walkable) { 
      sf::Rect<float> cxr = character->xRect(); 
      sf::Rect<float> cyr = character->yRect(); 
      sf::Rect<float> tr = (*tile)->getRect(); 

      if(!(cxr.left > tr.left + tr.width || 
       cxr.left + cxr.width < tr.left || 
       cxr.top > tr.top + tr.height || 
       cxr.top + cxr.height < tr.top)) { 
       float ox = 0; 
       if(character->position().x > (*tile)->position().x) { 
        ox = cxr.left - (tr.left + tr.width); 
       } 
       else { 
        ox = cxr.left + cxr.width - tr.left; 
       } 
       character->position(character->position().x - ox, character->position().y); 
      } 

      if(!(cyr.left > tr.left + tr.width || 
       cyr.left + cyr.width < tr.left || 
       cyr.top > tr.top + tr.height || 
       cyr.top + cyr.height < tr.top)) { 
       float oy = 0; 
       if(character->position().y > (*tile)->position().y) { 
        oy = cyr.top - (tr.top + tr.height); 
       } 
       else { 
        oy = cyr.top + cyr.height - tr.top; 
       } 
       character->position(character->position().x, character->position().y - oy); 
      } 
     } 
    } 
} 

답변

3

은 일반적으로 두 개체의 충돌 코드를 실행합니다. 두 객체는 ​​공간에서 적어도 하나의 점을 공유하면 서로 교차합니다. 그러나이 문제는 객체가 교차하고있는 경우 즉,에 충돌이 있었음을 의미하며 지금은 충돌이 없다는 것을 의미하는 입니다.

이상적인 충돌 코드는 에너지 전달을 계산하고 물체가 서로 접촉 할 때 의 정확한 순간을 객체의 속도를 수정해야합니다. 좋은 충돌 코드는 시간을 되돌리고 충돌이 발생한 순간을 찾아 내고, 그 순간을 기준으로 새로운 속도를 계산하고 앞으로 시간을 굴립니다. 그러나 이것들은하기가 어렵고 간단한 컴퓨터 게임을 위해 과도 할 수도 있습니다. 내가 당신에게 추천 할 수

쉬운하지만 강력한 솔루션은 다음과 같습니다

  1. 이동 앞으로 오브젝트 충돌에 대한
  2. 검사, 경우 서로 오브젝트 멀리
  3. 이동을 시작에서 충돌 반복하지 그들이 질량에 비례하지 않을 때까지. 벽 당신은 그들이 무한한 질량을 가지고 고려할 수 이동하고 문자 만 이동하지 않기 때문에
  4. 더 이상 반복

또한 수 교차하지 않는 객체 후 충돌하는 물체의 속도를 계산 과 같은 제약 조건을 사용하십시오. '물체는 벽과 교차 할 수 없습니다.'이 문자를 움직일 때 새로운 위치가 유효한지 확인하여이 제약 조건을 적용합니다. 새로운 위치가 유효한 경우에만 캐릭터를 움직입니다.

이 작은 예제는 유효성 검증을 보여 주어야합니다. MoveTo() 메서드로 위치를 업데이트 만 가능하게 만들고 MoveTo() 메서드 내에서 새 위치의 유효성을 검사하고 이동이 성공했는지 여부를 반환 할 수 있습니다. 이동이 성공적이지 않으면 호출자는 다른 조치를 취하려고합니다.(정확하게 접촉 위치까지 물체를 움직여 충돌을 처리 할 수있는 절호의 기회가 될 것입니다.)

class Character{ 

    bool MoveTo(float x, float y) 
    { 
     if (this.isValidPosition(x,y)) 
     { 
      this.x = x; 
      this.y = y; 
      return true; 
     } 
     return false; 
    } 

    void Update(float deltaTime) 
    { 
     float new_x = x + velocity_x*deltaTime; 
     float new_y = y + velocity_y*deltaTime; 

     if (!this.MoveTo(new_x, new_y)) 
     { 
      Console.Write("cannot move " + this + " to the new position, something is already there\n"); 
     } 
    } 

} 
+0

매우 흥미롭고, 이런 식의 업데이트는 절대로 생각해 본적이 없습니다. –

+0

이 시점에서, 나는 동시에 겹치는 두 개 이상의 객체 객체와 그들의 최종 위치/속도를 해결하는 방법을 다루는 데 어려움을 겪고 있습니다. –

관련 문제