You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
6.4 KiB
C#
182 lines
6.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.Netcode;
|
|
using UnityEngine;
|
|
using UnityEngine.Assertions;
|
|
using UnityEngine.Pool;
|
|
|
|
namespace Unity.BossRoom.Infrastructure
|
|
{
|
|
/// <summary>
|
|
/// Object Pool for networked objects, used for controlling how objects are spawned by Netcode. Netcode by default
|
|
/// will allocate new memory when spawning new objects. With this Networked Pool, we're using the ObjectPool to
|
|
/// reuse objects.
|
|
/// Boss Room uses this for projectiles. In theory it should use this for imps too, but we wanted to show vanilla spawning vs pooled spawning.
|
|
/// Hooks to NetworkManager's prefab handler to intercept object spawning and do custom actions.
|
|
/// </summary>
|
|
public class NetworkObjectPool : NetworkBehaviour
|
|
{
|
|
public static NetworkObjectPool Singleton { get; private set; }
|
|
|
|
[SerializeField]
|
|
List<PoolConfigObject> PooledPrefabsList;
|
|
|
|
HashSet<GameObject> m_Prefabs = new HashSet<GameObject>();
|
|
|
|
Dictionary<GameObject, ObjectPool<NetworkObject>> m_PooledObjects = new Dictionary<GameObject, ObjectPool<NetworkObject>>();
|
|
|
|
public void Awake()
|
|
{
|
|
if (Singleton != null && Singleton != this)
|
|
{
|
|
Destroy(gameObject);
|
|
}
|
|
else
|
|
{
|
|
Singleton = this;
|
|
}
|
|
}
|
|
|
|
public override void OnNetworkSpawn()
|
|
{
|
|
// Registers all objects in PooledPrefabsList to the cache.
|
|
foreach (var configObject in PooledPrefabsList)
|
|
{
|
|
RegisterPrefabInternal(configObject.Prefab, configObject.PrewarmCount);
|
|
}
|
|
}
|
|
|
|
public override void OnNetworkDespawn()
|
|
{
|
|
// Unregisters all objects in PooledPrefabsList from the cache.
|
|
foreach (var prefab in m_Prefabs)
|
|
{
|
|
// Unregister Netcode Spawn handlers
|
|
NetworkManager.Singleton.PrefabHandler.RemoveHandler(prefab);
|
|
m_PooledObjects[prefab].Clear();
|
|
}
|
|
m_PooledObjects.Clear();
|
|
m_Prefabs.Clear();
|
|
}
|
|
|
|
public void OnValidate()
|
|
{
|
|
for (var i = 0; i < PooledPrefabsList.Count; i++)
|
|
{
|
|
var prefab = PooledPrefabsList[i].Prefab;
|
|
if (prefab != null)
|
|
{
|
|
Assert.IsNotNull(prefab.GetComponent<NetworkObject>(), $"{nameof(NetworkObjectPool)}: Pooled prefab \"{prefab.name}\" at index {i.ToString()} has no {nameof(NetworkObject)} component.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an instance of the given prefab from the pool. The prefab must be registered to the pool.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// To spawn a NetworkObject from one of the pools, this must be called on the server, then the instance
|
|
/// returned from it must be spawned on the server. This method will then also be called on the client by the
|
|
/// PooledPrefabInstanceHandler when the client receives a spawn message for a prefab that has been registered
|
|
/// here.
|
|
/// </remarks>
|
|
/// <param name="prefab"></param>
|
|
/// <param name="position">The position to spawn the object at.</param>
|
|
/// <param name="rotation">The rotation to spawn the object with.</param>
|
|
/// <returns></returns>
|
|
public NetworkObject GetNetworkObject(GameObject prefab, Vector3 position, Quaternion rotation)
|
|
{
|
|
var networkObject = m_PooledObjects[prefab].Get();
|
|
|
|
var noTransform = networkObject.transform;
|
|
noTransform.position = position;
|
|
noTransform.rotation = rotation;
|
|
|
|
return networkObject;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return an object to the pool (reset objects before returning).
|
|
/// </summary>
|
|
public void ReturnNetworkObject(NetworkObject networkObject, GameObject prefab)
|
|
{
|
|
m_PooledObjects[prefab].Release(networkObject);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds up the cache for a prefab.
|
|
/// </summary>
|
|
void RegisterPrefabInternal(GameObject prefab, int prewarmCount)
|
|
{
|
|
NetworkObject CreateFunc()
|
|
{
|
|
return Instantiate(prefab).GetComponent<NetworkObject>();
|
|
}
|
|
|
|
void ActionOnGet(NetworkObject networkObject)
|
|
{
|
|
networkObject.gameObject.SetActive(true);
|
|
}
|
|
|
|
void ActionOnRelease(NetworkObject networkObject)
|
|
{
|
|
networkObject.gameObject.SetActive(false);
|
|
}
|
|
|
|
void ActionOnDestroy(NetworkObject networkObject)
|
|
{
|
|
Destroy(networkObject.gameObject);
|
|
}
|
|
|
|
m_Prefabs.Add(prefab);
|
|
|
|
// Create the pool
|
|
m_PooledObjects[prefab] = new ObjectPool<NetworkObject>(CreateFunc, ActionOnGet, ActionOnRelease, ActionOnDestroy, defaultCapacity: prewarmCount);
|
|
|
|
// Populate the pool
|
|
var prewarmNetworkObjects = new List<NetworkObject>();
|
|
for (var i = 0; i < prewarmCount; i++)
|
|
{
|
|
prewarmNetworkObjects.Add(m_PooledObjects[prefab].Get());
|
|
}
|
|
foreach (var networkObject in prewarmNetworkObjects)
|
|
{
|
|
m_PooledObjects[prefab].Release(networkObject);
|
|
}
|
|
|
|
// Register Netcode Spawn handlers
|
|
NetworkManager.Singleton.PrefabHandler.AddHandler(prefab, new PooledPrefabInstanceHandler(prefab, this));
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
struct PoolConfigObject
|
|
{
|
|
public GameObject Prefab;
|
|
public int PrewarmCount;
|
|
}
|
|
|
|
class PooledPrefabInstanceHandler : INetworkPrefabInstanceHandler
|
|
{
|
|
GameObject m_Prefab;
|
|
NetworkObjectPool m_Pool;
|
|
|
|
public PooledPrefabInstanceHandler(GameObject prefab, NetworkObjectPool pool)
|
|
{
|
|
m_Prefab = prefab;
|
|
m_Pool = pool;
|
|
}
|
|
|
|
NetworkObject INetworkPrefabInstanceHandler.Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
|
|
{
|
|
return m_Pool.GetNetworkObject(m_Prefab, position, rotation);
|
|
}
|
|
|
|
void INetworkPrefabInstanceHandler.Destroy(NetworkObject networkObject)
|
|
{
|
|
m_Pool.ReturnNetworkObject(networkObject, m_Prefab);
|
|
}
|
|
}
|
|
|
|
}
|