-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNetworkObjectPool.cs
181 lines (155 loc) · 6.36 KB
/
NetworkObjectPool.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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);
}
}
}