나는 약 20-30 명의 플레이어가 영구 서버에 한 번에 연결된 중간 규모의 멀티 플레이어로 아이소 메트릭 2D 게임을하고 있습니다. 올바른 움직임 예측 구현을 얻는 데 어려움이있었습니다.
물리 / 이동
게임에는 실제 물리 구현이 없지만 기본 원리를 사용하여 움직임을 구현합니다. 지속적으로 입력을 폴링하는 대신 상태 변경 (즉, 마우스 다운 / 업 / 이동 이벤트)은 플레이어가 제어하는 캐릭터 엔티티의 상태를 변경하는 데 사용됩니다. 플레이어의 방향 (즉, 북동쪽)은 일정한 속도와 결합되어 실체의 속도 인 실제 3D 벡터로 바뀝니다.
메인 게임 루프에서 "Draw"전에 "Update"가 호출됩니다. 업데이트 논리는 0이 아닌 속도로 모든 엔티티를 추적하는 "물리적 업데이트 작업"을 트리거합니다. 기본 통합을 사용하여 엔티티 위치를 변경합니다. 예를 들면 다음과 같습니다. entity.Position + = entity.Velocity.Scale (ElapsedTime.Seconds) (여기서 "Seconds"는 부동 소수점 값이지만 동일한 접근 방식은 밀리 초 정수 값에도 적용됩니다).
요점은 보간법이 움직임에 사용되지 않는다는 것입니다. 기본 물리 엔진에는 "이전 상태"또는 "현재 상태"라는 개념이 없으며 위치와 속도 만 있습니다.
상태 변경 및 업데이트 패킷
플레이어가 변경을 제어하는 캐릭터 엔티티의 속도가 변경되면, "아바타 이동"패킷이 엔티티의 동작 유형 (스탠드, 걷기, 달리기), 방향 (북동쪽) 및 현재 위치를 포함하는 서버로 전송됩니다. 이는 3D 1 인칭 게임 작동 방식과 다릅니다. 3D 게임에서는 플레이어가 움직일 때 속도 (방향)가 프레임마다 변경 될 수 있습니다. 모든 상태 변경을 전송하면 프레임 당 패킷을 효과적으로 전송하게되므로 너무 비쌉니다. 대신, 3D 게임은 상태 변경을 무시하고 고정 된 간격 (예 : 80-150ms마다)으로 "상태 업데이트"패킷을 보내는 것으로 보입니다.
게임에서 속도 및 방향 업데이트가 훨씬 덜 자주 발생하므로 모든 상태 변경을 전송하지 않아도됩니다. 모든 물리 시뮬레이션은 동일한 속도로 발생하고 결정 론적이지만 대기 시간은 여전히 문제입니다. 이런 이유로, 나는 3D 게임과 유사한 일상적인 위치 업데이트 패킷을 보내지 만 지금은 250ms마다 훨씬 덜 자주 있지만, 좋은 예측으로 쉽게 500ms로 높일 수 있다고 생각합니다. 가장 큰 문제는 내가 표준에서 벗어났다는 것입니다. 다른 모든 문서, 안내서 및 샘플은 온라인으로 일상적인 업데이트를 보내고 두 상태 사이에서 보간합니다. 내 아키텍처와 호환되지 않는 것 같습니다. (매우 기본적인) "네트워크 물리"아키텍처에 더 가까운 더 나은 움직임 예측 알고리즘을 생각해 내야합니다.
그런 다음 서버는 패킷을 수신하고 스크립트를 기반으로 이동 유형에서 플레이어 속도를 결정합니다 (플레이어가 실행할 수 있습니까? 플레이어의 실행 속도를 얻습니다). 속도가 설정되면 속도를 방향과 결합하여 개체의 속도 인 벡터를 얻습니다. 일부 치트 감지 및 기본 유효성 검사가 발생하며 서버 측의 엔티티는 현재 속도, 방향 및 위치로 업데이트됩니다. 플레이어가 이동 요청으로 서버를 넘치지 않도록 기본 조절도 수행됩니다.
자체 엔티티를 업데이트 한 후 서버는 "아바타 위치 업데이트"패킷을 범위 내의 다른 모든 플레이어에게 브로드 캐스트합니다. 위치 업데이트 패킷은 원격 클라이언트의 클라이언트 측 물리 시뮬레이션 (세계 상태)을 업데이트하고 예측 및 지연 보상을 수행하는 데 사용됩니다.
예측 및 지연 보상
위에서 언급 한 바와 같이, 고객은 자신의 입장에 대해 권위가 있습니다. 부정 행위 나 이상이있는 경우를 제외하고는 서버에서 클라이언트의 아바타 위치를 변경하지 않습니다. 클라이언트의 아바타에는 외삽 ( "지금 이동하고 나중에 수정")이 필요하지 않습니다 . 플레이어가 보는 것은 정확합니다. 그러나 움직이는 모든 원격 엔터티에는 일종의 외삽 또는 보간이 필요합니다. 클라이언트의 로컬 시뮬레이션 / 물리 엔진 내에서 어떤 종류의 예측 및 / 또는 지연 보상이 명확하게 요구됩니다.
문제
나는 다양한 알고리즘으로 고생하고 있으며 많은 질문과 문제가 있습니다.
외삽, 보간 또는 둘 다해야합니까? 내 "장감"은 속도에 기초한 순수한 외삽 법을 사용해야한다는 것입니다. 상태 변화는 클라이언트에 의해 수신되고, 클라이언트는 지연을 보상하는 "예측 된"속도를 계산하고, 일반 물리 시스템은 나머지를 수행합니다. 그러나 다른 모든 샘플 코드 및 기사와는 상충됩니다. 모두 여러 상태를 저장하고 물리 엔진없이 보간을 수행하는 것으로 보입니다.
패킷이 도착하면 고정 시간 (예 : 200ms) 동안 패킷의 속도로 패킷의 위치를 보간하려고 시도했습니다. 그런 다음 보간 위치와 현재 "오류"위치의 차이를 사용하여 새 벡터를 계산하고 전송 된 속도 대신 엔티티에 배치합니다. 그러나 다른 패킷이 해당 시간 간격으로 도착한다는 가정하에 다음 패킷이 도착할 때 "추측"하는 것은 매우 어렵습니다. 특히 모든 패킷이 고정 된 간격 (예 : 상태 변경)에 도달하지 않기 때문입니다. 개념에 근본적인 결함이 있습니까, 아니면 맞지만 수정 / 조정이 필요한가요?
원격 플레이어가 멈 추면 어떻게됩니까? 엔티티를 즉시 중지 할 수 있지만 다시 이동할 때까지 "잘못된"지점에 배치됩니다. 벡터를 추정하거나 보간하려고하면 이전 상태를 저장하지 않기 때문에 문제가 발생합니다. 물리 엔진은 "위치 X에 도달 한 후에 중지 할 필요가 없습니다"라고 말할 방법이 없습니다. 그것은 단지 속도를 이해하고 더 복잡하지 않습니다. "패킷 이동 상태"정보를 엔티티 또는 물리 엔진에 추가하는 것을 꺼려합니다. 기본 디자인 원칙을 위반하고 나머지 게임 엔진에서 네트워크 코드를 피하기 때문입니다.
엔티티가 충돌하면 어떻게됩니까? 세 가지 시나리오가 있습니다. 제어 플레이어가 로컬에서 충돌하거나, 위치 업데이트 중에 두 개체가 서버에서 충돌하거나, 원격 개체 업데이트가 로컬 클라이언트에서 충돌합니다. 모든 경우에있어 부정 행위를 제외하고는 충돌을 처리하는 방법을 잘 모르겠습니다. 두 상태 모두 "올바른"이지만 다른 시간대에 있습니다. 원격 엔터티의 경우 벽을 통과하는 것이 의미가 없으므로 로컬 클라이언트에서 충돌 감지를 수행하여 "중지"시킵니다. 위의 2 번 지점을 기준으로, "벽을 통해"엔티티를 지속적으로 이동 시키려고 시도하는 "보정 된 벡터"를 계산할 수 있습니다. 성공하지 않을 것입니다. 오류가 너무 높아져서 "스냅"될 때까지 원격 아바타가 고정됩니다. 위치. 게임은 어떻게이 문제를 해결합니까?