Skip to content

Instantly share code, notes, and snippets.

@James-Frowen
Last active November 2, 2023 03:25
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save James-Frowen/c2ab4cdc96165298518bd2db0781bbe6 to your computer and use it in GitHub Desktop.
Save James-Frowen/c2ab4cdc96165298518bd2db0781bbe6 to your computer and use it in GitHub Desktop.
Prefab Pool for Unity and Mirror Networking, see simple example here https://gist.github.com/James-Frowen/46ca5e8fd76d62527be7b958ca8dbaf1
using Mirror;
using UnityEngine;
namespace JamesFrowen.Spawning
{
public class MirrorPrefabPool : PrefabPool
{
private readonly NetworkIdentity _networkPrefab;
private bool _handlersRegistered;
public MirrorPrefabPool(GameObject prefab, int capacity = 100) : this(prefab.GetComponent<PrefabPoolBehaviour>(), prefab.GetComponent<NetworkIdentity>(), capacity) { }
public MirrorPrefabPool(PrefabPoolBehaviour prefab, int capacity = 100) : this(prefab, prefab.GetComponent<NetworkIdentity>(), capacity) { }
private MirrorPrefabPool(PrefabPoolBehaviour prefab, NetworkIdentity identity, int capacity = 100) : base(prefab, capacity)
{
if (identity == null)
{
throw new System.ArgumentNullException(nameof(identity), "Network prefab should have NetworkIdentity");
}
_networkPrefab = identity;
if (NetworkClient.active)
{
this.RegisterMirrorHandlers();
}
}
#region Mirror Handlers
private GameObject networkSpawnHandler(SpawnMessage msg)
{
var behaviour = this.Spawn(msg.position, msg.rotation);
return behaviour.gameObject;
}
private void networkUnSpawnHandler(GameObject gameObject)
{
var behaviour = gameObject.GetComponent<PrefabPoolBehaviour>();
this.Unspawn(behaviour);
}
public void RegisterMirrorHandlers()
{
if (this._handlersRegistered) { return; }
ClientScene.RegisterPrefab(this._networkPrefab.gameObject, this.networkSpawnHandler, this.networkUnSpawnHandler);
this._handlersRegistered = true;
}
public void UnregisterMirrorHandlers()
{
if (!this._handlersRegistered) { return; }
ClientScene.UnregisterPrefab(this._networkPrefab.gameObject);
this._handlersRegistered = false;
}
#endregion
#region IDisposable Support
protected override void Dispose(bool disposing)
{
if (!this.disposedValue)
{
this.UnregisterMirrorHandlers();
}
base.Dispose(disposing);
}
#endregion
}
}
using UnityEngine;
namespace JamesFrowen.Spawning
{
public class PrefabPool : System.IDisposable
{
/// <summary>
/// parent shared by all pools
/// </summary>
private static Transform _poolParent;
protected readonly PrefabPoolBehaviour _prefab;
private readonly PrefabPoolBehaviour[] _pool;
/// <summary>
/// parent for this pool, should be a child of _poolParent
/// </summary>
private readonly Transform _parent;
private int _next = -1;
public PrefabPool(GameObject prefab, int capacity = 100) : this(prefab.GetComponent<PrefabPoolBehaviour>(), capacity) { }
public PrefabPool(PrefabPoolBehaviour prefab, int capacity = 100)
{
if (prefab == null)
{
throw new System.ArgumentNullException(nameof(prefab));
}
this._prefab = prefab;
this._pool = new PrefabPoolBehaviour[capacity];
if (_poolParent == null)
{
_poolParent = new GameObject("PrefabPools").transform;
}
this._parent = new GameObject(this._prefab.name).transform;
this._parent.SetParent(_poolParent);
}
public void ClearNullObject()
{
var nextEmpty = 0;
for (var i = 0; i < this._pool.Length; i++)
{
if (this._pool[i] != null)
{
this._pool[nextEmpty] = this._pool[i];
nextEmpty++;
}
else
{
// clear c# object if Unity object is null
this._pool[i] = null;
}
}
this._next = nextEmpty - 1;
}
public void Warnup(int createCount)
{
for (var i = this._next + 1; i < createCount; i++)
{
var item = UnityEngine.Object.Instantiate(this._prefab);
item.OnInstantiate(this, this._parent);
this.putBack(item);
}
}
private PrefabPoolBehaviour getNext()
{
PrefabPoolBehaviour item;
if (this._next == -1)
{
item = UnityEngine.Object.Instantiate(this._prefab);
item.OnInstantiate(this, this._parent);
}
else
{
item = this._pool[this._next];
this._pool[this._next] = null;
this._next--;
}
item.BeforeSpawn();
return item;
}
private void putBack(PrefabPoolBehaviour obj)
{
if (this.disposedValue)
{
UnityEngine.Object.Destroy(obj.gameObject);
return;
}
if (this._next < this._pool.Length - 1)
{
this._next++;
this._pool[this._next] = obj;
obj.AfterUnspawn();
}
else
{
UnityEngine.Object.Destroy(obj.gameObject);
GameLog.LogWarning("NetworkWriterPool.Recycle, Pool was full leaving extra writer for GC");
}
}
public PrefabPoolBehaviour Spawn()
{
return this.getNext();
}
public PrefabPoolBehaviour Spawn(Vector3 position, Quaternion rotation)
{
var obj = this.getNext();
obj.transform.position = position;
obj.transform.rotation = rotation;
return obj;
}
public PrefabPoolBehaviour Spawn(Transform parent)
{
var obj = this.getNext();
obj.transform.SetParent(parent);
return obj;
}
public PrefabPoolBehaviour Spawn(Vector3 position, Quaternion rotation, Transform parent)
{
var obj = this.getNext();
obj.transform.SetParent(parent);
obj.transform.position = position;
obj.transform.rotation = rotation;
return obj;
}
public void Unspawn(PrefabPoolBehaviour obj)
{
this.putBack(obj);
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
for (var i = 0; i < this._pool.Length; i++)
{
if (this._pool[i] != null)
{
UnityEngine.Object.Destroy(this._pool[i].gameObject);
this._pool[i] = null;
}
}
}
this.disposedValue = true;
}
}
public void Dispose()
{
this.Dispose(true);
}
#endregion
}
}
using UnityEngine;
namespace JamesFrowen.Spawning
{
public class PrefabPoolBehaviour : MonoBehaviour
{
private PrefabPool _pool;
private Transform _parent;
internal virtual void OnInstantiate(PrefabPool pool, Transform _parent)
{
this._pool = pool;
this._parent = _parent;
this.transform.SetParent(this._parent);
}
internal virtual void BeforeSpawn()
{
this.gameObject.SetActive(true);
}
internal void AfterUnspawn()
{
this.gameObject.SetActive(false);
this.transform.SetParent(this._parent);
}
public void Unspawn()
{
this._pool.Unspawn(this);
}
}
}
using UnityEngine;
namespace JamesFrowen.Spawning
{
[RequireComponent(typeof(Rigidbody))]
public class PrefabRigidbodyPoolBehaviour : PrefabPoolBehaviour
{
private Rigidbody _rigidBody;
internal override void OnInstantiate(PrefabPool pool, Transform _parent)
{
base.OnInstantiate(pool, _parent);
this._rigidBody = this.GetComponent<Rigidbody>();
}
internal override void BeforeSpawn()
{
this._rigidBody.velocity = Vector3.zero;
this._rigidBody.angularVelocity = Vector3.zero;
base.BeforeSpawn();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment