MLAPI系列 - 03 - Object【对象池】
对象池
Netcode for GameObjects (Netcode)提供了对象池的内置支持,这允许您覆盖Netcode的默认destroy方法,并用您自己的逻辑生成处理程序。 这允许您将被破坏的网络对象存储在一个池中,以便以后重用。 这对于经常使用的对象很有用,比如投射物,也是提高应用程序整体性能的一种方式。 通过预先实例化和重用这些对象的实例,对象池消除了在运行时创建或销毁对象的需要,这可以为CPU节省大量工作。 这意味着,不是一次又一次地创建或销毁同一个对象,而是在使用后简单地停用它,然后,当需要另一个对象时,池回收停用的对象之一并重新激活它。
看见对象池简介了解更多关于对象池的重要性。
1 网络预处理实例处理程序
您可以通过包含INetworkPrefabInstanceHandler 接口并注册NetworkPrefabHandler 注册自己的【对象池】处理程序.
public interface INetworkPrefabInstanceHandler
{
NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation);
void Destroy(NetworkObject networkObject);
}
Netcode 将使用Instantiate 和Destroy 方法代替默认的派生处理程序NetworkObject 在Spawn 和DeSpawn 期间使用。 因为消息实例化一个新的NetworkObject 源自主机或服务器,两者都不会调用Instantiate 方法。 如果INetworkPrefabInstanceHandler 实现NetworkPrefabHanlder (NetworkManager.PrefabHandler ),主机或服务器生成关联的NetworkObject 时,所有客户端(不包括主机)都将调用实例化方法.
2 案例
下面的例子来自Boss Room。它展示了如何使用对象池来处理不同的投射对象。 在该示例中,NetworkObjectPool 包含池对象和类的数据结构PooledPrefabInstanceHandler 处理程序实现INetworkPrefabInstanceHandler .
assets/boss room/Scripts/Shared/Net/networkobjectpool . cs
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Assertions;
namespace BossRoom.Scripts.Shared.Net.NetworkObjectPool
{
public class NetworkObjectPool : NetworkBehaviour
{
private static NetworkObjectPool _instance;
public static NetworkObjectPool Singleton { get { return _instance; } }
[SerializeField]
List<PoolConfigObject> PooledPrefabsList;
HashSet<GameObject> prefabs = new HashSet<GameObject>();
Dictionary<GameObject, Queue<NetworkObject>> pooledObjects = new Dictionary<GameObject, Queue<NetworkObject>>();
private bool m_HasInitialized = false;
public void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(this.gameObject);
}
else
{
_instance = this;
}
}
public override void OnNetworkSpawn()
{
InitializePool();
}
public override void OnNetworkDespawn()
{
ClearPool();
}
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.");
}
}
}
public NetworkObject GetNetworkObject(GameObject prefab)
{
return GetNetworkObjectInternal(prefab, Vector3.zero, Quaternion.identity);
}
public NetworkObject GetNetworkObject(GameObject prefab, Vector3 position, Quaternion rotation)
{
return GetNetworkObjectInternal(prefab, position, rotation);
}
public void ReturnNetworkObject(NetworkObject networkObject, GameObject prefab)
{
var go = networkObject.gameObject;
go.SetActive(false);
pooledObjects[prefab].Enqueue(networkObject);
}
public void AddPrefab(GameObject prefab, int prewarmCount = 0)
{
var networkObject = prefab.GetComponent<NetworkObject>();
Assert.IsNotNull(networkObject, $"{nameof(prefab)} must have {nameof(networkObject)} component.");
Assert.IsFalse(prefabs.Contains(prefab), $"Prefab {prefab.name} is already registered in the pool.");
RegisterPrefabInternal(prefab, prewarmCount);
}
private void RegisterPrefabInternal(GameObject prefab, int prewarmCount)
{
prefabs.Add(prefab);
var prefabQueue = new Queue<NetworkObject>();
pooledObjects[prefab] = prefabQueue;
for (int i = 0; i < prewarmCount; i++)
{
var go = CreateInstance(prefab);
ReturnNetworkObject(go.GetComponent<NetworkObject>(), prefab);
}
NetworkManager.Singleton.PrefabHandler.AddHandler(prefab, new PooledPrefabInstanceHandler(prefab, this));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private GameObject CreateInstance(GameObject prefab)
{
return Instantiate(prefab);
}
private NetworkObject GetNetworkObjectInternal(GameObject prefab, Vector3 position, Quaternion rotation)
{
var queue = pooledObjects[prefab];
NetworkObject networkObject;
if (queue.Count > 0)
{
networkObject = queue.Dequeue();
}
else
{
networkObject = CreateInstance(prefab).GetComponent<NetworkObject>();
}
var go = networkObject.gameObject;
go.SetActive(true);
go.transform.position = position;
go.transform.rotation = rotation;
return networkObject;
}
public void InitializePool()
{
if (m_HasInitialized) return;
foreach (var configObject in PooledPrefabsList)
{
RegisterPrefabInternal(configObject.Prefab, configObject.PrewarmCount);
}
m_HasInitialized = true;
}
public void ClearPool()
{
foreach (var prefab in prefabs)
{
NetworkManager.Singleton.PrefabHandler.RemoveHandler(prefab);
}
pooledObjects.Clear();
}
}
[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)
{
var netObject = m_Pool.GetNetworkObject(m_Prefab, position, rotation);
return netObject;
}
void INetworkPrefabInstanceHandler.Destroy(NetworkObject networkObject)
{
m_Pool.ReturnNetworkObject(networkObject, m_Prefab);
}
}
}
|