게임에서 클라이언트 / 서버 네트워크 아키텍처에 대해 배우기 위해 멀티 플레이어 소행성 클론을 구현하고 있습니다. 클라이언트 / 서버 기술에 관한 GafferOnGames 및 Valve의 출판물을 읽는 데 시간을 보냈습니다. 두 가지 개념에 문제가 있습니다.
-
현재 box2d로 물리를 시뮬레이션하고 초당 약 20 회 세계 상태를 클라이언트에게 보내는 신뢰할 수있는 게임 서버가 있습니다. 각 클라이언트는 수신 한 마지막 몇 개의 스냅 샷을 추적하고 스프라이트의 움직임을 매끄럽게하기 위해 두 상태 사이에서 잠복합니다. 그러나 그렇게 매끄럽지는 않습니다. 그것은 잠시 동안 매끄럽게 될 수 있습니다. 그런 다음 약간 거꾸로 한 다음 다시 매끄럽게 할 수 있습니다. TCP와 UDP를 모두 시도했지만 모두 동일합니다. 내 문제가 무엇인지 알 수 있습니까? (참고 : 나는 이것을 싱글 플레이어 용으로 먼저 구현했으며, 물리 세계를 초당 20 회만 업데이트 할 때 스프라이트 움직임이 60fps에서 완벽하게 매끄 럽습니다).
-
첫 번째 문제를 해결하기 위해 클라이언트가 box2d 시뮬레이션을 실행하고 서버 스냅 샷이 일치하지 않을 때 스프라이트 위치를 업데이트해야한다고 생각했습니다. 싱글 플레이어 구현이 매끄 럽기 때문에 이것이 더 부드러울 수 있다고 생각했습니다. 이것이 좋은 생각입니까?
위의 문제를 해결하지 않더라도 클라이언트 측 예측에 필요합니까? 예를 들어, 플레이어가 우주선을 움직이려고하면 물리 시뮬레이션없이 소행성, 벽 또는 적의 함선에 충돌했는지 어떻게 알 수 있습니까? 우주선이 서버에서 개체를 쳤다는 스냅 샷을 받기 전에 충돌해야하는 개체를 통과하는 것처럼 보입니다.
감사!
답변
클라이언트와 서버 모두에서 시뮬레이션을 확실히 실행하십시오. 다른 것들은 대기 시간이 너무 길다. 고정 된 시간 간격을 사용하고 포인터 비교를 피하면서 동일한 순서로 객체를 삽입하여 시뮬레이션이 일치하는지 확인해야합니다. Box2D로 이것을 시도하지는 않았지만 물리 시뮬레이션에서 모든 머신에서 동일한 동작을 달성하는 것이 일반적으로 가능합니다. 모든 수학은 일반적으로 IEEE 754 binary32 float를 기반으로하며 그 동작은 예를 들어 +-*/
몇 가지 등의 작업에 대해 엄격하게 정의됩니다 . 에 대해 조심해야합니다 sin
.cos
런타임마다 다를 수 있기 때문에 좋아합니다 (여러 플랫폼을 위해 개발할 때 특히 중요합니다). 또한 컴파일러에서 부동 최적화를 위해 엄격한 설정을 사용해야합니다. 서버에서 객체 상태를 주기적으로 보내 객체를 동기화 할 수 있습니다. 불필요한 말더듬을 피하기 위해 차이가 임계 값보다 크지 않으면 업데이트하지 마십시오.
염두에 두어야 할 한 가지 문제는 새로운 객체를 생성하고 이것이 클라이언트 간의 시뮬레이션을 어떻게 변화시킬 것인가입니다. 이를 해결하는 한 가지 방법은 서버가 모든 오브젝트를 작성하게하는 것입니다. 현재 시간 단계가 t
이면 서버는에 추가 될 개체를 예약합니다 t+d
. 따라서 추가 할 개체와 추가시기를 포함하는 새 개체 목록을 모든 클라이언트에서 유지 관리하고 서버에서 미리 업데이트 할 수 있습니다. d
충분히 큰 경우 다른 결과의 위험을 최소화합니다. 실제로 차이를 처리 할 수없는 경우 해당 시간 단계를 시뮬레이션하기 전에 클라이언트가 특정 시간 단계 동안 새 객체에 대한 정보를 기다리도록 할 수 있습니다.
답변
그것들 사이의 보간은 항상 보간 할 다음 데이터 세트에 의존하기 때문에 그렇게 좋아 보이지 않을 것입니다. 즉, 지연 시간이 짧으면 모든 것이 따라 오기를 기다려야합니다.
있다 GameDev에 오래된 기사 당신이 마지막으로 데이터를 한 지점 과거 개체의 위치를 예측하는 차 스플라인을 사용하는 방법에 대한이. 그런 다음 해당 위치를 사용하고 새 위치를 설명 할 새 데이터를 가져올 때 스플라인을 조정합니다. 또한 두 번째 물리 시뮬레이션을 실행하는 것보다 훨씬 저렴할 것이므로 클라이언트를 명시 적으로 구현하여 클라이언트를 구현했기 때문에 신뢰하는 사람을 결정할 필요가 없음을 의미합니다. 🙂
답변
비슷한 작업을 직접 수행했으며 클라이언트에서만 Box2D를 실행했습니다. 내가 한 방법은 클라이언트가 자체 시뮬레이션을 거의 자체적으로 실행하여 모든 동기화 패킷의 현재 속도 (및 회전)를 서버로 보내는 것입니다. 그런 다음 서버는이 정보를 다른 플레이어에게 전송합니다. 다른 플레이어는 새로받은 속도를 복제 된 엔티티로 설정합니다. 클라이언트간에 눈에 띄는 차이없이 매우 매끄 럽습니다.
물론 여기서 문제는 엔티티에 대한 중앙 집중식 제어가 없다는 것입니다. 그러나 서버 측에서 물리학을 시뮬레이션하여 서버 측에서도 수행 할 수 있다고 생각합니다.
답변
개인적으로 서버에서만 시뮬레이션을 실행하고 관련된 객체의 선형 / 각속도 / 가속에 대한 변경 사항을 브로드 캐스트하도록합니다. 어떤 이유에서든 특정 개체가 물리적 속성 (위에서 언급 한 속도 및 가속과 같은)을 변경하면이 특정 변경 사항이 서버에서 클라이언트로 전송되고 클라이언트가 그에 따라 객체의 데이터.
현재 구현에 비해 이점은 클라이언트 측 보간의 필요성을 무효화하고 객체에 대해 매우 충실한 동작을 생성한다는 것입니다. 문제는이 방법이 지연 시간에 매우 취약하다는 점입니다. 이는 플레이어가 지리적으로 너무 멀리 떨어져있을 때 매우 큰 문제가됩니다.
질문 1에 관해서는, 스냅 샷의 각 수신 사이에 정확히 20 초 간격이있을 것이라는 절대적인 보장이 없기 때문에 문제는 지연 시간에 변동이있을 것이라고 말합니다. 밀리 초 단위로 측정 된 시간을 “t”로 설명하겠습니다.
1) 게임이 시작된 이후 t = 20에서 클라이언트는 스냅 샷을 수신하고 성공적으로 보간을 보냈습니다.
2) t = 40에서 서버와 클라이언트 사이에 대기 시간이 있었고 스냅 샷은 실제로 t = 41에 도달했습니다.
3) t = 60에서 서버는 다른 스냅 샷을 보냈지 만 대기 시간으로 인해 시뮬레이션의 1 초가 클라이언트 측에서 낭비되었습니다. 스냅 샷이 t = 60에 도달하면 클라이언트는 40 및 60 인스턴트의 보간을 수행하지 않지만 실제로는 41-60 인스턴트에서 보간하여 다른 동작을 생성합니다. 이러한 부정확성으로 인해 최종적으로 “자 키감”이 발생할 수 있습니다.
질문 2의 경우, 객체의 위치를 알려주는 각 프레임에 패키지를 보내지 않고도 각 객체가 실제로 클라이언트-서버 동기화되었는지 여부를 효율적으로 추적하는 무언가를 구현하면 아이디어가 효과가 있습니다. 불연속 간격으로 수행하더라도 질문 1의 동일한 문제에서 실행될뿐만 아니라 너무 많은 양의 데이터를 전송할 수 없습니다 (나쁜 일).