2016-10-11 9 views
12

UDP를 사용하여 멀티 플레이어 Pong을 작성했습니다. 클라이언트에서 부드럽게 보이는 효과를 내기 위해 보간과 외삽 법을 사용하고 있습니다."고무 밴딩 (rubber banding)"은 멀티 레이어 보간 스터 터를 해결합니까?

작동합니다. 그러나 볼에 끊임없이 약간의 끊김이 있습니다. 새로운 패킷이 수신 될 때마다 작은 비트가 앞으로 이동합니다. 조금 뒤떨어져 보였지만 재생 가능합니다.

게임을 더 매끄럽게 보이게 만드는 방법이 있어야합니다. 나는 Rubber Banding에 대해 읽었습니다. 여기에서 가장 좋은 방법은 무엇입니까?

내 질문에 잘 답변 할 수있는 사람이 찾아 주길 바랍니다.

이반의 요청에 따라 업데이트

, 여기에 핑 시대의 그래프이다. 그러나 문제는 클라이언트 스무딩 코드 내부에 있다고 생각합니다.

enter image description here

+0

그런데 ping/fps를 측정 했습니까? – Ivan

+0

@ 이반 나는 핑을 측정하고 있습니다. 그러나, 나는 그것을 지금 사용하지 않고있다. – Z0q

+0

나는이 메트릭스에 대해 묻고 있는데, '약간 지체 된'것이 어떤 상황에 있는지를 정량화 할 수 있습니다. 예 : 핑이 900ms라면 기본적으로 어떤 경험도 괜찮습니다 – Ivan

답변

6

당신의 previous question에서 상황에 작성, 난 당신이 다른 각 클라이언트에서 패들 & 볼 위치를 전송하는 것으로 알고 있습니다. 그러나 클라이언트가 각 순간의 패들 위치에 동의하는 한 볼의 움직임은 완전히 결정됩니다 (반올림 오류는 제외). 각 클라이언트는 패들과 볼의 위치와 속도로 자체 내부 상태를 유지해야합니다. 클라이언트가 속도를 변경 할 때마다

  • 패들 패킷 + 속도 패의 타임 스탬프가 위치하며, 전송 :

    // input thread 
    if input changed, 
        alter paddle speed and/or direction 
        send timestamped message to inform my opponent of paddle change 
    
    // incoming network thread 
    if paddle packet received 
        alter opponent's paddle speed and/or direction at time it was sent 
        fix any errors in previously extrapolated paddle position <--- Easy 
    if ball-packet received 
        fix any errors in ball position and speed <--- Tricky 
    
    // update entities thread 
    for each entity (my paddle, opponent paddle, the ball) 
        compute updated entity position, adjusted by time-since-last-update 
        if ball reached my end, send ball-packet to other side 
        draw updated entity 
    

    이 두 가지 패키지 유형이 교환되고 있다고 가정 의사 코드는 다음과 유사한 것 자신의 외륜의.

  • 볼 패킷은 타임 스탬프가 지정된 위치와 볼의 속도이며 볼이 클라이언트의 (로컬)쪽에 도달 할 때마다 보내집니다.

의사 코드는 update-entities 스레드의 모든 알 수없는 항목에 대해 외삽을 수행합니다 ("정상적으로 계속 움직이는 것으로 가정합니다"). 문제가 발생하는 유일한 지점은 <--- 화살표입니다.

패들 위치를 새로운 위치로 변형하여 손쉽게 수정할 수 있습니다. 짧은 시간에 움직임을 보간하면 덜 거슬리게 움직일 수 있습니다.

두 클라이언트가 동의하면 볼 위치를 쉽게 보정 할 수 있습니다. 그런 다음 보간 트릭을 다시 수행하여 더 부드럽게 만들 수 있습니다. 그러나 한 클라이언트는 니어 미스와 가까운 히트를 볼 수 있습니다. 이 경우 피어 - 투 - 피어 모델을 사용하기 때문에 로컬 클라이언트가 전화를 걸고 상대방에게 일어난 일을 설명합니다 (다른 설계에서는 중앙 서버에서 이러한 종류의 결정을 내릴 수 있습니다. 이것은 부정 행위를 피하는 것이 좋습니다). 양쪽 클라이언트가 동의하지 않으면 추한 점프를 피할 수는 없지만, 핑 스파이크와 일치하지 않는 한 비교적 드물고 짧아야합니다.

+0

안녕하세요, 답변 해 주셔서 감사합니다. 그건 내 이전 질문이 아니야. 두 클라이언트가 동의하지 않으면 어떻게 이러한 점프를 피할 수 있습니까? 그게 내 질문이다 – Z0q

+0

그리고 내 대답은 당신이 너무 자주 정보의 잘못된 유형을 교환하고, 자주 피할 수없는 불일치로 이어지는 것입니다. 아직 알지 못하는 클라이언트에게만 정보를 교환하고 들어오는 정보를 신뢰해야합니다. 클라이언트 B는 네트워크 지연으로 인해 클라이언트 A가 최근 수행 한 패들의 움직임을 알 수 없습니다. 그러므로 클라이언트 B는 클라이언트 A의 움직임에 관해 클라이언트 A에게 알리지 않아야합니다. – tucuxi

2

이 효과를 제거 할 수있는 아이디어 중 하나는 클라이언트에서 오류 예측 수정을 적용 할 때 평활화를 사용하는 것입니다..그 코드는 그 볼 위치와 클라이언트를 식별의 어느 시점에서

작동 방법

는 다르다. 대신 바로 클라이언트 코드 (당신이 그 점프를 볼 수있는 이유 중 하나 인)에 대한 보정으로 것을 적용

Discrepancy

, 당신은 그 시간 동안, cl_smoothtime 예를 들어, 수행 500ms.

처음에는 프로그램에서 오류 감지 이벤트가 발생한 시간을 m_flPredictionErrorTime으로 저장해야합니다.

public void onErrorDetected() { 
    this.m_flPredictionErrorTime = System.currentTimeMillis(); 
} 

어딘가에 가까운 디스플레이 코드에 당신은 당신이 표시하려고 얼마나 많은 오류 계산합니다. 여기에 대한 몇 가지 의사 코드가 있습니다.

public void draw() { 
    Point preditctionError = this.clientPredictedBallCoordinates - this.serverCoordinates; 
    Point deltaToDisplay = calculateErrorVector(preditctionError); 
    Point positionToDisplay = clientPredictedBallCoordinates + deltaToDisplay; 
    // actually draw the ball here 
} 

public Point calculateErrorVector(Point coordinatesDelta) { 
    double errorAmount = (System.currentTimeMillis() - this.m_flPredictionErrorTime)/this.cl_smoothtime. 
    if (errorAmount > 1.0) { 
     // whole difference applied in full, so returning zero delta 
     return new Point(0,0); 
    } 
    if (errorAmount < 0) { 
     // no errors detected yet so return zero delta 
     return new Point(0,0); 
    } 
    Point delta = new Point(coordinates.x*errorAmount, coordinates.y*errorAmount); 
    return delta; 
} 

나는 Source Multiplayer Networking wiki에서이 아이디어를 포착했습니다. Cpp의 실제 코드 예제는 SDK에서 GetPredictionErrorSmoothingVector function 주위에 있습니다.