2D 물리 엔진에서 래핑 와이어 (예 : Worms Ninja Rope) 구현


34

최근에 로프 물리학을 시험해 보았는데 스프링이나 조인트와 함께 묶인 일련의 물체로 로프를 만드는 "표준"솔루션이 만족스럽지 않다는 것을 알게되었습니다. 특히 로프 스윙이 게임 플레이와 관련이있을 때. 나는 줄을 감싸거나 처지는 능력에 대해 정말로 신경 쓰지 않습니다.

게임 플레이에서 중요한 것은 로프가 환경감싸고 나서 랩핑 하는 능력입니다 . 로프처럼 행동 할 필요조차 없습니다. 직선 선으로 구성된 "와이어"가 할 수 있습니다. 그림은 다음과 같습니다.

닌자 밧줄, 장애물을 감싸

이것은 웜 게임의 "닌자 로프"와 매우 유사합니다.

2D 물리 엔진을 사용하고 있기 때문에 내 환경은 2D 볼록 다각형으로 구성됩니다. (특히 Farseer에서 SAT를 사용하고 있습니다.)

제 질문은 이것입니다 : "래핑"효과를 어떻게 구현 하시겠습니까?

와이어가 "분할"및 "결합"하는 일련의 라인 세그먼트로 구성 될 것임은 분명합니다. 그리고 움직이는 객체가 부착되는 해당 라인의 마지막 (활성) 세그먼트는 고정 길이 조인트가됩니다.

그러나 활성 선분을 언제 어디서 분할해야하는지 결정하는 데 필요한 수학 / 알고리즘은 무엇입니까? 그리고 이전 세그먼트와 결합해야 할 때?

(이전 에이 질문은 또한 동적 환경 에서이 작업을 수행하는 방법에 대해서도 질문했습니다. 다른 질문으로 나누기로 결정했습니다.)

답변:


18

로프를 분리 할시기를 결정하려면 로프가 각 프레임을 덮는 영역을 확인해야합니다. 당신이하는 일은 덮여있는 영역과 레벨 지오메트리로 충돌 검사를하는 것입니다. 스윙이 커버하는 영역은 호 여야합니다. 충돌이 발생하면 로프에 새 세그먼트를 만들어야합니다. 스윙 아크와 충돌하는 모서리를 확인하십시오. 스윙 호와 충돌하는 모서리가 여러 개인 경우 이전 프레임 동안 로프와 충돌 지점 사이의 각도가 가장 작은 모서리를 선택해야합니다.

닌자 로프 상황에 대한 유용한 다이어그램

충돌 감지를 수행하는 방법은 현재 로프 세그먼트의 루트 인 O, 이전 프레임의 로프 끝 위치, A, 현재 프레임의 로프 끝 위치, B 및 다각형의 각 코너 포인트 P입니다. 레벨 형상에서 (OA x OP), (OP x OB) 및 (OA x OB)를 계산합니다. 여기서 "x"는 두 벡터 사이의 교차 곱의 Z 좌표를 나타냅니다. 세 결과 모두 음수 또는 양수의 부호가 같고 OP의 길이가 로프 세그먼트의 길이보다 작 으면 점 P가 스윙으로 덮여있는 영역 내에 있으므로 로프를 분할해야합니다. 충돌하는 코너 포인트가 여러 개있는 경우 로프에 닿는 첫 번째 포인트를 사용하려고합니다. 즉, OA와 OP 사이의 각도가 가장 작은 것을 의미합니다. 내적을 사용하여 각도를 결정하십시오.

세그먼트 결합에 대해서는 이전 세그먼트와 현재 세그먼트의 호를 비교하십시오. 현재 세그먼트가 왼쪽에서 오른쪽으로 또는 그 반대로 회전 한 경우 세그먼트를 결합해야합니다.

세그먼트 결합에 대한 수학을 위해 이전 로프 세그먼트 Q의 부착 점과 분할 사례에 대한 점을 사용합니다. 이제 벡터 QO, OA 및 OB를 비교하고 싶을 것입니다. (QO x OA)의 부호가 (QO x OB)의 부호와 다른 경우, 로프가 왼쪽에서 오른쪽으로 또는 그 반대로 교차하여 세그먼트에 합류해야합니다. 로프가 180도 회전하는 경우에도 이런 일이 발생할 수 있습니다. 따라서 로프가 다각형 모양 대신 공간의 단일 지점을 감쌀 수 있도록하려면 특별한 경우를 추가해야합니다.

물론이 방법은 로프에 매달려있는 객체에 대해 충돌 감지를 수행하여 레벨 지오메트리 내부로 끝나지 않는다고 가정합니다.


1
이 접근법의 문제점은 부동 소수점 정밀도 오류로 인해 로프가 점을 통과 할 수 있다는 것입니다.
Andrew Russell

16

내가 웜을 연주한지 오래되었지만, 내가 기억하는 것에서 – 밧줄이 물건을 감쌀 때, 한 번에 움직이는 단 하나의 직선 부분이 있습니다. 로프의 나머지 부분은 고정됩니다

실제 물리학은 거의 없습니다. 활성 단면은 끝에 질량이있는 단일 강성 스프링으로 모델링 할 수 있습니다.

흥미로운 부분은 로프의 비활성 섹션을 활성 섹션과 분리 / 결합하는 논리입니다.

두 가지 주요 작업이 있다고 상상합니다.

'분할 (Split)'-밧줄이 무언가 교차했습니다. 교차로에서 비활성 섹션과 새롭고 더 짧은 활성 섹션으로 분할

'조인'-활성 로프가 가장 가까운 교차점이 더 이상 존재하지 않는 위치로 이동했습니다 (단순한 각도 / 도트 제품 테스트 일 수 있습니까?). 2 개의 섹션을 다시 결합하여 새롭고 긴 활성 섹션을 만듭니다.

2D 다각형으로 구성된 장면에서 모든 분할 지점은 충돌 메시의 꼭짓점에 있어야합니다. 충돌 감지는 '이 업데이트에서 로프가 정점을지나 가면 해당 정점에서 로프를 분할 / 결합합니까?


2
이 녀석은 바로 그 자리에 있었는데 ... 사실, 그것은 "뻣뻣한"봄이 아니며, 당신은 단지 직선을 회전시킬뿐입니다 ...
speeder

당신의 대답은 기술적으로 정확합니다. 그러나 나는 선분을 가지고 그것들을 나누고 합치는 것이 명백하다고 가정했다. 나는 그것을하기위한 실제 알고리즘 / 수학에 관심이 있습니다. 내 질문을 좀 더 구체적으로 만들었습니다.
앤드류 러셀

3

Gusanos 의 닌자 로프가 어떻게 구현 되었는지 확인하십시오 .

  • 로프는 무언가에 부착 될 때까지 입자처럼 작용합니다.
  • 연결되면 로프는 웜에 힘을가합니다.
  • 이 코드에서 다른 웜과 같이 동적 객체에 첨부하는 것은 여전히 ​​TODO입니다.
  • 객체 / 모서리를 감싸는 것이 지원되는지 기억할 수 없습니다 ...

ninjarope.cpp 에서 관련 발췌 :


void NinjaRope::think()
{
    if ( m_length > game.options.ninja_rope_maxLength )
        m_length = game.options.ninja_rope_maxLength;

    if (!active)
        return;

    if ( justCreated && m_type->creation )
    {
        m_type->creation->run(this);
        justCreated = false;
    }

    for ( int i = 0; !deleteMe && i < m_type->repeat; ++i)
    {
        pos += spd;

        BaseVec<long> ipos(pos);

        // TODO: Try to attach to worms/objects

        Vec diff(m_worm->pos, pos);
        float curLen = diff.length();
        Vec force(diff * game.options.ninja_rope_pullForce);

        if(!game.level.getMaterial( ipos.x, ipos.y ).particle_pass)
        {
            if(!attached)
            {
                m_length = 450.f / 16.f - 1.0f;
                attached = true;
                spd.zero();
                if ( m_type->groundCollision  )
                    m_type->groundCollision->run(this);
            }
        }
        else
            attached = false;

        if(attached)
        {
            if(curLen > m_length)
            {
                m_worm->addSpeed(force / curLen);
            }
        }
        else
        {
            spd.y += m_type->gravity;

            if(curLen > m_length)
            {
                spd -= force / curLen;
            }
        }
    }
}

1
음 ... 이것은 내 질문에 전혀 대답하지 않는 것 같습니다. 내 질문의 요점은 다각형으로 만든 세계 주위에 밧줄을 감싸는 것입니다. 구사 노스는 줄 바꿈이없고 비트 맵 세계가없는 것 같습니다.
앤드류 러셀

1

머리 꼭대기에서 구체적인 알고리즘을 줄 수는 없지만, 닌자 로프의 충돌을 감지하는 데 중요한 두 가지가 있습니다. 장애물에 잠재적으로 충돌하는 정점 세그먼트의 나머지 길이와 동일한 마지막 "분할"반경 내; 현재 스윙 방향 (시계 방향 또는 시계 반대 방향). "분할"정점에서 근처의 각 정점까지의 임시 각도 목록을 생성 한 경우, 알고리즘은 세그먼트가 주어진 정점에 대해 해당 각도를 벗어나려고하는지주의를 기울여야합니다. 그렇다면 파이처럼 쉬운 분할 작업을 수행해야합니다. 마지막 분할 정점에서 새 분할까지의 선일 뿐이며 새로운 나머지가 계산됩니다.

정점 만 중요하다고 생각합니다. 장애물에서 꼭짓점 사이의 세그먼트에 부딪 칠 위험이있는 경우, 로프 끝에 매달려있는 사람에 대한 일반적인 충돌 감지가 시작됩니다. 다시 말해 로프는 "snag"상태가됩니다. 어쨌든 코너는 서로 구분되므로 문제가되지 않습니다.

미안하지만 구체적인 내용은 없지만 개념적으로 이러한 일을 수행하기 위해 필요한 곳에 도달 할 수 있기를 바랍니다. :)


1

다음은 유사한 유형의 시뮬레이션에 대한 논문 링크가있는 게시물입니다 (게임이 아닌 공학 / 학술적 맥락에서) : https://gamedev.stackexchange.com/a/10350/6398

이러한 종류의 "와이어"시뮬레이션 (Umihara Kawase 게임에서 볼 수 있음)에 대한 충돌 감지 및 응답에 대해 적어도 두 가지 다른 접근 방식을 시도했습니다. 적어도, 나는 이것이 당신이 추구하는 것이라고 생각합니다. 이런 종류의 시뮬레이션에는 특정한 용어가없는 것 같습니다. 대부분의 사람들처럼 보이기 때문에 "로프"보다는 "와이어"라고 부르는 경향이 있습니다. "로프"를 "입자 사슬"과 동의어로 간주하십시오. 그리고 닌자 로프의 끈적 끈적한 행동을 원한다면 (즉, 밀고 당길 수 있습니다), 이것은 밧줄보다 단단한 철사와 비슷합니다. 어쨌든..

Pekuja의 대답은 양호합니다. 세 지점의 부호있는 영역이 0 인 시간을 해결하여 연속적인 충돌 감지를 구현할 수 있습니다.

(OTOH를 완전히 기억할 수는 없지만 다음과 같이 접근 할 수 있습니다 .a, b, c를 통과하는 행에 점 a가 포함될 때 시간 t를 찾으십시오 (점 (ab, cb) = 0으로 t)의 값을 찾은 다음 유효한 시간 0 <= t <1이 주어지면 세그먼트 bc에서 a의 파라 메트릭 위치 s를 찾습니다. 즉 a = (1-s) b + s c b와 c (즉, 0 <= s <= 1 인 경우)는 유효한 충돌입니다.

AFAICR 당신은 다른 방법으로 접근 할 수 있습니다 (즉, s를 풀고 t를 찾기 위해 이것을 꽂습니다). 그러나 훨씬 덜 직관적입니다. (이것이 말이되지 않으면 죄송합니다. 노트를 파낼 시간이 없으며 몇 년이 지났습니다!)

따라서 이벤트가 발생하는 모든 시간을 계산할 수 있습니다 (예 : 로프 노드를 삽입하거나 제거해야 함). 가장 빠른 이벤트 (노드 삽입 또는 제거)를 처리 한 다음 t = 0과 t = 1 사이에 더 이상 이벤트가 없을 때까지 반복 / 재귀하십시오.

이 접근 방식에 대한 경고 하나 : 로프를 감쌀 수있는 객체가 동적 일 경우 (특히, 객체를 시뮬레이션하거나 로프에 미치는 영향을 반대로하는 경우) 그 객체가 각 클립을 통과하거나 통과 할 경우 문제가 발생할 수 있습니다 다른-와이어가 엉 키게 될 수 있습니다. 그리고 box2d 스타일의 물리 시뮬레이션에서 이러한 종류의 상호 작용 / 이동 (객체의 모서리가 서로 미끄러 져 들어가는 것)을 방지하는 것은 확실히 어려울 것입니다. 그 맥락에서 물체 사이의 작은 양의 침투는 정상적인 행동입니다.

(적어도 .. 이것은 "wire"의 구현 중 하나에 문제가있었습니다.)

훨씬 더 안정적이지만 특정 조건에서 일부 충돌을 놓치는 다른 솔루션은 정적 테스트를 사용하는 것입니다 (즉, 시간 순서에 대해 걱정하지 말고 각 세그먼트를 찾은대로 재귀 적으로 세분화하십시오). 훨씬 더 견고 함-와이어가 모서리에서 엉키지 않고 소량의 침투가 양호합니다.

Pekuja의 접근 방식이 이것에도 효과가 있다고 생각하지만 다른 접근 방식이 있습니다. 내가 사용한 한 가지 접근 방식은 보조 충돌 데이터를 추가하는 것입니다. 세계의 각 볼록한 정점 v (로프가 감쌀 수있는 모양의 모서리)에서 직선 선분 uv를 형성하는 점 u를 추가하십시오. 여기서 u는 일부입니다 "모서리 내부"(즉, 세계 내부, "뒤"v)를 가리 킵니다. u를 계산하려면 보간 법선을 따라 v에서 안쪽으로 광선을 투사하고 v 이후 또는 광선이 세상의 가장자리와 교차하기 전에 약간의 거리를 멈출 수 있습니다. 솔리드 영역을 종료하거나 시각적 도구 / 레벨 편집기를 사용하여 수동으로 세그먼트를 월드에 페인트 할 수 있습니다.

어쨌든, 이제 "corner linesegs"uv 세트가 생겼습니다. 와이어의 각 uv 및 각 세그먼트 ab에 대해, ab와 uv가 교차하는지 확인하십시오 (즉, 정적, 부울 라인-라인-예 : 교차 쿼리). 그렇다면, 재귀를 반복하고 (lineegeg ab를 av 및 vb로 분할, 즉 v를 삽입) v에서 로프가 구부러진 방향을 기록합니다. 그런 다음 와이어에서 인접한 lineegs ab, bc의 각 쌍에 대해 b에서 현재 굽힘 방향이 있는지 테스트합니다. b가 생성 될 때와 동일하다 (이러한 모든 "굽힘 방향"테스트는 부호있는 영역 테스트 임). 그렇지 않은 경우 두 세그먼트를 ac로 병합하십시오 (예 : b 제거).

아니면 내가 합병하고 나뉘었을 수도 있지만 잊어 버릴 수도 있지만 가능한 두 가지 명령 중 적어도 하나에서 작동합니다! :)

현재 프레임에 대해 계산 된 모든 와이어 세그먼트가 주어지면 두 와이어 끝점 사이의 거리 제약 조건을 시뮬레이션 할 수 있습니다 (그리고 내부 점, 즉 와이어와 월드 사이의 접점도 포함 할 수 있음). ).

어쨌든, 잘만되면 이것은 약간의 사용이 될 것입니다.


0

이에 대한 한 가지 접근법은 로프를 스프링으로 연결된 충돌 가능한 입자로 모델링하는 것입니다. (아마도 뼈처럼 뻣뻣한 것). 입자가 환경과 충돌하여 로프가 품목을 감싸도록합니다.

다음은 소스가있는 데모입니다. http://www.ewjordan.com/rgbDemo/

(첫 번째 레벨에서 오른쪽으로 이동하면 상호 작용할 수있는 빨간 밧줄이 있습니다)


2
어-이것은 내가 원하지 않는 것입니다 (질문 참조).
앤드류 러셀

아 원래 질문에서 명확하지 않았습니다. 시간을내어 설명을 해주셔서 감사합니다. (훌륭한 다이어그램!) 동적 분할을 수행하는 것과는 달리 일련의 매우 작은 고정 조인트를 사용합니다. 환경에서 큰 성능 문제가 아니라면 코딩이 훨씬 쉽습니다.
Rachel Blum
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.