커맨드

목차

커맨드 설명=

커맨드는 서버 권한으로 클라이언트 예측을 지원하기 위한 볼트 내의 완전히 선택적인 구성입니다. 이 기능을 지원하지 않으려면 게임에서 커맨드를 사용할 필요가 없습니다. 실제로 Bolt에서 가장 쉬운 구현 - 완전한 클라이언트 권한 - 커맨드는 필요하지 않으므로 사용할 필요가 없습니다. 또한 NPC와 같이 완전히 서버 지향적인 것은 클라이언트 예측이 아니기 때문에 명령을 사용할 필요가 없습니다. 볼트 상태를 통한 간단한 변환 동기화를 사용해야 합니다.

커맨드 사용은 게임 오버플로에 추가적인 복잡성을 가중시키므로 광범위하게 사용하기 전에 충분히 이해해야 합니다. 고급 튜토리얼은 명령어와 함께 간단한 캐릭터 모터를 구현하므로 이 모터로 시작하는 것이 좋습니다. 커맨드를 먼저 이해하지 않고 자신의 모터를 사용하는 것은 권장되지 않습니다.

메인 화면으로

예측

로컬 엔티티(일반적으로 플레이어)는 항상 예측됩니다. 로컬 엔티티는 커맨드 시스템을 사용하여 로컬 컴퓨터로 즉시 이동합니다. 빠른 응답은 플레이어에게 매우 중요하기 때문에 명령 시스템을 사용하여 로컬 컴퓨터로 즉시 이동합니다. 플레이어의 입력은 커맨드의 일부로 서버로 전송됩니다. 서버는 이러한 입력도 플레이하여 클라이언트가 이미 예측한 것과 동일한 시뮬레이션을 수행합니다. 그런 다음 서버는 특정 프레임에 대한 결과(최종 위치, 속도 등)를 플레이어에게 반환합니다. 그런 다음 해당 프레임에 있는 서버에 자신의 위치 및 기타 상태를 다시 설정합니다(커맨드 결과의 "보정"). 그리고 그 시점부터 현재까지의 입력 내용을 다시 반복해서 자신이 예측한 곳으로 돌아오길 바라요.

메인 화면으로

권한부여

클라이언트의 예측된 동작에 따라, 서버는 우리가 주 권한이라고 부르는 것을 수행합니다. 서버의 시뮬레이션이 다르면 플레이어는 다른 위치(예: 다른 위치)에 있게 됩니다. 시뮬레이션은 서버에서 권한 있게 실행되며 플레이어는 시뮬레이션이 어떻게 될지 예측하려고 합니다. 플레이어의 속도를 매우 빠른 속도로 설정하면 클라이언트 시뮬레이션이 서버와 전혀 관련이 없기 때문에 서버는 상관하지 않습니다.

플레이어의 프록시(즉, 기타 플레이어 박스에 플레이어를 나타내는 것)는 일반적으로 동기화된 변환과 함께 이동하기 위해 Bolt 상태 시스템을 사용합니다. 제어 플레이어는 자신의 이동을 예측하므로, 서버에서 자신의 플레이어 개체가 다시 동기화되는 것을 원하지 않기 때문에 플레이어의 Transform 속성 컨트롤러를 제외한 모든 사용자로 설정할 수 있습니다. 만약 모든 사람으로 설정된다면, 플레이어는 자신의 움직임을 예측하겠지만, 자신의 플레이어에 대한 예측되지 않은 상태 동기화도 서버에서 돌아오게 될 것이고, 이것은 본질적으로 그의 예측된 가치와 충돌할 것이고, 당신은 명백한 유물을 보게 될 것입니다.

한 가지 제안으로, 네트워크에서 클라이언트 예측 서버 권한 이동 모터를 테스트하는 경우 지연 시간 시뮬레이션을 활성화하고 Bolt에 제공되는 기본값과 같이 합리적인 것으로 설정해야 합니다. 그 이유는 지연 시간이 클수록 구현 시 오류가 더 뚜렷하고 분명하기 때문입니다. 시뮬레이션이 없고 서버가 로컬인 경우 효과가 좋다고 생각할 수 있습니다. 지연 시간 sim을 켜면 문제가 있는 위치를 매우 빠르게 확인할 수 있습니다.

명령은 기본적으로 컨트롤러에서 서버로 송수신되는 전송 속도로 전송되는 네트워크 스트림입니다(서버에 대한 입력, 결과 반환). 이는 클라이언트 예측에 명령을 사용하지 않더라도 유용한 구성이 될 수 있습니다.

메인 화면으로

커맨드 정의

커맨드를 생성하거나 수정하려면 Bolt Assets 창으로 이동한 다음 마우스 오른쪽 버튼을 클릭하여 New Command를 만들거나 목록에서 하나를 선택하여 해당 정의를 편집하십시오. Bolt Editor 창에서 클라이언트로부터 입력 명령을 보내고 서버에서 결과 값을 반환하는 데 사용할 필드인 InputsResults를 포함할 수 있습니다.

커맨드 정의
커맨드 정의.

커맨드에는 동작을 변경할 수 있는 몇 가지 설정도 있습니다.

  • Is Instant - 이 명령의 모든 대기열에 있는 입력은 서버에 접속하는 즉시 다음 프레임에서 실행됩니다.
  • 프레임 제한 활성화 - SimulateController()당 하나의 입력만 대기열에 넣습니다. 이렇게 하면 스피드 해킹을 방지할 수 있습니다.
  • 수정 보간 - 서버에서 수신한 결과의 보간입니다.
  • 델타 압축 사용 - 네트워크 트래픽 및 처리 시간 약간 증가된 가격에 대한 할당을 줄입니다. 항상 결과를 측정하십시오! 절대 델타 압축이 되지 않으므로, 입력/결과에만 사용하지 마십시오.

메인 화면으로

커맨드 사용하기

커맨드를 사용하는 Bolt API는 상대적으로 작지만, 커맨드를 활용하기 위해서는 움직이는 모든 부분을 이해해야 합니다. 새 Bolt Entity를 생성할 때 상태를 정의하고 Bolt.EntityEventListener<[여기에 상태]]>를 확장하는 컴포넌트 클래스를 연결합니다. 다음 세 가지 주요 방법에 액세스할 수 있습니다.

  • public override void SimulateController() : 게임에서 입력 정보를 수집하여 Command에 넣는 데 사용합니다. SimulateController는 프레임당 한 번 실행됩니다.
  • public override void ExecuteCommand() : 엔티티의 소유자와 컨트롤러에서 실행되므로 서버가 생성한 플레이어 캐릭터가 클라이언트에게 있으면 Execute Command가 서버와 제어 클라이언트 모두에서 실행됩니다. 다른 클라이언트에서 실행되지 않습니다.
  • entity.QueueInput(cmd) : 클라이언트에서 로컬 실행 인수로 전달된 명령과 원격 실행을 위해 서버로 전송되도록 예약하기 위해 SimulateController() 함수 내부에서 이 메서드를 호출합니다. 이것이 Bolt가 클라이언트 측 예측을 하도록 하는 것입니다. 명령은 서버와 클라이언트에서 모두 실행됩니다.

서버가 명령을 실행하면 명령어의 결과를 명령을 생성한 클라이언트에 다시 전송하고, 클라이언트에서 해당 명령의 상태를 올바른 상태로 재정의합니다.

resetState 파라미터는 resetState가 true일 때 전달된 명령의 Result로 캐릭터 모터의 상태를 재설정하도록 요청합니다. 이 문제는 원격 제어 클라이언트에서만 발생하며 서버에서는 발생하지 않습니다. 이 작업은 모든 프레임의 시작 부분에 한 번 발생하며 전달된 명령은 서버로부터 올바른 결과를 받은 명령입니다.

resetState 명령을 실행한 후, 볼트는 재설정 프레임에서 현재 프레임으로 "캐치 업"하기 위해 클라이언트에서 다른 모든 명령을 다시 실행합니다. 이는 모든 프레임에서 발생합니다(이것이 시뮬레이션 속도입니다).

일반적인 질문입니다. 클라이언트에서 상태 로직을 재설정하면 플레이어가 빠르게 움직이는 이유는 무엇입니까?
그 이유는 모든 체크 표시마다 Bolt가 사용자가 특정 프레임에 있던 위치로 되돌린 다음(리셋 상태) 해당 프레임에서 현재 프레임으로 대기 중인 모든 명령을 재생하기 때문입니다. 대부분의 시나리오에서는 새 체크 표시의 추가 입력을 사용하여 체크 표시 이전과 동일한 위치로 돌아갑니다. 플레이된 입력은 일반적으로 동일한 네트워크의 서버에서 재생되는 경우에도 10개 이상의 명령입니다. 재설정 상태 로직을 설명하면, Bolt는 먼저 위치를 재설정하지 않고 (앞으로 누르는 경우 전진 입력으로) 10개 이상의 명령을 실행합니다. 따라서 체크당 "앞으로 이동"을 10회 이상 실행할 수 있습니다! 그래서 이렇게 빨리 움직이는 거예요. 이것은 완전히 클라이언트 쪽이고 서버는 이러한 빠른 이동을 반영하지 않습니다. 기본적으로 서버를 완전히 무시하는 것입니다.

메인 화면으로

커맨드와 같이 Input을 큐에 넣기

Bolt 사용자의 일반적인 질문은 입력을 올바르게 대기열에 넣는 방법입니다. 대부분의 사용자는 자신의 원샷 입력이 연속으로 여러 번 수행되거나 경우에 따라서는 반대 현상이 발생하며 입력 내용이 간단히 누락된다는 것을 알게 됩니다. 그 이유는 간단하지만 유니티의 Update / FixedUpdate의 작동 방식에 대한 지식이 필요합니다. 유니티는 Update 시작 시 입력을 수집하고 Bolt의 입력 큐는 FixedUpdate에서 발생합니다.

업데이트는 프레임당 한 번 발생합니다. FixedUpdate는 일정한 간격으로 실행됩니다. 프레임률이 높으면 각 FixedUpdate 간에 여러 Update가 실행됩니다. 프레임률이 낮은 경우 Unity는 물리 눈금 동기화를 유지하기 위해 단일 프레임에서 여러 개의 FixedUpdate를 실행합니다.

빈 테스트 씬에서 편집기에서 실행 중인 시나리오를 상상해 보십시오. 프레임률이 매우 높습니다. 이 경우 시뮬레이션 속도가 60(초당 물리 눈금 60개)이고 프레임률이 180이라고 가정합니다. 즉, 각 틱 사이에 세 번의 업데이트가 발생한다는 의미입니다.

따라서 이 시나리오에서는 한 번의 고정 업데이트당 세 번의 업데이트가 있습니다.

Update      - collect input for jump == false
Update      - collect input for jump == true (you pressed the jump button)
Update      - collect input for jump == false
FixedUpdate - queue input (false)

이 예에서는 SimulateController에서 대기열에 넣을 수 있도록 입력을 추적하는 데이터 구조를 가지고 있습니다. 두 번째 업데이트에서 점프 버튼을 클릭합니다. 그러나 데이터 구조가 게임을 대기하기 전에 세 번째 업데이트에서 점프를 다시 거짓으로 재설정하기 때문에 실제로 게임에 뛰어들지는 않습니다. 솔루션은 간단합니다. 점프 플래그를 true로만 설정하고, 입력을 폴링하는 동안에는 절대 false로 재설정하지 마십시오. 대신 입력 대기열이 끝나면 모든 원샷 입력을 재설정합니다.

물론 프레임률이 낮은 상황에서는 그 반대 현상이 발생할 수 있습니다.

Update        - input polled
FixedUpdate   - queue input
FixedUpdate   - queue same input (again)
FixedUpdate   - queue same input (again)

이 경우 볼트의 SimulateController 이후 원샷 입력을 지우지 않으면 동일한 온샷 입력을 3회 연속 대기열에 넣습니다.

기술문서 TOP으로 돌아가기