외삽 법으로 충돌 감지 중단


10

스프라이트의 움직임에 외삽 법을 적용하기 전에 내 충돌이 완벽하게 작동했습니다. 그러나 스프라이트의 움직임에 외삽 법을 적용한 후 (사물을 부드럽게) 충돌이 더 이상 작동하지 않습니다.

이것은 외삽 전에 일이 작동하는 방식입니다.

여기에 이미지 설명을 입력하십시오

그러나 외삽 법을 구현하면 충돌 루틴이 중단됩니다. 나는 이것이 외삽 루틴 (내 렌더링 호출에 위치)에 의해 생성 된 새로운 좌표에 작용하기 때문이라고 가정합니다.

외삽을 적용한 후

여기에 이미지 설명을 입력하십시오

이 동작을 수정하는 방법?

외삽 직후에 추가 충돌 검사를 시도했지만 많은 문제가 해결되는 것처럼 보이지만 렌더링에 논리를 넣는 것이 문제가 아니기 때문에 이것을 배제했습니다.

또한 spritesX 위치의 복사본을 만들어 원본보다 외삽하고 그림을 사용하여 그림을 그릴 때 논리가 그대로 유지되도록 원본을 그대로 두었습니다.이 방법이 더 나은 옵션이지만 여전히 이상한 효과가 있습니다. 벽과 충돌 할 때. 나는 이것이 또한 이것을 다루는 올바른 방법이 아니라고 확신합니다.

나는 여기에서 비슷한 몇 가지 질문을 찾았지만 그 대답은 도움이되지 못했습니다.

이것은 내 외삽 코드입니다.

public void onDrawFrame(GL10 gl) {


        //Set/Re-set loop back to 0 to start counting again
        loops=0;

        while(System.currentTimeMillis() > nextGameTick && loops < maxFrameskip){

        SceneManager.getInstance().getCurrentScene().updateLogic();
        nextGameTick+=skipTicks;
        timeCorrection += (1000d/ticksPerSecond) % 1;
        nextGameTick+=timeCorrection;
        timeCorrection %=1;
        loops++;
        tics++;

     }

        extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks; 

        render(extrapolation);
}

외삽 적용

            render(float extrapolation){

            //This example shows extrapolation for X axis only.  Y position (spriteScreenY is assumed to be valid)
            extrapolatedPosX = spriteGridX+(SpriteXVelocity*dt)*extrapolation;
            spriteScreenPosX = extrapolationPosX * screenWidth;

            drawSprite(spriteScreenX, spriteScreenY);           


        }

편집하다

위에서 언급했듯이, Sprite의 좌표 사본을 구체적으로 사용하여 복사하려고 시도했습니다 .... 이것은 자체 문제가 있습니다.

첫 번째로, 복사에 관계없이 스프라이트가 움직일 때 매우 매끄럽고 정지 할 때 약간 왼쪽 / 오른쪽으로 흔들립니다. 여전히 시간을 기준으로 위치를 추정하기 때문입니다. 이 정상적인 동작입니까, 스프라이트가 멈 추면 '끄기'할 수 있습니까?

왼쪽 / 오른쪽에 플래그를 설정하고 이들 중 하나가 활성화 된 경우에만 외삽을 시도했습니다. 또한 차이가 있는지 확인하기 위해 마지막 위치와 현재 위치를 복사하려고했습니다. 그러나 충돌이 발생하는 한 도움이되지 않습니다.

사용자가 say를 누르고 오른쪽 버튼과 스프라이트가 오른쪽으로 움직이면 벽에 닿을 때 오른쪽 버튼을 계속 누르고 있으면 스프라이트는 벽에 의해 정지 된 상태에서 오른쪽으로 계속 애니메이션됩니다 ( 그러나 오른쪽 플래그가 여전히 설정되어 있고 충돌 루틴이 스프라이트를 벽 밖으로 지속적으로 이동시키기 때문에 스프라이트가 여전히 움직이고 있음을 코드 (플레이어가 아닌)에 표시하기 때문에 실제로 움직이지 않습니다. 외삽이 계속됩니다. 플레이어가 볼 수있는 것은 스프라이트 '정적'입니다 (예, 애니메이션이지만 실제로 화면을 가로 질러 움직이지는 않습니다). 그리고 외삽 법이 그렇게하려고 할 때마다 격렬하게 흔들립니다 ..... ..이 도움을 바랍니다


1
완전히 이해하려면이 일부 소화를 취할 것 ( '보간'은 겉으로는 12 개의 다른 의미를 가지고 있으며, 여기에서 당신이 무엇을 의미 언뜻보기에 전적으로 명확하지 않다),하지만 내 첫번째 본능은 당신이 일을해서는 안된다 '는 어떤 영향을 당신의 렌더링 루틴에서의 오브젝트 위치 '. 렌더러는 오브젝트를 지정된 위치에 그려야하고, 오브젝트를 조작하는 것은 본질적으로 렌더러를 게임의 물리학에 연결하기 때문에 문제를 일으키는 레시피가 있습니다. 이상적인 세계에서는 렌더러가 게임 오브젝트에 대한 const 포인터를 사용할 수 있어야합니다.
Steven Stadnicki

안녕하세요 @StevenStadnicki, 많은 감사 의견을 남겨주, 렌더러에 전달되는 보간 값을 보여주는 사례의 다수가이 참조하십시오 mysecretroom.com/www/programming-and-software/... 내가 적응 곳에서 내 암호. 나의 제한된 이해는 이것이 마지막 업데이트 이후에 걸린 시간을 기반으로 마지막 업데이트와 다음 업데이트 사이의 위치를 ​​보간한다는 것입니다. 나는 약간 악몽에 동의합니다! 환호 :-) - 당신이 내보다 쉽게 작업 할 수 제안 및 대체 수 있다면 감사하겠습니다
BungleBonce

6
글쎄,``뭔가를위한 코드를 찾을 수 있기 때문에 최선의 방법은 아닙니다 ''라고 말할 것입니다. :-) 그러나이 경우 링크 된 페이지가 보간 값을 사용하여 객체 를 표시 할 위치를 파악 하지만 실제로 객체 위치를 업데이트하지는 않습니다. 매 프레임마다 계산 되는 드로우 특정 위치를 가지지 만 해당 위치를 객체의 실제 '물리적'위치와 분리하여 유지하면됩니다.
Steven Stadnicki

안녕하세요 @StevenStadnicki, 내 질문 ( "복사본을 만들려고 노력했습니다"으로 시작하는 단락)에 설명 된 것처럼 실제로 이미 'draw only'위치를 사용하려고했습니다 :-) 보간 방법을 제안 할 수 있습니까 렌더링 루틴 내에서 스프라이트의 위치를 ​​조정할 필요가 없습니까? 감사!
BungleBonce

코드를 보면 보간 대신 외삽을하는 것처럼 보입니다.
Durza007

답변:


1

아직 의견을 게시 할 수 없으므로 답변으로 게시하겠습니다.

문제를 올바르게 이해하면 다음과 같이 진행됩니다.

  1. 먼저 충돌이 있습니다
  2. 그런 다음 물체의 위치가 수정됩니다 (아마 충돌 감지 루틴에 의해)
  3. 객체의 업데이트 된 위치가 렌더 함수로 전송됩니다
  4. 그런 다음 render 함수는 외삽 법을 사용하여 객체의 위치를 ​​업데이트합니다.
  5. 외삽 된 물체의 위치가 충돌 감지 루틴을 중단시킵니다.

가능한 3 가지 해결책을 생각할 수 있습니다. 최소 IMHO에 가장 바람직한 순서로 나열합니다.

  1. 외삽을 렌더 함수 밖으로 이동하십시오. 물체의 위치를 ​​추정 한 다음 충돌을 테스트합니다.
  2. 또는 렌더링 함수에서 외삽을 유지하려면 충돌이 발생했음을 나타내는 플래그를 설정하십시오. 충돌 감지는 이미 수행 한 것처럼 객체의 위치를 ​​수정하도록하지만 외삽 값을 계산하기 전에 먼저 충돌 플래그를 확인하십시오. 객체는 이미 필요한 곳에 있기 때문에 이동할 필요가 없습니다.
  3. 나에게 수정보다 더 많은 해결 방법이 될 수있는 마지막 가능성은 충돌 감지를 과도하게 보상하는 것입니다. 충돌 후 외삽 후 오브젝트가 벽에 다시 오도록 오브젝트를 벽에서 멀리 이동시킵니다.

# 2의 예제 코드.

if (collisionHasOccured)
    extrpolation = 0.0f;
else
    extrapolation = (float)(System.currentTimeMillis() + skipTicks - nextGameTick) / (float)skipTicks;

# 2가 더 논리적으로 정확한 솔루션 인 것 같지만 # 2는 아마도 가장 빠르고 가장 쉬운 방법이라고 생각합니다. 델타 시간 솔루션 # 1을 처리하는 방법에 따라 큰 델타가 동일한 방식으로 크게 깰 수 있습니다.이 경우 # 1과 # 2를 함께 사용해야합니다.

편집 : 나는 당신의 코드를 일찍 이해하지 못했습니다. 루프는 가능한 빨리 렌더링하고 설정된 간격으로 업데이트합니다. 이것이 업데이트보다 더 많은 것을 그리는 경우를 처리하기 위해 스프라이트 위치를 보간하는 이유입니다. 그러나 루프가 뒤쳐지면 잡히거나 최대 프레임 드로우 수를 건너 뛸 때까지 업데이트를 폴링합니다.

즉, 유일한 문제는 충돌 후 물체가 움직이고 있다는 것입니다. 충돌이 발생하면 객체는 해당 방향으로 이동을 중지해야합니다. 따라서 충돌이 발생하면 속도를 0으로 설정하십시오. 그러면 렌더링 기능이 오브젝트를 더 이상 이동하지 못하게됩니다.


안녕하세요 @ Aholio 옵션 2를 시도했지만 작동하지만 몇 가지 결함이 발생하여 잘못했을 수 있습니다. 다시 방문하겠습니다. 그러나이 작업을 수행하는 방법에 대한 정보를 찾을 수는 없지만 옵션 1에 매우 관심이 있습니다. 논리 루틴에서 어떻게 말을 추정 할 수 있습니까? BTW 내 델타가 고정되어 있으므로 1/60 또는 0.01667입니다. (실제로 게임의 각 반복에 걸리는 시간이 진행 상황에 따라 다르지만 실제로는 0.01667 이상을 가져서는 안되지만 통합하는 데 사용하는 수치입니다. ) 따라서 도움을 주시면 감사하겠습니다.
BungleBonce

아마도 나는 당신이 무엇을하는지 완전히 이해하지 못합니다. 나에게 조금 이상해 보이는 것은 당신이 당신의 드로우 기능에서 위치 이동만을하는 것이 아니라는 것입니다. 당신은 또한 시간 계산을하고 있습니다. 이것은 모든 객체에 대해 수행됩니까? 올바른 순서는 게임 루프 코드에서 수행 된 시간 계산입니다. 게임 루프는 델타를 update (dt) 함수로 전달합니다. 업데이트 (dt)는 모든 게임 오브젝트를 업데이트합니다. 모든 움직임, 외삽 및 충돌 감지를 처리해야합니다. update ()가 게임 루프로 돌아간 후 새로 업데이트 된 모든 게임 객체를 그리는 render () 함수를 호출합니다.
Aholio

흠, 현재 내 업데이트 기능이 모든 것을 수행합니다. 움직임을합니까 (움직임에 의해 스프라이트의 새로운 위치를 계산합니다-이것은 델타 시간으로 계산됩니다). 렌더 기능 (실제로 그리기 제외)에서하고있는 유일한 것은 '새로운'위치를 추정하는 것입니다. 물론 이것은 마지막 논리 업데이트에서 렌더 호출까지의 시간을 고려해야하기 때문에 시간을 사용합니다. 어쨌든 내 이해입니다 :-) 어떻게할까요? 논리 업데이트 내에서 외삽 법을 사용하는 방법을 이해하지 못합니다. 감사!
BungleBonce

내 답변을 업데이트했습니다. 도움이
되길 바랍니다

고마워요, 내 원래 Q에서 "정지되면 약간 왼쪽 / 오른쪽으로 흔들립니다"-스프라이트를 중지하더라도 외삽 법은 여전히 ​​스프라이트를 '움직이는'상태로 유지합니다. 내 외삽을 해결하기 위해 스프라이트의 이전 및 현재 위치, 따라서 스프라이트가 정적이더라도 각 프레임 반복마다 해결되는 '외삽'값을 기반으로이 흔들림 효과가 여전히 있습니다. 나는 심지어 '오래된 현재 pos가 같다면 외삽하지 마십시오'라고 말하려고 시도했지만 여전히 작동하지 않는 것 같습니다. 나는 다시 가서 또 다른 모습을 취할 것입니다!
BungleBonce

0

물리 렌더링과 업데이트를 완전히 분리해야하는 것처럼 보입니다. 일반적으로 기본 시뮬레이션은 개별 시간 단계로 실행되며 빈도는 절대 변하지 않습니다. 예를 들어 1/60 초마다 공의 움직임을 시뮬레이션 할 수 있습니다.

가변 프레임 레이트를 허용하기 위해 렌더링 코드는 가변 주파수에서 작동해야하지만 모든 시뮬레이션은 여전히 ​​고정 된 시간 단계에 있어야합니다. 이를 통해 그래픽이 시뮬레이션에서 메모리를 읽기 전용으로 읽을 수 있으며 외삽 대신 보간을 설정할 수 있습니다.

외삽 법은 미래의 가치가 어디로 향할 것인지를 예측하려고하기 때문에, 갑작스러운 움직임의 변화는 막대한 외삽 오차를 줄 수 있습니다. 대신 시뮬레이션 뒤의 프레임에 대해 장면을 렌더링하고 불연속 알려진 위치간에 보간하는 것이 좋습니다.

구현에 대한 자세한 내용을 보려면 here here 기사 에서이 주제에 대한 짧은 섹션을 이미 작성했습니다 . "타임 스텝핑"섹션을 참조하십시오.

이 기사의 중요한 의사 코드는 다음과 같습니다.

const float fps = 100
const float dt = 1 / fps
float accumulator = 0

// In units seconds
float frameStart = GetCurrentTime( )

// main loop
while(true)
  const float currentTime = GetCurrentTime( )

  // Store the time elapsed since the last frame began
  accumulator += currentTime - frameStart( )

  // Record the starting of this frame
  frameStart = currentTime

  // Avoid spiral of death and clamp dt, thus clamping
  // how many times the UpdatePhysics can be called in
  // a single game loop.
  if(accumulator > 0.2f)
    accumulator = 0.2f

  while(accumulator > dt)
    UpdatePhysics( dt )
    accumulator -= dt

  const float alpha = accumulator / dt;

  RenderGame( alpha )

void RenderGame( float alpha )
  for shape in game do
    // calculate an interpolated transform for rendering
    Transform i = shape.previous * alpha + shape.current * (1.0f - alpha)
    shape.previous = shape.current
    shape.Render( i )

RenderGame기능이 가장 중요합니다. 아이디어는 이산 시뮬레이션 위치 사이에 보간을 사용하는 것입니다. 렌더링 코드는 시뮬레이션 읽기 전용 데이터의 자체 사본을 만들고 임시 보간 값을 사용하여 렌더링 할 수 있습니다. 이것은 당신이 가지고있는 것처럼 바보 같은 문제없이 당신에게 매우 부드러운 움직임을 줄 것입니다!

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