고급 스폰
개요
Fusion에서의 스폰은 Runner.Spawn()
, Runner.SpawnAsync()
, 또는 Runner.TrySpawn()
을 호출하는 것으로 이루어집니다. 이 호출은 다른 모든 피어에 복제되는 네트워크 엔티티를 생성합니다.
- 서버/클라이언트 모드(서버/호스트/싱글)에서는 서버만 오브젝트를 스폰 할 수 있으며, 해당 스폰은 모든 클라이언트에 복제됩니다.
- 공유 모드에서는 모든 클라이언트가
Spawn()
을 호출할 수 있지만, 이 호출은 공유 서버에 메시지를 전송하여 실제 스폰을 시작하게 합니다.
Spawn()
이 호출되거나 복제되면, 응답으로 네트워크 오브젝트(즉, NetworkObject
컴포넌트가 있는 유니티의 GameObject
)가 생성됩니다. 해당 게임 오브젝트가 어떻게 생성되는지는 NetworkRunner
에 연결된 Object Provider에 의해 결정됩니다.
오브젝트 제공자
오브젝트 제공자는 Runner.Spawn()
및 Runner.Despawn()
작업이 유니티 GameObject를 생성하고 소멸하는 방식을 정의하는 INetworkObjectProvider
구현체입니다. 모든 활성 NetworkRunner
는 관련된 INetworkObjectProvider
인스턴스를 가지고 있으며, 기본적으로는 NetworkObjectProviderDefault
인스턴스가 사용됩니다.
사용자 정의 오브젝트 제공자 사용 사례
사용자 맞춤형 INetworkObjectProvider
클래스를 생성하면 스폰 되는 실제 GameObject의 생명주기를 정밀하게 제어할 수 있으며, 다음과 같은 경우에 유용할 수 있습니다;
- 풀링(Pooling)
- 런타임 중에 네트워크 오브젝트를 동적으로 생성
- 싱글 플레이어 모드로의 전환 및 그 반대의 고급 처리
오브젝트 제공자 할당
네트워크 러너에 오브젝트 제공자를 설정하는 방법에는 여러 가지가 있습니다.
Runner.GameStart()
를 호출할 때INetworkObjectProvider
인스턴스를 전달합니다.
C#
runner.StartGame(new StartGameArgs(){ ObjectProvider = new MyObjectProvider()});
- 또는, Network Runner 게임 오브젝트에
INetworkObjectProvider
를 구현하는 컴포넌트를 추가합니다. - 또는, 둘 다 하지 않으면 러너는 자동으로 자신의 게임 오브젝트에
NetworkObjectProviderDefault
를 추가합니다.
INetworkObjectProvider 구현
INetworkObjectProvider
인터페이스는 다음 두 가지 메서드의 구현을 요구합니다:
AcquireInstance()
:NetworkRunner.Spawn()
이 호출되었을 때 풀에서 객체를 획득하는 데 사용됩니다.ReleaseInstance()
:NetworkRunner.Destroy()
가 호출되었을 때 풀에서 객체를 해제하고 반환하는 데 사용됩니다.
NetworkObjectProviderDefault
NetworkObjectProviderDefault
는 INetworkObjectProvider
의 기본 대체 구현체입니다. Runner.StartGame()
에 전달된 StartArgs
에서 제공자를 지정하지 않으면, 러너는 자신의 게임 오브젝트에서 INetworkObjectProvider
를 구현하는 컴포넌트를 검색합니다. 만약 구현체가 없다면, 러너는 자동으로 자신의 게임 오브젝트에 NetworkObjectProviderDefault
컴포넌트를 추가하여 이를 오브젝트 제공자로 사용합니다.
NetworkObjectProviderDefault
는 매우 기본적인 구현체로, 풀링 없이 전달된 프리팹의 복사본을 인스턴스화하여 Spawn()
에 응답합니다. 반면, Despawn()
호출은 Destroy()
를 사용하여 처리됩니다.
사용자 정의 풀링
객체 풀링은 메모리 단편화를 최소화하고 CPU 및 가비지 컬렉터에 가해지는 부담을 줄이기 위해 사용되는 일반적인 패턴입니다.
이와 같은 이유로, 필수는 아니지만 INetworkObjectProvider
구현에서 NetworkObject
의 풀링을 구현하는 것이 권장됩니다.
게임 세션에 사용될 객체 풀은 INetworkObjectPool
을 구현해야 합니다. 또한, 게임이 시작되기 전에 이를 알고 있어야 하며, NetworkRunner.StartGame()
에 전달되는 StartGameArgs.ObjectPool
매개변수에 할당되어야 합니다. 만약 객체 풀이 지정되지 않으면, NetworkObjectPoolDefault
가 사용됩니다.
런타임에서 사용자 정의 객체 제공
프리팹을 사용하여 스폰 하는 대신 런타임에서 게임 오브젝트를 동적으로 생성하려면, 사용자 정의 INetworkObjectProvider
클래스를 생성하고 AcquirePrefabInstance()
메서드를 오버라이드 하여 다음을 수행해야 합니다:
Instantiate()
,new GameObject()
, 또는GameObject.CreatePrimitive()
를 사용하여 게임 오브젝트를 생성합니다.- 게임 오브젝트에 아직
NetworkObject
컴포넌트가 없다면 추가합니다. - 원하는 자식
GameObject
와NetworkBehaviour
를 추가합니다. - 네트워크 오브젝트 구조에 대한 모든 변경 후 마지막으로
NetworkObjectBaker.Bake()
를 호출하여 네트워크 오브젝트를 베이크 합니다. - 결과로
NetworkObject
인스턴스를 반환합니다.
자체 사용자 정의 INetworkObjectProvider
구현을 만들 때 기본 클래스로 NetworkObjectProviderDefault
를 사용하는 것이 가능하며, 대부분의 경우 추천됩니다.
중요: 스폰 되어 부착된 후에는 네트워크 오브젝트에 네트워크 행동(Network Behaviour)을 추가하거나 제거할 수 없습니다. 네트워크 오브젝트의 네트워크 행동에 대한 모든 커스터마이징은 스폰 되기 전에 이루어져야 합니다. 물론 네트워크와 관련 없는 컴포넌트는 언제든지 추가하거나 제거할 수 있습니다.
사용자 정의 오브젝트 제공자 예제
이 예제 코드는 미리 만들어진 프리팹을 사용하여 스폰 하는 대신, 런타임에서 사용자 정의 네트워크 오브젝트를 생성하는 방법을 보여줍니다.
C#
public class BakingObjectProvider : NetworkObjectProviderDefault
{
// For this sample, we are using very high flag values to indicate custom.
// Other values will fall through the default instantiation handling.
public const int CUSTOM_PREFAB_FLAG = 100000;
// The NetworkObjectBaker class can be reused and is Runner independent.
private static NetworkObjectBaker _baker;
private static NetworkObjectBaker Baker => _baker ??= new NetworkObjectBaker();
public override NetworkObjectAcquireResult AcquirePrefabInstance(NetworkRunner runner, in NetworkPrefabAcquireContext context, out NetworkObject result)
{
// Detect if this is a custom spawn by its high prefabID value we are passing.
// The Spawn call will need to pass this value instead of a prefab.
if (context.PrefabId.RawValue == CUSTOM_PREFAB_FLAG)
{
var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
var no = go.AddComponent<NetworkObject>();
go.AddComponent<NetworkTransform>();
go.name = $"Custom Object";
// Baking is required for the NetworkObject to be valid for spawning.
Baker.Bake(go);
// Move the object to the applicable Runner Scene/PhysicsScene/DontDestroyOnLoad
// These implementations exist in the INetworkSceneManager assigned to the runner.
if (context.DontDestroyOnLoad)
{
runner.MakeDontDestroyOnLoad(go);
}
else
{
runner.MoveToRunnerScene(go);
}
// We are finished. Return the NetworkObject and report success.
result = no;
return NetworkObjectAcquireResult.Success;
}
// For all other spawns, use the default spawning.
return base.AcquirePrefabInstance(runner, context, out result);
}
}
Back to top