Photon Fusion은 Photon에서 개발한 차세대 네트워크 엔진으로, Unity에서 멀티플레이어 게임을 개발할 때 사용됩니다. 기존에 사용하던 Photon PUN과 Bolt의 장점을 결합하여 높은 성능과 유연성을 제공합니다. 그러면서 기존과 방식과 사용 함수가 다소 다르기 때문에 주의해야합니다.

자세한 변경사항은 다음과 같습니다.
Fusion의 주요 컴포넌트는 NetworkRunner와 NetworkObject가 있습니다.
NetworkRunner는 Fusion의 핵심으로 네트워킹의 주체라고 볼 수 있습니다. NetworkRunner는 네트워킹과 시뮬레이션을 모두 관리하는 단일 오브젝트입니다.
NetworkObject는 유니티의 프리팹 또는 객체가 네트워크에 존재하기 위해 필요한 컴포넌트입니다. NetworkObject를 추가하면 런타임시 네트워크 ID가 할당 되고 동기화 된 틱 기반 시뮬레이션에서 동작합니다.
이를 위해서는 SimulationBehaviour 및 NetworkBehaviour의 상속이 필요합니다.
[Networked] public byte life { get; set; }
위처럼 Netword은 거의 모든 속성에 사용할 수 있으며 해당 값은 네트워크상에서 공유됩니다.
bool값은 예외적으로 NetworkBool을 사용합니다.
[Networked(OnChanged = "OnTypeChanged")] public Type type { get; set; }
public static void OnTypeChanged(Changed<TheClassWhichHasTheProperty> changed)
{
// your code here - check API docs for more details
}
또한 위와 같이 변경될때마다 호출 되는 콜백을 등록 할 수도 있습니다.
NetworkBehaviour는 기존의 MonoBehaviour를 대처합니다.
MonoBehaviour의 동작을 네트워크에서도 동작 할 수 있게합니다.
또한 NetworkTransform를 통해 네트워크에서 동기화된 이동을 스냅샵 보간을 통해 자연스럽게 움직일 수 있으며 NetworkRigidbody를 통한 물리연산 제어도 시행합니다.
휴머노이드 기반의 아바타의 경우 NetworkCharacterController를 통해 이동을 구현할 수 있습니다.
네트워크 상에서 동작하기 위해서는 SimulationBehaviour의 라이프 사이클을 따른다.
Awake, Start등의 내장 코드는 사용 가능하지만 안전을 위해 대처한다.
Start()는 Spawned()로 전환하면 객체가 처음 활성화 될 때 호출된다.
또한 FixedUpdateNetwork()는 가장 중요한 함수로 고정된 네트워크 틱마다 호출 된다.
NetworkTransform, NetworkRigidbody는 이때 호출하는게 안전하다.
transform.position += transform.forward * Runner.DeltaTime;
Runner.DeltaTime는 Time.DeltaTime 처럼 일정한 속도를 구현하는데 필요하다.
Render()는 FixedUpdateNetwork()에 대한 모든 호출 후 LateUpdate() 전에 실행되어 네트워크 진행 후 결과를 표시할 때 사용한다.
Fusion은 입력 처리를 두 단계로 나눠진다.
첫 번째 단계는 Fusion이 OnGetInput() 콜백에서 폴링하고 두 번째 단계는 일반적으로 메인 캐릭터의 SimulationBehaviour에 의해 FixedUpdateNetwork() 내에서 GetInput() 또는 TryGetInput()을 호출하여 처리된다.
// 입력 예시
public void OnInput(NetworkRunner runner, NetworkInput input)
{
var data = new NetworkInputData();
if (Input.GetKey(KeyCode.W))
data.direction += Vector2.up;
if (Input.GetKey(KeyCode.S))
data.direction += Vector2.down;
if (Input.GetKey(KeyCode.A))
data.direction += Vector2.left;
if (Input.GetKey(KeyCode.D))
data.direction += Vector2.right;
data.buttons.Set(NetworkInputData.MOUSEBUTTON0, _mouseButton0);
_mouseButton0 = false;
input.Set(data);
}
// 입력 처리 예시
public override void FixedUpdateNetwork()
{
if (GetInput(out NetworkInputData data))
{
// move
data.direction.Normalize();
_cc.Move(5 * data.direction * Runner.DeltaTime);
}
}
RPC는 void를 반환타입으로 가는 함수를 선언하고 [Rpc] 태그하고 호출 위치와 실행 위치를 지정합니다.
[Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
public void RPC_Configure(string name, Color color)
{
playerName = name;
playerColor = color;
}
위 코드는 RpcSources.InputAuthority(입력 권한을 가진 클라이언트) 권한을 가지는 클라이언트에서 호출 할 수 있으며 RpcTargets.StateAuthority(상태를 관리하는 주체) 를 가지는 클라이언트에서 실행 됩니다. 즉 플레이어 클라이언트에서 호출할 수 있고 실행은 서버(호스트)에서 실행 됩니다.
참고
https://doc.photonengine.com/ko-kr/fusion/v1/fusion-intro