네트워크로 연결된 실시간 게임을 위해 게임 상태 스냅 샷 시스템은 어떻게 구현됩니까?


12

네트워킹 클래스를위한 프로젝트로 간단한 클라이언트-서버 실시간 멀티 플레이어 게임을 만들고 싶습니다.

실시간 멀티 플레이어 네트워크 모델에 대해 많이 읽었으며 클라이언트와 서버와 지연 보상 기술 간의 관계를 이해합니다.

내가하고 싶은 것은 Quake 3 네트워크 모델과 비슷한 것입니다. 기본적으로 서버는 전체 게임 상태의 스냅 샷을 저장합니다. 클라이언트로부터 입력을 받으면 서버는 변경 사항을 반영하는 새 스냅 샷을 만듭니다. 그런 다음 새 스냅 샷과 마지막 스냅 샷의 차이점을 계산하여 클라이언트에 전송하여 동기화 할 수 있도록합니다.

클라이언트와 서버가 안정적으로 연결되어 있으면 동기화를 위해 필요한 최소한의 데이터 만 전송됩니다. 클라이언트가 동기화되지 않으면 전체 스냅 샷도 요청할 수 있습니다.

그러나 스냅 샷 시스템을 구현하는 좋은 방법을 찾을 수 없습니다. 싱글 플레이어 프로그래밍 아키텍처에서 벗어나기 힘들고 게임 상태를 다음과 같은 방식으로 저장하는 방법에 대해 생각합니다.

  • 모든 데이터는 논리와 분리
  • 게임 상태의 스냅 샷 사이의 차이를 계산할 수 있습니다
  • 코드를 통해 게임 엔터티를 쉽게 조작 할 수 있습니다

스냅 샷 클래스 는 어떻게 구현됩니까? 엔터티와 해당 데이터는 어떻게 저장됩니까? 모든 클라이언트 엔티티에 서버의 ID와 일치하는 ID가 있습니까?

스냅 샷 차이는 어떻게 계산됩니까?

일반적으로 게임 상태 스냅 샷 시스템은 어떻게 구현됩니까?


4
+1. 이것은 단일 질문에 비해 너무 광범위하지만 IMO는 답변에서 대략적으로 다룰 수있는 흥미로운 주제입니다.
Kromster

Snapshot 1 (실제 세계) 만 저장하고 들어오는 모든 변경 사항을이 표준 세계 상태에 저장 한 다음 목록 또는 다른 이름으로 변경 사항을 저장하십시오. 그런 다음 변경 내용을 모든 클라이언트에게 보내는 시간이되면 목록의 내용을 모든 클라이언트에게 보내고 목록을 지우면 0에서 시작합니다 (변경). 어쩌면 이것이 2 개의 스냅 샷을 저장하는 것만 큼 좋지는 않지만이 방법을 사용하면 2 개의 스냅 샷을 빠르게 확산시키는 방법에 대한 알고리즘에 대해 걱정할 필요가 없습니다.
tkausl

이것을 읽어 보셨습니까 : fabiensanglard.net/quake3/network.php-quake 3 네트워크 모델의 검토에는 구현에 대한 토론이 포함됩니다.
Steven

어떤 종류의 게임을 구성하려고합니까? 네트워크 설정은 제작중인 게임 유형에 따라 크게 다릅니다. RTS는 네트워킹 측면에서 FPS처럼 동작하지 않습니다.
AturSams

답변:


3

두 개의 스냅 샷 인스턴스 (현재 인스턴스와 마지막 동기화 인스턴스)를 유지하여 스냅 샷 델타 (이전 동기화 상태로 변경)를 계산할 수 있습니다.

클라이언트 입력이 도착하면 현재 스냅 샷을 수정합니다. 그런 다음 델타를 클라이언트에 보낼 때, 현재 하나의 필드별로 (재귀 적으로) 마지막으로 동기화 된 스냅 샷을 계산하고 델타를 계산하고 직렬화합니다. 직렬화의 경우 전역 상태 범위와 달리 클래스 범위의 각 필드에 고유 ID를 할당 할 수 있습니다. 클라이언트와 서버는 전역 상태에 대해 동일한 데이터 구조를 공유해야 클라이언트가 특정 ID가 적용되는 것을 이해할 수 있습니다.

그런 다음 델타가 계산 될 때 현재 상태를 복제하고 마지막으로 동기화 된 상태로 만들기 때문에 현재와 마지막으로 동기화 된 상태는 동일하지만 인스턴스가 다르므로 현재 상태를 수정하고 다른 상태에는 영향을 미치지 않습니다.

이 접근 방식은 특히 리플렉션을 사용하여 구현하는 것이 더 쉬울 수 있지만 (고급스러운 경우) 리플렉션 부분을 고도로 최적화하더라도 (대부분의 리플렉션 호출을 캐시하도록 데이터 스키마를 구축함으로써) 느릴 수 있습니다. 주로 잠재적으로 큰 상태의 두 복사본을 비교해야하기 때문입니다. 물론 비교와 언어를 구현하는 방법에 달려 있습니다. C ++에서는 하드 코딩 된 비교기를 사용하면 빠르지 만 유연하지는 않습니다. 전역 상태 구조를 변경하려면이 비교기를 수정해야하며 이러한 변경은 초기 프로젝트 단계에서 자주 발생합니다.

또 다른 방법은 더티 플래그를 사용하는 것입니다. 클라이언트 입력이 도착할 때마다 단일 글로벌 상태 사본에 적용하고 해당 필드를 더티로 플래그 지정합니다. 그런 다음 클라이언트를 동기화 할 때 동일한 고유 ID를 사용하여 더티 필드를 (재귀 적으로) 직렬화합니다. (사소한) 단점은 때로는 필요한 것보다 더 많은 데이터를 보내는 것 int field1입니다. 이점은 거대한 계층 적 데이터 구조를 가지므로 델타를 계산하기 위해 완전히 분석 할 필요가없고 더티 경로 만 계산한다는 것입니다.

일반적으로이 작업은 매우 복잡 할 수 있으며 최종 솔루션의 유연성에 달려 있습니다. 예를 들어 Unity3D 5 (다가오는)는 속성을 사용하여 클라이언트와 자동 동기화해야하는 데이터를 지정합니다 (매우 유연한 접근 방식, 필드에 속성을 추가하는 것 외에 다른 작업을 수행 할 필요가 없음). 빌드 후 단계. 자세한 내용은 여기를 참조하십시오.


2

먼저 프로토콜 준수 방식으로 관련 데이터를 나타내는 방법을 알아야합니다. 이것은 게임과 관련된 데이터에 따라 다릅니다. RTS 게임을 예로 사용하겠습니다.

네트워킹을 위해 게임의 모든 엔티티 (예 : 픽업, 유닛, 건물, 천연 자원, 파괴 가능)가 열거됩니다.

플레이어는 자신과 관련된 데이터를 가져야합니다 (예 : 모든 보이는 단위).

  • 그들은 살아 있거나 죽었습니까?
  • 그들은 어떤 유형입니까?
  • 그들은 얼마나 많은 건강을 남겼습니까?
  • 현재 위치, 회전, 속도 (속도 + 방향), 가까운 미래 경로 ...
  • 활동 : 공격, 걷기, 건물, 고정, 치유 등
  • 버프 / 디버프 상태 효과
  • 그리고 아마도 마나, 방패 등의 다른 능력치?

처음에는 플레이어가 게임에 입장하기 전에 전체 상태를 받아야합니다 (또는 플레이어와 관련된 모든 정보).

각 장치에는 정수 ID가 있습니다. 속성이 열거되므로 완전한 식별자도 있습니다. 단위 ID는 길이가 32 비트 일 필요는 없습니다 (절충하지 않아도 될 수 있음). 그것은 20 비트 일 수 있습니다 (속성을 위해 10 비트를 남겨 둡니다). 유닛의 ID는 고유해야하며, 유닛이 인스턴스화 및 / 또는 게임 세계에 추가 될 때 카운터에 의해 할당 될 수 있습니다 (건물 및 자원은 고정 유닛으로 간주되며 맵에서 자원은 ID로 할당 될 수 있음) 로드 됨).

서버는 현재 전역 상태를 저장합니다. 각 플레이어의 가장 최근에 업데이트 된 상태는 list최근 변경 사항 에 대한 포인터로 표시됩니다 (포인터 이후의 모든 변경 사항은 해당 플레이어로 아직 전송되지 않음). 변경 사항이 list발생할 때 추가됩니다 . 서버가 마지막 업데이트를 보낸 후에는 목록을 반복하기 시작할 수 있습니다. 서버는 플레이어의 포인터를 목록을 따라 꼬리로 옮기고 도중에 모든 변경 사항을 수집하여 보낼 버퍼에 배치합니다. 플레이어 (즉, 프로토콜의 형식은 다음과 같을 수 있습니다 : unit_id; attr_id; new_value) 새로운 유닛도 변경으로 간주되며 모든 속성 값과 함께 수신 플레이어에게 전송됩니다.

가비지 컬렉터와 함께 언어를 사용하지 않는 경우 지연되는 포인터를 설정 한 다음 목록에서 가장 오래된 플레이어 포인터를 따라 잡아 객체를 해제해야합니다. 우선 순위 힙에서 가장 오래된 플레이어를 기억하거나 게으른 포인터가 같을 때까지 (즉, 플레이어 포인터 중 하나와 동일한 항목을 가리킬 때) 반복하고 해제 할 수 있습니다.

제기하지 않은 몇 가지 질문은 흥미 롭습니다.

  1. 클라이언트가 모든 데이터를 담은 스냅 샷을 먼저 받아야합니까? 시선 이외의 품목은 어떻습니까? RTS 게임에서 전쟁의 안개는 어떻습니까? 모든 데이터를 보내면 클라이언트가 해킹되어 다른 보안 조치에 따라 플레이어가 사용할 수없는 데이터를 표시 할 수 있습니다. 관련 데이터 만 보내면 문제가 해결 된 것입니다.
  2. 모든 정보를 보내는 대신 변경 사항을 보내야하는 경우는 언제입니까? 최신 머신에서 사용할 수있는 대역폭을 고려할 때 모든 정보를 보내는 대신 "델타"를 보내면 어떤 이점이 있습니까?
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.