Object Spawning
유니티에서는 일반적으로 Instantiate 함수를 통해 게임오브젝트를 만들곤 합니다
Instantiate함수를 이용하여 만든 게임오브젝트를 동기화 하기 위해서는 Spawn()이라는 함수를 이용하면 됩니다!
Network Prefab에는 게임오브젝트에 NetworkObject 컴포넌트가 붙어 있어야 합니다.
주의할 점은, 둘 이상의 NetworkObject 컴포넌트가 붙어 있는 프리팹은 추가할 수 없습니다!
NetworkObject컴포넌트를 가지면 NetworkObject.NetworkObjectId를 이용할 수 있습니다!
사용법은 아래와 같습니다!
Spawning a Network Prefab (Overview)
Netcode는 서버권위적이므로 Network 프리팹을 소환하기 위해서는 서버 또는 호스트에서 이루어져야 합니다!
GameObject go = Instantiate(myPrefab, Vector3.zero, Quaternion.identity);
go.GetComponent<NetworkObject>().Spawn();
단, 주의할 점은 NetworkManager에 NetworkPrefab에 등록해주어야합니다!
public void Spawn(bool destroyWithScene = true);
다음 예시는, non-pooled방식을 이용하여 NetworkObject를 역동적으로 생성하는 방법과 Despawn하는 방법입니다!
public class NonPooledDynamicSpawner : NetworkBehaviour
{
public GameObject PrefabToSpawn;
public bool DestroyWithSpawner;
private GameObject m_PrefabInstance;
private NetworkObject m_SpawnedNetworkObject;
public override void OnNetworkSpawn()
{
// Only the server spawns, clients will disable this component on their side
enabled = IsServer;
if (!enabled || PrefabToSpawn == null)
{
return;
}
// Instantiate the GameObject Instance
m_PrefabInstance = Instantiate(PrefabToSpawn);
// Optional, this example applies the spawner's position and rotation to the new instance
m_PrefabInstance.transform.position = transform.position;
m_PrefabInstance.transform.rotation = transform.rotation;
// Get the instance's NetworkObject and Spawn
m_SpawnedNetworkObject = m_PrefabInstance.GetComponent<NetworkObject>();
m_SpawnedNetworkObject.Spawn();
}
public override void OnNetworkDespawn()
{
if (IsServer && DestroyWithSpawner && m_SpawnedNetworkObject != null && m_SpawnedNetworkObject.IsSpawned)
{
m_SpawnedNetworkObject.Despawn();
}
base.OnNetworkDespawn();
}
}
Destroying / Despawning
기본적으로, 서버나 호스트에서 Network Prefab이 파괴되면 모든 클라이언트에서 자동으로 파괴됩니다!
클라이언트측에서 연결이 끊겼을 때, 클라이언트측의 Network Object 프리팹이 파괴되지 않길 바란다면
m_SpawnedNetworkObject.DontDestroyWithOwner = true;
m_SpawnedNetworkObject.Despawn();
위 코드와 같이, m_SpawnedNetworkObject.DontDestroyWithOwner = true; 를 설정하면 됩니다!
또는,
컴포넌트에서 세팅할 수 있습니다!
Despawning
서버만이 NetworkObject를 despawn할 수 있습니다
서버측에서 NetworkObject를 despawn/destroy하게 되면 클라이언트측에서도 똑같이 despawn/destroy됩니다
클라이언트측에서는 절대 Destroy를 호출할 수 없습니다
만약 클라이언트가 우선이 되는 모델을 사용하고 싶다면, ServerRPC를 호출하여 서버측에 despawning됨을 연기(속인다는 뜻)하도록 하면 됩니다!
Pooled Dynamic Spawning
Pooled Dynamic Spawning은 Netcode Object가 Despawn될 때 파괴하지 않습니다
즉, Despawn될 때 Disabled상태로 바꿔버리는 것입니다!
NGO에서는 INetworkPrefabInstanceHandler 인터페이스를 통해 많은 Netcode Object를 Instantiation하고 파할 수 있습니다!
다음 코드는 PooledDynamicSpawn의 예시입니다!
public class SinglePooledDynamicSpawner : NetworkBehaviour, INetworkPrefabInstanceHandler
{
public GameObject PrefabToSpawn;
public bool SpawnPrefabAutomatically;
private GameObject m_PrefabInstance;
private NetworkObject m_SpawnedNetworkObject;
private void Start()
{
// Instantiate our instance when we start (for both clients and server)
m_PrefabInstance = Instantiate(PrefabToSpawn);
// Get the NetworkObject component assigned to the Prefab instance
m_SpawnedNetworkObject = m_PrefabInstance.GetComponent<NetworkObject>();
// Set it to be inactive
m_PrefabInstance.SetActive(false);
}
private IEnumerator DespawnTimer()
{
yield return new WaitForSeconds(2);
m_SpawnedNetworkObject.Despawn();
StartCoroutine(SpawnTimer());
yield break;
}
private IEnumerator SpawnTimer()
{
yield return new WaitForSeconds(2);
SpawnInstance();
yield break;
}
/// <summary>
/// Invoked only on clients and not server or host
/// INetworkPrefabInstanceHandler.Instantiate implementation
/// Called when Netcode for GameObjects need an instance to be spawned
/// </summary>
public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
{
m_PrefabInstance.SetActive(true);
m_PrefabInstance.transform.position = transform.position;
m_PrefabInstance.transform.rotation = transform.rotation;
return m_SpawnedNetworkObject;
}
/// <summary>
/// Client and Server side
/// INetworkPrefabInstanceHandler.Destroy implementation
/// </summary>
public void Destroy(NetworkObject networkObject)
{
m_PrefabInstance.SetActive(false);
}
public void SpawnInstance()
{
if (!IsServer)
{
return;
}
if (m_PrefabInstance != null && m_SpawnedNetworkObject != null && !m_SpawnedNetworkObject.IsSpawned)
{
m_PrefabInstance.SetActive(true);
m_SpawnedNetworkObject.Spawn();
StartCoroutine(DespawnTimer());
}
}
public override void OnNetworkSpawn()
{
// We register our network Prefab and this NetworkBehaviour that implements the
// INetworkPrefabInstanceHandler interface with the Prefab handler
NetworkManager.PrefabHandler.AddHandler(PrefabToSpawn, this);
if (!IsServer || !SpawnPrefabAutomatically)
{
return;
}
if (SpawnPrefabAutomatically)
{
SpawnInstance();
}
}
public override void OnNetworkDespawn()
{
if (m_SpawnedNetworkObject != null && m_SpawnedNetworkObject.IsSpawned)
{
m_SpawnedNetworkObject.Despawn();
}
base.OnNetworkDespawn();
}
public override void OnDestroy()
{
// This example destroys the
if (m_PrefabInstance != null)
{
// Always deregister the prefab
NetworkManager.Singleton.PrefabHandler.RemoveHandler(PrefabToSpawn);
Destroy(m_PrefabInstance);
}
base.OnDestroy();
}
}
Spanw Prefab Automatically를 체크하면 Host가 접속함과 동시에 Prefab To Spawn에 등록한 Network Object를 소환합니다
2초 후에는 SetActive를 False로 만들고 2초 후에 다시 true로 계속 반복합니다!
즉, 기본적인 Pooling System과 성격이 비슷합니다!
2022.09.04 - [나만의 꿀팁] - Unity Pooling System (최적화 구웃!)
In-Scene Placed NetworkObject
씬에 이미 놓여져 있는 NetworkObject들은 자동으로 복제됩니다!
따라서,추가 작업을 할 필요는 없습니다!
일반적으로, 씬에 놓여 있는 NetworkObject들을 동기화하는데에는 2가지 방법이 있습니다
- Soft Synchronization (Scene Management 사용할 때)
- Prefab Synchronization (Scene Management 사용하지 않을 )
Scene Management를 사용하면 얻는 이점은 씬에 놓여 있는 NetworkObject들을 매번 NetworkManager안에 NetworkObject로 등록하지 않아도 됩니다!