feat: spawning particles with object pooling

This commit is contained in:
Gerard Gascón 2024-04-19 15:57:25 +02:00
parent 858e49e1d9
commit efac0019ba
14 changed files with 235 additions and 21 deletions

View file

@ -112,3 +112,4 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
animator: {fileID: 1908639354391246343}
particleAnimation: {fileID: 11400000, guid: 3aa2da34174b2354d94d7764f109d1c0, type: 2}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a956b08a87cc495ca71443493064210d
timeCreated: 1713519317

View file

@ -0,0 +1,5 @@
namespace Pooling {
public interface IPooledObject {
void OnObjectSpawn();
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 75e27a032d064219bd1f9314952bdbb8
timeCreated: 1713534220

View file

@ -0,0 +1,152 @@
using System.Collections.Generic;
using UnityEngine;
namespace Pooling {
public class Pooler : MonoBehaviour {
private static Pooler _instance;
public static Pooler Instance {
get {
if (_instance != null)
return _instance;
_instance = FindObjectOfType<Pooler>();
if (_instance != null)
return _instance;
GameObject obj = new("[Object Pool]");
_instance = obj.AddComponent<Pooler>();
return _instance;
}
}
private static List<GameObject> _tempList = new();
private Dictionary<GameObject, List<GameObject>> _pooledObjects = new();
private Dictionary<GameObject, GameObject> _spawnedObjects = new();
public static void CreatePool<T>(T prefab, int initialPoolSize) where T : Component {
CreatePool(prefab.gameObject, initialPoolSize);
}
public static void CreatePool(GameObject prefab, int initialPoolSize) {
if (prefab == null || Instance._pooledObjects.ContainsKey(prefab))
return;
List<GameObject> list = new();
Instance._pooledObjects.Add(prefab, list);
if (initialPoolSize <= 0) return;
bool active = prefab.activeSelf;
prefab.SetActive(false);
Transform parent = Instance.transform;
while (list.Count < initialPoolSize) {
GameObject obj = Instantiate(prefab, parent);
list.Add(obj);
}
prefab.SetActive(active);
}
public static T Spawn<T>(T prefab, Transform parent, Vector3 position, Quaternion rotation)
where T : Component =>
Spawn(prefab.gameObject, parent, position, rotation).GetComponent<T>();
public static T Spawn<T>(T prefab, Vector3 position, Quaternion rotation) where T : Component =>
Spawn(prefab.gameObject, null, position, rotation).GetComponent<T>();
public static T Spawn<T>(T prefab, Transform parent, Vector3 position) where T : Component =>
Spawn(prefab.gameObject, parent, position, Quaternion.identity).GetComponent<T>();
public static T Spawn<T>(T prefab, Vector3 position) where T : Component =>
Spawn(prefab.gameObject, null, position, Quaternion.identity).GetComponent<T>();
public static T Spawn<T>(T prefab, Transform parent) where T : Component =>
Spawn(prefab.gameObject, parent, Vector3.zero, Quaternion.identity).GetComponent<T>();
public static T Spawn<T>(T prefab) where T : Component =>
Spawn(prefab.gameObject, null, Vector3.zero, Quaternion.identity).GetComponent<T>();
public static GameObject Spawn(GameObject prefab, Transform parent, Vector3 position) =>
Spawn(prefab, parent, position, Quaternion.identity);
public static GameObject Spawn(GameObject prefab, Vector3 position, Quaternion rotation) =>
Spawn(prefab, null, position, rotation);
public static GameObject Spawn(GameObject prefab, Transform parent) =>
Spawn(prefab, parent, Vector3.zero, Quaternion.identity);
public static GameObject Spawn(GameObject prefab, Vector3 position) =>
Spawn(prefab, null, position, Quaternion.identity);
public static GameObject Spawn(GameObject prefab) =>
Spawn(prefab, null, Vector3.zero, Quaternion.identity);
public static GameObject Spawn(GameObject prefab, Transform parent, Vector3 position, Quaternion rotation) {
List<GameObject> list;
Transform trans;
GameObject obj;
if (Instance._pooledObjects.TryGetValue(prefab, out list)) {
obj = null;
if (list.Count > 0) {
while (!obj && list.Count > 0) {
obj = list[0];
list.RemoveAt(0);
}
if (obj != null) {
trans = obj.transform;
trans.parent = parent;
trans.localPosition = position;
trans.localRotation = rotation;
obj.SetActive(true);
Instance._spawnedObjects.Add(obj, prefab);
TryCallPooledObject(obj);
return obj;
}
}
obj = Instantiate(prefab);
trans = obj.transform;
trans.parent = parent;
trans.localPosition = position;
trans.localRotation = rotation;
Instance._spawnedObjects.Add(obj, prefab);
TryCallPooledObject(obj);
return obj;
}
obj = Instantiate(prefab);
trans = obj.transform;
trans.parent = parent;
trans.localPosition = position;
trans.localRotation = rotation;
TryCallPooledObject(obj);
return obj;
}
private static void TryCallPooledObject(GameObject obj) {
if(obj.TryGetComponent(out IPooledObject pooledObject))
pooledObject.OnObjectSpawn();
}
public static void Recycle<T>(T obj) where T : Component {
Recycle(obj.gameObject);
}
public static void Recycle(GameObject obj) {
GameObject prefab;
if (Instance._spawnedObjects.TryGetValue(obj, out prefab))
Recycle(obj, prefab);
else
Destroy(obj);
}
private static void Recycle(GameObject obj, GameObject prefab) {
Instance._pooledObjects[prefab].Add(obj);
Instance._spawnedObjects.Remove(obj);
obj.transform.parent = Instance.transform;
obj.SetActive(false);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b8fc72fe7498488581bb9ecd15cdb057
timeCreated: 1713519317

View file

@ -0,0 +1,39 @@
using UnityEngine;
namespace Pooling {
public static class PoolerExtensions {
public static void CreatePool<T>(this T prefab) where T : Component =>
Pooler.CreatePool(prefab, 0);
public static void CreatePool<T>(this T prefab, int initialPoolSize) where T : Component =>
Pooler.CreatePool(prefab, initialPoolSize);
public static void CreatePool(this GameObject prefab) =>
Pooler.CreatePool(prefab, 0);
public static void CreatePool(this GameObject prefab, int initialPoolSize) =>
Pooler.CreatePool(prefab, initialPoolSize);
public static T Spawn<T>(this T prefab, Transform parent, Vector3 position, Quaternion rotation) where T : Component =>
Pooler.Spawn(prefab, parent, position, rotation);
public static T Spawn<T>(this T prefab, Vector3 position, Quaternion rotation) where T : Component =>
Pooler.Spawn(prefab, null, position, rotation);
public static T Spawn<T>(this T prefab, Transform parent, Vector3 position) where T : Component =>
Pooler.Spawn(prefab, parent, position, Quaternion.identity);
public static T Spawn<T>(this T prefab, Vector3 position) where T : Component =>
Pooler.Spawn(prefab, null, position, Quaternion.identity);
public static T Spawn<T>(this T prefab, Transform parent) where T : Component =>
Pooler.Spawn(prefab, parent, Vector3.zero, Quaternion.identity);
public static T Spawn<T>(this T prefab) where T : Component =>
Pooler.Spawn(prefab, null, Vector3.zero, Quaternion.identity);
public static GameObject Spawn(this GameObject prefab, Transform parent, Vector3 position, Quaternion rotation) =>
Pooler.Spawn(prefab, parent, position, rotation);
public static GameObject Spawn(this GameObject prefab, Vector3 position, Quaternion rotation) =>
Pooler.Spawn(prefab, null, position, rotation);
public static GameObject Spawn(this GameObject prefab, Transform parent, Vector3 position) =>
Pooler.Spawn(prefab, parent, position, Quaternion.identity);
public static GameObject Spawn(this GameObject prefab, Vector3 position) =>
Pooler.Spawn(prefab, null, position, Quaternion.identity);
public static GameObject Spawn(this GameObject prefab, Transform parent) =>
Pooler.Spawn(prefab, parent, Vector3.zero, Quaternion.identity);
public static GameObject Spawn(this GameObject prefab) =>
Pooler.Spawn(prefab, null, Vector3.zero, Quaternion.identity);
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ea3218d098d947b6b008751f3c2cd8f4
timeCreated: 1713522613

View file

@ -0,0 +1,3 @@
{
"name": "SantJordi.Pooling"
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1e89d5d1db358ce43890b8310d696377
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -8,7 +8,8 @@
"GUID:1220ccfff01d26041a9bb8cd7ae584af",
"GUID:58f2f98b0cec4e74998cb65ad59190b4",
"GUID:0c752da273b17c547ae705acf0f2adf2",
"GUID:a8b14a26d6924d6bb65739ca56ae8187"
"GUID:a8b14a26d6924d6bb65739ca56ae8187",
"GUID:1e89d5d1db358ce43890b8310d696377"
],
"includePlatforms": [],
"excludePlatforms": [],

View file

@ -1,14 +1,19 @@
using System;
using Pooling;
using Presenter;
using UnityEngine;
using UnityEngine.Serialization;
using Animation = FramedAnimator.Animation;
using Animator = FramedAnimator.Animator;
namespace View.Scene {
public class GrowParticle : MonoBehaviour {
public class GrowParticle : MonoBehaviour, IPooledObject {
[SerializeField] private Animator animator;
[SerializeField] private Animation particleAnimation;
private void Start() {
animator.OnAnimationEnd += _ => Destroy(gameObject);
public void OnObjectSpawn() {
animator.ChangeAnimation(particleAnimation);
animator.OnAnimationEnd += _ => Pooler.Recycle(gameObject);
animator.PlayUntil(1f);
}
}

View file

@ -1,4 +1,5 @@
using System;
using Pooling;
using Presenter;
using UnityEngine;
using Random = UnityEngine.Random;
@ -9,9 +10,13 @@ namespace View.Scene {
[SerializeField] private Transform growParticlePositions;
[SerializeField, Range(0, 180)] private float angleRange;
private void Awake() {
growParticle.CreatePool();
}
public void OnInputReceived() {
float randomRotation = Random.Range(-angleRange / 2f, angleRange / 2f);
Instantiate(growParticle, growParticlePositions.position, Quaternion.Euler(0f, 0f, randomRotation));
growParticle.Spawn(growParticlePositions.position, Quaternion.Euler(0, 0, randomRotation));
}
private void OnDrawGizmosSelected() {

View file

@ -1,16 +0,0 @@
[LOG] System::create : Header version = 2.02.06. Current version = 2.02.06.
[LOG] Manager::init : maxchannels = 256 studioflags = 00000006 flags 00000000 extradriverdata 0000000000000000.
[LOG] SystemI::init : Initialize version=20206 (124257), maxchannels=256, flags=0x00020000
[LOG] SystemI::setOutputInternal : Setting output to 'FMOD WASAPI Output'
[LOG] OutputWASAPI::init : Mix Format (WAVEFORMATEX): wFormatTag=0xFFFE, nChannels=2, nSamplesPerSec=48000, nAvgBytesPerSec=384000, nBlockAlign=8, wBitsPerSample=32, cbSize=22.
[LOG] OutputWASAPI::init : Mix Format (WAVEFORMATEXTENSIBLE): wValidBitsPerSample=32, dwChannelMask=0x00000003, SubFormat=00000003-0000-0010-8000-00AA00389B71.
[LOG] OutputWASAPI::init : Output buffer size: 4096 samples, latency: 0.00ms, period: 10.67ms, DSP buffer: 1024 * 4
[LOG] Thread::initThread : Init FMOD stream thread. Affinity: 0x4000000000000003, Priority: 0xFFFF7FFB, Stack Size: 98304, Semaphore: No, Sleep Time: 10, Looping: Yes.
[LOG] Thread::initThread : Init FMOD mixer thread. Affinity: 0x4000000000000001, Priority: 0xFFFF7FFA, Stack Size: 81920, Semaphore: No, Sleep Time: 0, Looping: Yes.
[LOG] AsyncManager::init : manager 000002035DC49838 isAsync 0 updatePeriod 0.02
[LOG] AsyncManager::init : done
[LOG] PlaybackSystem::init :
[LOG] Thread::initThread : Init FMOD Studio sample load thread. Affinity: 0x4000000000000003, Priority: 0xFFFF7FFD, Stack Size: 98304, Semaphore: No, Sleep Time: 1, Looping: No.
[LOG] PlaybackSystem::init : done
[LOG] Thread::initThread : Init FMOD Studio bank load thread. Affinity: 0x4000000000000003, Priority: 0xFFFF7FFD, Stack Size: 98304, Semaphore: No, Sleep Time: 1, Looping: No.
[LOG] Manager::init : done.