This document is about: FUSION 1
SWITCH TO

Fusion 소개

개요

Fusion은 유니티를 위한 새로운 고성능 상태 동기화 네트워킹 라이브러리입니다. 단일 API를 통해, 네트워크 연결이 없는 단일 플레이어 모드 뿐만 아니라 서로 다른 두 개의 네트워크 토폴로지를 지원합니다.

Fusion은 공통 유니티 작업 흐름에 자연스럽게 통합되는 동시에 데이터 압축, 클라이언트 측 예측 및 지연 보상과 같은 고급 기능을 즉시 사용할 수 있도록 단순성을 염두에 두고 구축되었습니다.

예를 들어, RPC 및 네트워크 상태는 명시적인 직렬화 코드가 필요 없는 MonoBehaviour의 메소드 및 속성에 대한 특성으로 정의되며, 네트워크 객체는 중첩 및 변형과 같은 유니티의 최신 프리팹 기능을 모두 사용하여 프리팹으로 정의할 수 있습니다.

Fusion은 최신 압축 알고리즘을 사용하여 최소 CPU 오버헤드로 필요 대역폭을 줄입니다. 데이터는 완전한 압축 스냅샷(호스트 모드에만 해당) 또는 부분 청크로 전송되며 최종적으로 일관성이 유지됩니다. 후자의 경우, 매우 많은 플레이어 수를 지원할 수 있도록 완전히 구성 가능한 관심 지역 시스템이 제공됩니다.

Fusion은 강력한 틱 기반 시뮬레이션을 구현하며 공유 모드 또는 호스트 모드에서 작동합니다. 주요 차이점은 네트워크 객체에 대한 권한(변경 기능)을 누가 가지고 있느냐에 있지만, 결국에 이것은 차례로 사용 가능한 다른 SDK 기능을 나타냅니다.

호스트 모드 / 서버 모드

호스트 모드에서 헤드리스 전용 서버로 실행하든, 동일한 장치에서 클라이언트와 서버를 같이 실행하든 서버는 예외 없이 모든 객체에 대해 완전하고 배타적인 상태 권한을 갖고있습니다.

클라이언트는 입력을 서버로 보내거나 RPC를 사용하여 변경 요청을 통해서만 네트워크 객체를 수정할 수 있습니다.

클라이언트가 네트워크 상태에 대해 직접 변경한 내용은 로컬 예측에 불과하며, 변경 내용을 수신할 때 서버에서 실제 권한 있는 스냅샷으로 재정의됩니다. 클라이언트가 서버 제공 상태로 롤백되고 로컬(예측된) 틱으로 다시 시뮬레이션되므로 이를 조정이라고 합니다.

이전 예측이 정확했다면 이 프로세스는 원활합니다. 그렇지 않으면 상태가 업데이트되고 네트워크 상태가 렌더링 상태와 별개이므로 렌더링이 이 새로운 상태로 스냅되거나 다양한 형태의 보간, 오류 수정 및 평활을 사용하여 보정으로 인한 인위적인 시각적 차이를 줄일 수 있습니다.

호스트 모드에서 Fusion은 각 클라이언트가 과거에 다른 클라이언트를 볼 수 있고 서버에 일반적인 레이 캐스트를 플레이어가 보는 것과 일치하지 않는다는 사실을 고려하여 지연 보정 히트 박스를 지원합니다. 지연 보상은 서버가 주어진 입력을 받았을 때 플레이어의 관점에서 세계을 볼 수 있게 해줍니다.

방화벽이나 라우터가 있는 호스트 모드를 실행할 때 Photon 클라우드는 필요에 따라 UDP 펀치스루 또는 패키지 릴레이를 투명하게 제공하지만 세션은 호스트가 소유하고 있으므로 호스트의 연결이 끊어지면 손실됩니다. Fusion은 현재 호스트의 연결이 끊어진 경우 네트워크 권한을 새 클라이언트로 전송할 수 있는 호스트 마이그레이션 메커니즘을 제공합니다. 공유 모드와 달리 클라이언트 코드에서 특별한 처리가 필요합니다.

공유 모드

공유 모드에서는 네트워크 객체에 대한 권한이 모든 클라이언트에 분산됩니다. 구체적으로 각 클라이언트는 처음에는 자신이 생성하는 객체에 대한 상태 권한을 갖고 있지만 다른 클라이언트에 해당 상태 권한을 자유롭게 해제할 수 있습니다. 선택적으로, 클라이언트는 마음대로 상태 권한 갖게 허용될 수 있습니다.

공유 모드에서는 데이터 전송 모드가 항상 최종 일관성이며 지연 보상, 예측 및 롤백과 같은 기능을 사용할 수 없습니다. 시뮬레이션은 모든 클라이언트에서 항상 동일한 틱 속도로 진행되지만 틱이 클라이언트 간에 정렬되지는 않습니다.

공유 모드 네트워크 세션은 Photon 클라우드에 의해 소유되며 클라이언트가 연결되어 있는 한 활성 상태로 유지됩니다. Photon 클라우드는 패키지 릴레이 역할을 하며 유니티를 실행할 필요 없이 네트워크 상태에 완전히 접근할 수 있으므로 전용 서버 하드웨어를 스핀업하지 않고도 경량 서버 로직 및 데이터 검증(예: 치트 보호)을 구현할 수 있습니다.

공유 모드는 여러 면에서 PUN과 유사하지만 기능이 완전하고 빠르며 런타임 할당 오버헤드가 없습니다.

PUN, Bolt 및 Fusion 비교

Fusion은 유니티(Bolt 및 PUN)용으로 기존의 두 Photon 상태 전송 제품을 발전시키고 대체하기 위해 개발되었습니다. Fusion에는 지원되는 모든 아키텍처 등을 포함합니다!

PUN과 Bolt는 견고한 네트워킹 솔루션이지만, 아키텍처는 추가적인 최적화를 허용하지 않습니다. Fusion은 PUN과 Bolt에 있는 최고의 개념을 결합하는 동시에 고성능 아키텍처를 통해 바로 최신 기능을 사용할 수 있습니다. 다음 이미지에는 향상된 기능이 요약되어 있습니다.

Table - PUN vs Bolt vs Fusion
PUN vs Bolt vs Fusion

기본 사항

사용할 두 가지 주요 Fusion 컴포넌트는 NetworkRunnerNetworkObject입니다. NetworkRunner는 Fusion의 핵심으로 생각할 수 있습니다. 즉, 네트워킹과 시뮬레이션을 모두 관리하는 씬에 단일 러너가 있습니다. 이것은 서버와 클라이언트 모두에서 발생합니다.

NetworkObject를 일반 유니티 프리팹 또는 씬 객체에 추가하면 런타임에 네트워크 ID가 할당되고 동기화된 틱 기반 시뮬레이션의 일부가 됩니다. 또한 이 특정 객체를 항상 데이터 전송의 일부로 만드는 등의 몇 가지 옵션을 구성할 수도 있습니다(글로벌 개체).

게임 객체에서 상속되고 실제 네트워킹을 추가하는 두 가지 기본 동작은 SimulationBehaviourNetworkBehaviour입니다.

네트워크 속성(게임 상태를 구성)은 네트워크 동작(SimulationBehaviour의 특수한 하위 클래스)에 추가되며, 후자는 네트워크 속성을 전송하지 않고 시뮬레이션 단계와 콜백을 제어하는 데 사용될 수 있습니다.

네트워크 동작은 네트워크를 통해 자동으로 동기화되는 데이터를 저장하는 데 사용해야 합니다. 네트워크 상태 값을 정의하려면 속성을 만들고 다음과 같이 [Networked]로 표시하면 됩니다:

C#

[Networked] public byte life { get; set; }

[Networked]는 모든 원시 타입에 대해 작동합니다(단일 비트로 올바르게 직렬화되므로 대신 NetworkBool을 사용해야 하는 bool 제외). 구조와 다른 NetworkObject(심지어 프리팹)에 대한 참조도 모든 클라이언트에서 안전하게 식별할 수 있기 때문에 작동합니다.

네트워크 속성 값이 변경될 때마다 트리거되는 콜백을 등록하는 것도 쉽습니다:

C#

[Networked(OnChanged = "OnTypeChanged")] public Type type { get; set; }

public static void OnTypeChanged(Changed<TheClassWhichHasTheProperty> changed)
{
  // your code here - check API docs for more details
}

실제 콜백 이름 외에도 콜백이 실행되는 위치를 제어할 수 있습니다:

  • OnChangedLocal (true/false) - 속성을 변경한 시스템에서 이벤트 후크가 호출되도록 true로 설정, 예 서버(기본 false)
  • OnChangedRemote (true/false) - 속성을 변경한 시스템에서만 이벤트 후크가 호출되도록 false를 설정(기본값 true)

사전 구축된 네트워크 Behaviour

Fusion은 게임이나 프로토타입을 빠르게 실행할 수 있도록 미리 구축된 다양한 NetworkBehaviour를 제공합니다.

NetworkTransform은 객체 변환을 동기화된 상태로 유지합니다(콜라이더도 포함할 수 있음). 렌더링은 최신의 스냅샷 보간을 통해 자동으로 매우 부드럽게 유지합니다. 유니티의 Transform에 대한 데이터를 직접 변경하여 (어떤 게임 클라이언트에서도) 완전한 클라이언트 측 예측을 수행하는 것도 간단합니다.

물리적으로 제어되는 리기드바디의 경우 NetworkRigidbody가 권장됩니다. 동일한 보간 옵션이 작동하며 Fusion은 구성 옵션을 설정하기만 하면 유니티 물리(PhysX)로 직접 전체 예측/롤백을 수행할 수 있습니다.

휴머노이드 캐릭터처럼 플레이어가 직접 조종하는 객체를 위해 Fusion에는 NetworkCharacterController가 내장돼 있습니다. 보다 복잡한 사용 사례의 경우 기본 정적 쿼리(표면 접선, 관통 보정 등)를 재사용하고 스티어링/이동에 대한 사용자 지정 코드와 일치시킬 수 있습니다.

틱 기반 콜백

게임 플레이 시뮬레이션 코드를 작성하기 위해서는 SimulationBehaviour의 라이프 사이클이 필요합니다. Awake()Start() 등과 같은 유니티 내장 코드를 계속 사용할 수 있지만, 네트워크에 안전한 제품을 사용하는 것이 좋습니다:

Fusion의 Start()에 해당하는 값을 Spawned()라고 합니다. 특정 시스템(서버 또는 클라이언트)에서 해당 객체가 처음으로 활성화될 때 트리거됩니다. 객체의 라이프사이클 동안 변하지 않는 초기화를 한 번만 수행하려면 Awake()를 사용합니다. GetComponent<>() 호출은 종종 이 범주에 속하지만 일반적으로 Start()를 사용했던 곳에는 Spawned()를 사용합니다.

Fusion의 가장 중요한 콜백은 FixedUpdateNetwork()입니다. FixedUpdateNetwork()는 다음 네트워크 상태에 대한 로직을 실행하고 조정 중에 다시 시뮬레이션하기 위해 Fusion이 사용하는 것입니다.

API 문서에는 이러한 모든 콜백에 대한 자세한 설명이 있지만 이해해야 할 중요한 부분은 렌더링 속도와 무관하게 고정된 간격으로 FixedUpdate()와 같이 FixedUpdateNetwork()가 호출된다는 것입니다. 그리고 FixedUpdate()와는 달리 서버에서 전송된 업데이트를 조정하기 위해 재시뮬레이션이 발생할 때 동일한 상태/프레임/틱에 대해 여러 번 호출할 수 있습니다.

네트워크 속성의 경우 Fusion이 FixedUpdateNetwork()를 호출하기 전에 모든 네트워크 상태 속성을 암시적으로 재설정하므로 재시뮬레이션이 투명합니다.

예를 들어, 문제의 객체가 NetworkTransform 또는 NetworkRigidbody를 사용하는 한 FixedUpdateNetwork() 내에서 이와 같은 것을 실행하는 것은 완벽하게 안전합니다:

C#

transform.position += transform.forward * Runner.DeltaTime;

Runner.DeltaTime의 사용은 네트워크 틱 속도에 따라 시뮬레이션을 유지하는 데 필요하다는 것에 주의하세요.

Render()FixedUpdateNetwork()에 대한 모든 호출 후 LateUpdate() 전에 실행됩니다.

입력

Fusion은 입력 처리를 두 단계로 나눕니다:

  1. 로컬 하드웨어에서 입력을 수집하여 구조체에 담아둡니다. 이 작업은 항상 클라이언트와 호스트(전용 서버가 아닌)에서만 새로운 틱마다 한 번씩 수행됩니다. 이것은 서버로 전송되며 클라이언트에서 즉시 클라이언트 측 예측을 위해 로컬로 사용됩니다.
  2. FixedUpdateNetwork()에서 이 입력을 읽어 게임 상태를 변경할 수 있습니다(시뮬레이션 진행). 클라이언트(입력 권한이 있는 네트워크 객체)와 호스트/서버 모두에서 이 작업을 수행할 수 있습니다. 클라이언트에서는 조정 롤백에서 동일한 틱/프레임이 재시뮬레이션될 때 여러 번 발생할 수 있습니다.

첫 번째 단계는 플레이어가 하는 일을 기록하고 나중에 사용할 수 있도록 저장하는 일반적인 유니티 입력 처리 메커니즘으로 생각할 수 있으며, 두 번째 단계는 네트워크 상태를 수정하기 위해 해당 입력을 적용하는 분리된 스크립트입니다.

첫 번째 단계는 Fusion이 OnGetInput() 콜백에서 폴링하고 두 번째 단계는 일반적으로 메인 캐릭터의 SimulationBehaviour에 의해 FixedUpdateNetwork() 내에서 GetInput() 또는 TryGetInput()을 호출하여 처리합니다.

원격 프로시저 호출

네트워크 클라이언트 간에 게임 상태를 동기화하고 서버 권한을 유지하기 위해서는 입력 구조체와 [Networked] 속성을 사용하는 것이 가장 실용적인 방법이 될 수 있습니다.

예를 들어 클라이언트가 입력 권한이 없는 객체와 드물게 복잡한 상호 작용을 수행하려는 경우(예: 인벤토리의 특정 키를 사용하여 잠긴 문을 여는 경우)입니다.

사용할 열쇠와 어떤 문을 열지를 알려주는 필드를 입력 구조체의 일부로 포함할 수 있지만, 많은 혼란을 주게 되므로 이러한 접근 방식은 거의 사용되지 않습니다.

또 다른 고려 사항은 일반 Fusion 입력을 신뢰할 수 없다는 것입니다. 즉, 패킷이 손실될 수 있습니다. 캐릭터를 이동하는 등의 연속 입력을 수행하는 경우에는 이러한 현상이 거의 나타나지 않지만 단일 일회성 작업을 수행하는 경우에는 해당 캐릭터가 서버에 도착하는지 확인해야 할 수 있습니다.

이러한 경우 Fusion은 RPC(원격 프로시저 호출)를 지원합니다.

Fusion은 RPC를 위한 간단하면서도 강력한 구문을 구현합니다. SimulationBehaviour에서 RPC를 정의하려면 return type ; void를 가진 일반 C# 메소드를 선언하고 이를 [Rpc] 속성으로 태그합니다. 이것은 구조체와 Fusion 객체에 대한 참조(예: NetworkObject 또는 PlayerRef)뿐만 아니라 모든 원시 매개 변수(위의 bool 제외 참고)를 포함될 수 있습니다.

[Rpc] 속성을 사용하면 호출할 수 있는 위치와 실행되는 위치를 필터링할 수 있습니다:

C#

[Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
public void RPC_Configure(string name, Color color)
{
    playerName = name;
    playerColor = color;
}

"RPC" 프리픽스 사용에 주의하세요. 이 정확한 표기법을 사용할 필요는 없지만 메소드 이름을 "rpc"(대소문자 구분 없음)로 사전 또는 사후 수정해야 합니다.

소스 및 대상 속성 외에도 [Rpc] 속성에는 몇 가지 추가 옵션 매개 변수가 있습니다:

  • Channel(신뢰할 수 있음/신뢰할 수 없음) - 전송 중 RPC가 손실되는 것을 신경 쓰지 않으면 신뢰할 수 없음으로 설정합니다(기본값 신뢰할 수 있음)
  • InvokeLocal(true/false) - 로컬 클라이언트에서 RPC를 호출하지 않으려면 false로 설정합니다(기본값 true)
  • InvokeResim(true/false) - 재시뮬레이션 중에도 RPC를 호출하려면 true로 설정합니다(기본값 false)

RPC에 대한 마지막 주의 사항은 RPC에 명시적인 상태가 없다는 것을 명심해야 한다는 것입니다. "모든 클라이언트"에 전송하더라도 아직 온라인 상태가 아닌 클라이언트는 수신하지 않으며, 다시 시작했다가 다시 시작하는 클라이언트는 발생한 일을 잊어버릴 것입니다. 이러한 이유로 RPC의 상태가 정말 일시적인지(예: 채팅 메시지) 간접적으로 [Networked] 속성에 기록되도록 항상 확인해야 합니다.

다음에 학습할 내용

Fusion을 시작하려면 Fusion 100 시리즈로 시작하는 것이 좋습니다. 이 튜토리얼에서는 Fusion을 시작하는 데 필요한 모든 기본 사항을 설명합니다.

Back to top