com.amanotes.prefabpool 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,43 @@
1
+ #if UNITY_EDITOR
2
+
3
+ using UnityEngine;
4
+ using UnityEditor;
5
+
6
+ namespace Amanotes.Core
7
+ {
8
+ [CustomPropertyDrawer(typeof(PoolEntry))]
9
+ internal class PoolEntryDrawer : PropertyDrawer
10
+ {
11
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
12
+ {
13
+ EditorGUI.BeginProperty(position, label, property);
14
+
15
+ var tileNameProp = property.FindPropertyRelative(nameof(PoolEntry.id));
16
+ var tilePrefabProp = property.FindPropertyRelative(nameof(PoolEntry.prefab));
17
+ var prewarmProp = property.FindPropertyRelative(nameof(PoolEntry.prewarmCount));
18
+
19
+ var thirdWidth = position.width / 3f - 2f;
20
+ var nameRect = new Rect(position.x, position.y, thirdWidth, EditorGUIUtility.singleLineHeight);
21
+ var prefabRect = new Rect(position.x + thirdWidth + 3f, position.y, thirdWidth, EditorGUIUtility.singleLineHeight);
22
+ var sliderRect = new Rect(position.x + (thirdWidth + 3f) * 2, position.y, thirdWidth, EditorGUIUtility.singleLineHeight);
23
+
24
+ EditorGUI.PropertyField(nameRect, tileNameProp, GUIContent.none);
25
+ EditorGUI.PropertyField(prefabRect, tilePrefabProp, GUIContent.none);
26
+
27
+ EditorGUI.BeginChangeCheck();
28
+ var sliderValue = EditorGUI.IntSlider(sliderRect, prewarmProp.intValue, 0, 10);
29
+ if (EditorGUI.EndChangeCheck())
30
+ {
31
+ prewarmProp.intValue = sliderValue;
32
+ }
33
+
34
+ EditorGUI.EndProperty();
35
+ }
36
+
37
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
38
+ {
39
+ return EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
40
+ }
41
+ }
42
+ }
43
+ #endif
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # Prefab Pool
2
+
3
+ Generic object pooling system for Unity.
4
+
5
+ ## Features
6
+
7
+ - ✅ Simple API for Get/Return pattern
8
+ - ✅ Automatic prefab instantiation
9
+ - ✅ `IPoolPrefab` interface for lifecycle callbacks
10
+ - ✅ Pre-warming support
11
+ - ✅ Material pooling support
12
+ - ✅ No dependencies on other packages
13
+
14
+ ## Installation
15
+
16
+ Add to your `manifest.json`:
17
+ ```json
18
+ {
19
+ "dependencies": {
20
+ "com.amanotes.prefabpool": "1.0.0"
21
+ }
22
+ }
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```csharp
28
+ using Amanotes.PrefabPool;
29
+
30
+ public class Example : MonoBehaviour
31
+ {
32
+ void Start()
33
+ {
34
+ var pool = new PrefabPool();
35
+ var group = CreatePoolGroup();
36
+ pool.RegisterGroup(group, transform);
37
+
38
+ // Get object from pool
39
+ var obj = pool.Get<SpriteRenderer>("enemy");
40
+
41
+ // Return to pool when done
42
+ pool.Return(obj.gameObject);
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ### Implement IPoolPrefab
50
+
51
+ ```csharp
52
+ public class Enemy : MonoBehaviour, IPoolPrefab
53
+ {
54
+ public void OnGet()
55
+ {
56
+ // Called when retrieved from pool
57
+ gameObject.SetActive(true);
58
+ ResetState();
59
+ }
60
+
61
+ public void OnReturn()
62
+ {
63
+ // Called when returned to pool
64
+ gameObject.SetActive(false);
65
+ }
66
+ }
67
+ ```
68
+
69
+ ### Create Pool Configuration
70
+
71
+ ```csharp
72
+ var poolGroup = ScriptableObject.CreateInstance<PoolGroup>();
73
+ poolGroup.name = "Enemies";
74
+ poolGroup.entries = new List<PoolEntry>
75
+ {
76
+ new PoolEntry
77
+ {
78
+ id = "enemy",
79
+ prefab = enemyPrefab,
80
+ prewarmCount = 10
81
+ }
82
+ };
83
+ ```
84
+
85
+ ## API Reference
86
+
87
+ ### IPrefabPool Interface
88
+
89
+ ```csharp
90
+ void RegisterGroup(PoolGroup group, Transform parent);
91
+ T Get<T>(string type) where T : Component;
92
+ void Return(GameObject obj);
93
+ IEnumerator Initialize();
94
+ ```
95
+
96
+ ### IPoolPrefab Interface
97
+
98
+ ```csharp
99
+ void OnGet(); // Called when retrieved
100
+ void OnReturn(); // Called when returned
101
+ ```
102
+
103
+ ## License
104
+
105
+ Copyright © Amanotes
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "Amanotes.PrefabPool",
3
+ "rootNamespace": "Amanotes.Core",
4
+ "references": [],
5
+ "includePlatforms": [],
6
+ "excludePlatforms": [],
7
+ "allowUnsafeCode": false,
8
+ "overrideReferences": false,
9
+ "precompiledReferences": [],
10
+ "autoReferenced": true,
11
+ "defineConstraints": [],
12
+ "versionDefines": [],
13
+ "noEngineReferences": false
14
+ }
@@ -0,0 +1,105 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using UnityEngine;
4
+ using UnityObject = UnityEngine.Object;
5
+
6
+ namespace Amanotes.Core
7
+ {
8
+
9
+ public static class MaterialExtensions
10
+ {
11
+ public static void SetAlpha(this Material m, float a)
12
+ {
13
+ if (m == null) return;
14
+ var isCloned = (m.hideFlags & HideFlags.DontSave) != 0;
15
+ if (!isCloned) Debug.LogWarning($"Modify material directly <{m}>!");
16
+
17
+ var c = m.color;
18
+ c.a = a;
19
+ m.color = c;
20
+ }
21
+ }
22
+
23
+
24
+ public partial class MaterialPool
25
+ {
26
+ [NonSerialized] private static readonly Dictionary<int, MaterialPool> _map = new Dictionary<int, MaterialPool>();
27
+ [NonSerialized] private static readonly Dictionary<int, MaterialPool> _cloned = new Dictionary<int, MaterialPool>();
28
+
29
+ private static MaterialPool GetPool(Material sourceOrClone)
30
+ {
31
+ if (sourceOrClone == null) return null;
32
+ var instId = sourceOrClone.GetInstanceID();
33
+ var isCloned = sourceOrClone.hideFlags == HideFlags.DontSave;
34
+ return isCloned
35
+ ? _cloned.GetValueOrDefault(instId)
36
+ : _map.GetValueOrDefault(instId);
37
+ }
38
+
39
+ public static Material Take(Material sourceOrClone, int borrowId)
40
+ {
41
+ if (sourceOrClone == null) return null;
42
+ var pool = GetPool(sourceOrClone) ?? new MaterialPool(sourceOrClone);
43
+ return pool.InternalTake(borrowId);
44
+ }
45
+
46
+ public static void Return(int borrowId)
47
+ {
48
+ foreach (var kvp in _map)
49
+ {
50
+ kvp.Value.InternalReturn(borrowId);
51
+ }
52
+ }
53
+ }
54
+
55
+ public partial class MaterialPool
56
+ {
57
+ private readonly Material original;
58
+ private readonly List<Material> pool = new List<Material>();
59
+ private readonly Dictionary<int, Material> borrowed = new Dictionary<int, Material>();
60
+
61
+ private MaterialPool(Material source)
62
+ {
63
+ original = source;
64
+ _map[original.GetInstanceID()] = this;
65
+
66
+ if (source.hideFlags == HideFlags.DontSave)
67
+ {
68
+ Debug.LogWarning($"Invalid source hideFlags: {source.name} : {source.hideFlags}");
69
+ }
70
+ }
71
+
72
+ // public bool IsOriginalColor(Color c) => original.color == c;
73
+ // public bool IsOriginalAlpha(float a) => Mathf.Approximately(original.color.a, a);
74
+
75
+ private Material GetOne()
76
+ {
77
+ Material result;
78
+ if (pool.Count <= 0)
79
+ {
80
+ result = new Material(original) { hideFlags = HideFlags.DontSave };
81
+ _cloned[result.GetInstanceID()] = this;
82
+ return result;
83
+ }
84
+
85
+ result = pool[pool.Count - 1];
86
+ pool.RemoveAt(pool.Count - 1);
87
+ return result;
88
+ }
89
+
90
+ private Material InternalTake(int borrowId)
91
+ {
92
+ if (borrowed.TryGetValue(borrowId, out var mat)) return mat;
93
+
94
+ var material = GetOne();
95
+ material.name = $"{original.name}_{borrowId}";
96
+ borrowed[borrowId] = material;
97
+ return material;
98
+ }
99
+
100
+ private void InternalReturn(int borrowId)
101
+ {
102
+ if (borrowed.Remove(borrowId, out var mat)) pool.Add(mat);
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,80 @@
1
+ using System.Collections.Generic;
2
+ using UnityEditor;
3
+ using UnityEngine;
4
+
5
+ namespace Amanotes.Core
6
+ {
7
+ public static class MeshRendererExtensions
8
+ {
9
+ public static void SetAlpha(this List<SpriteRenderer> sprites, float value)
10
+ {
11
+ for (var i = 0; i < sprites.Count; i++)
12
+ {
13
+ var sprt = sprites[i];
14
+ if (sprt == null) continue;
15
+
16
+ var c = sprt.color;
17
+ c.a = value;
18
+ sprt.color = c;
19
+ }
20
+ }
21
+
22
+
23
+ public static void SetColor(this List<MeshRenderer> renderers, Color c, bool keepAlpha = true)
24
+ {
25
+ for (var i = 0; i < renderers.Count; i++)
26
+ {
27
+ renderers[i].SetColor(c, keepAlpha);
28
+ }
29
+ }
30
+
31
+ public static void SetColor(this List<SpriteRenderer> sprites, Color c, bool keepAlpha = true)
32
+ {
33
+ for (var i = 0; i < sprites.Count; i++)
34
+ {
35
+ sprites[i].SetColor(c, keepAlpha);
36
+ }
37
+ }
38
+
39
+ public static void SetColor(this MeshRenderer renderer, Color c, bool keepAlpha = true)
40
+ {
41
+ if (renderer == null) return;
42
+ var m = renderer.sharedMaterial;
43
+ if (m == null) return;
44
+
45
+ if (keepAlpha) c.a = m.color.a;
46
+ renderer.sharedMaterial.color = c;
47
+ }
48
+
49
+ public static void SetColor(this SpriteRenderer sprt, Color c, bool keepAlpha = true)
50
+ {
51
+ if (sprt == null) return;
52
+ if (keepAlpha) c.a = sprt.color.a;
53
+ sprt.color = c;
54
+ }
55
+
56
+ public static void SetAlpha(this SpriteRenderer sprt, float alpha)
57
+ {
58
+ if (sprt == null) return;
59
+ var c = sprt.color;
60
+ c.a = sprt.color.a;
61
+ sprt.color = c;
62
+ }
63
+
64
+ public static void CloneAndSetAlpha(this MeshRenderer renderer, float alpha)
65
+ {
66
+ // Prevent accidentally changes to prefab's material
67
+ #if UNITY_EDITOR
68
+ if (PrefabUtility.IsPartOfPrefabAsset(renderer)) return;
69
+ if (!Application.isPlaying) return;
70
+ #endif
71
+ if (renderer == null) return;
72
+
73
+ var oMat = renderer.sharedMaterial;
74
+ var cMat = MaterialPool.Take(oMat, renderer.GetInstanceID());
75
+ renderer.sharedMaterial = cMat;
76
+
77
+ cMat.SetAlpha(alpha);
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,413 @@
1
+ using System;
2
+ using System.Collections;
3
+ using System.Collections.Generic;
4
+ using UnityEditor;
5
+ using UnityEngine;
6
+ using UnityObject = UnityEngine.Object;
7
+
8
+ namespace Amanotes.Core
9
+ {
10
+ public interface IPoolPrefab
11
+ {
12
+ void OnGet();
13
+ void OnReturn();
14
+ }
15
+
16
+ [Serializable] public class PoolEntry
17
+ {
18
+ public string id;
19
+ public GameObject prefab;
20
+ public int prewarmCount;
21
+
22
+ [NonSerialized] private readonly List<GameObject> _pool = new List<GameObject>();
23
+ [NonSerialized] private PoolGroup _parentGroup;
24
+
25
+ public void SetParentGroup(PoolGroup parentGroup) => _parentGroup = parentGroup;
26
+
27
+ public GameObject Get(Transform poolParent, Transform spawnParent, Vector3 localPosition)
28
+ {
29
+ GameObject resultGameObject = null;
30
+
31
+ if (prefab == null)
32
+ {
33
+ Debug.LogWarning($"Prefab pool object is null. <{id}>");
34
+ return null;
35
+ }
36
+
37
+ if (_pool.Count > 0)
38
+ {
39
+ var lastIndex = _pool.Count - 1;
40
+ resultGameObject = _pool[lastIndex];
41
+ _pool.RemoveAt(lastIndex);
42
+ }
43
+
44
+ if (resultGameObject == null)
45
+ {
46
+ // Create prefab inside the pool's transform
47
+ resultGameObject = UnityObject.Instantiate(prefab, poolParent.position, Quaternion.identity, poolParent);
48
+ resultGameObject.hideFlags = HideFlags.DontSave;
49
+ }
50
+
51
+ // Move to spawn parent when retrieved
52
+ var t = resultGameObject.transform;
53
+ t.SetParent(spawnParent, false);
54
+ t.localPosition = localPosition;
55
+ t.localRotation = Quaternion.identity;
56
+
57
+ // Call OnGet on any IPoolPrefab components
58
+ var poolComponents = resultGameObject.GetComponents<IPoolPrefab>();
59
+ foreach (var pp in poolComponents)
60
+ {
61
+ pp.OnGet();
62
+ }
63
+
64
+ return resultGameObject;
65
+ }
66
+
67
+ public void Return(GameObject gameObject, Transform poolParent)
68
+ {
69
+ if (gameObject == null) return;
70
+
71
+ // Call OnReturn on any IPoolPrefab components
72
+ var poolComponents = gameObject.GetComponents<IPoolPrefab>();
73
+ foreach (var pp in poolComponents)
74
+ {
75
+ pp.OnReturn();
76
+ }
77
+
78
+ var t = gameObject.transform;
79
+ t.SetParent(poolParent, false);
80
+ t.localPosition = Vector3.zero;
81
+ _pool.Add(gameObject);
82
+ }
83
+
84
+ public void DestroyPool()
85
+ {
86
+ for (var i = 0; i < _pool.Count; i++)
87
+ {
88
+ if (_pool[i] != null)
89
+ {
90
+ UnityObject.Destroy(_pool[i]);
91
+ }
92
+ }
93
+ _pool.Clear();
94
+ }
95
+
96
+ public IEnumerator PreAllocate(Transform poolParent)
97
+ {
98
+ for (var i = 0; i < prewarmCount; i++)
99
+ {
100
+ var instance = UnityObject.Instantiate(prefab, poolParent.position, Quaternion.identity, poolParent);
101
+ instance.hideFlags = HideFlags.DontSave;
102
+ _pool.Add(instance);
103
+ }
104
+
105
+ yield return null;
106
+ }
107
+ }
108
+
109
+ [Serializable] public class PoolGroup
110
+ {
111
+ public string id;
112
+ public List<PoolEntry> entries = new List<PoolEntry>();
113
+ public PoolEntry fallbackEntry;
114
+
115
+ [NonSerialized] private IPrefabPool _prefabPool;
116
+ [NonSerialized] private Transform _spawnParent;
117
+
118
+ public void Register(IPrefabPool prefabPool, Transform spawnParent)
119
+ {
120
+ _prefabPool = prefabPool;
121
+ _spawnParent = spawnParent;
122
+
123
+ // Set parent group reference for each entry
124
+ foreach (var entry in entries)
125
+ {
126
+ entry.SetParentGroup(this);
127
+ }
128
+
129
+ // Set parent group reference for fallback entry
130
+ if (fallbackEntry != null)
131
+ {
132
+ fallbackEntry.SetParentGroup(this);
133
+ }
134
+ }
135
+
136
+ public GameObject Get(string prefabId, Vector3 localPosition = default, Transform customParent = null)
137
+ {
138
+ return _prefabPool?.Get(this, prefabId, localPosition, customParent);
139
+ }
140
+
141
+ public T Get<T>(string prefabId, Vector3 localPosition = default) where T : class
142
+ {
143
+ var go = Get(prefabId, localPosition);
144
+ if (go == null) return default(T);
145
+ var c = go.GetComponent<T>();
146
+ return c ?? go.GetComponentWithInterface<T>();
147
+ }
148
+
149
+ public void Return(GameObject gameObject)
150
+ {
151
+ _prefabPool?.Return(this, gameObject);
152
+ }
153
+
154
+ public Transform SpawnParent => _spawnParent;
155
+ }
156
+
157
+ public interface IPrefabPool
158
+ {
159
+ bool IsReady { get; }
160
+ void RegisterGroup(PoolGroup group, Transform spawnParent);
161
+ void SetSpawnParent(string groupId, Transform spawnParent);
162
+ GameObject Get(PoolGroup group, string prefabId, Vector3 localPosition = default, Transform customParent = null);
163
+ void Return(PoolGroup group, GameObject gameObject);
164
+ IEnumerator Initialize();
165
+ }
166
+
167
+ public static class PrefabPoolExtensions
168
+ {
169
+ public static void DestroyLast<T>(this List<T> list) where T : Component
170
+ {
171
+ if (list.Count == 0) return;
172
+ var last = list[list.Count - 1];
173
+ list.RemoveAt(list.Count-1);
174
+ if (last == null) return;
175
+
176
+ if (Application.isPlaying)
177
+ {
178
+ UnityObject.Destroy(last.gameObject);
179
+ }
180
+ else
181
+ {
182
+ UnityObject.DestroyImmediate(last.gameObject);
183
+ }
184
+ }
185
+
186
+ public static T ClonePrefab<T>(this GameObject prefab, Transform parent) where T : Component
187
+ {
188
+ #if UNITY_EDITOR
189
+ var c = Application.isPlaying
190
+ ? UnityObject.Instantiate(prefab, parent.position, Quaternion.identity, parent)
191
+ : (GameObject)PrefabUtility.InstantiatePrefab(prefab, parent);
192
+ #else
193
+ var c = UnityObject.Instantiate(prefab, parent.position, Quaternion.identity, parent);
194
+ #endif
195
+ return c.GetComponent<T>();
196
+ }
197
+
198
+ public static void ResetAndClear<T>(this List<T> list, GameObject prefab, PrefabPool pool = null)
199
+ where T : Component
200
+ {
201
+ var usePool = pool != null;
202
+
203
+ #if UNITY_EDITOR
204
+ if (!Application.isPlaying) usePool = false;
205
+ #endif
206
+ if (usePool)
207
+ {
208
+ pool.Resize<T>(prefab, list, 0, null);
209
+ }
210
+ else
211
+ {
212
+ while (list.Count > 0) list.DestroyLast();
213
+ }
214
+ }
215
+
216
+ public static void Resize<T>(this List<T> list, int count, GameObject prefab, Transform parent, PrefabPool pool) where T : Component
217
+ {
218
+ var usePool = pool != null;
219
+
220
+ #if UNITY_EDITOR
221
+ if (!Application.isPlaying) usePool = false;
222
+ #endif
223
+ if (usePool)
224
+ {
225
+ pool.Resize<T>(prefab, list, count, parent);
226
+ }
227
+ else
228
+ {
229
+ while (list.Count > count) list.DestroyLast();
230
+ while (list.Count < count)
231
+ {
232
+ list.Add(prefab.ClonePrefab<T>(parent));
233
+ }
234
+
235
+ for (var i = 0; i < list.Count; i++)
236
+ {
237
+ if (list[i] != null) continue;
238
+ list[i] = prefab.ClonePrefab<T>(parent);
239
+ }
240
+ }
241
+ }
242
+ }
243
+
244
+
245
+
246
+ public class PrefabPool : MonoBehaviour, IPrefabPool
247
+ {
248
+ [NonSerialized] private bool _isDestroyed = false;
249
+ [NonSerialized] private bool _isReady = false;
250
+ [NonSerialized] private readonly Dictionary<string, PoolGroup> _groups = new Dictionary<string, PoolGroup>();
251
+ [NonSerialized] private readonly Dictionary<int, PoolEntry> _instanceTypeMap = new Dictionary<int, PoolEntry>();
252
+ [NonSerialized] private readonly Dictionary<GameObject, PoolEntry> _customPool = new Dictionary<GameObject, PoolEntry>();
253
+
254
+ public void Resize<T>(GameObject prefab, List<T> list, int count, Transform parent) where T: Component
255
+ {
256
+ if (_customPool == null)
257
+ {
258
+ Debug.LogWarning("Something wrong: _customPool is null!");
259
+ return;
260
+ }
261
+
262
+ if (prefab == null)
263
+ {
264
+ Debug.LogWarning($"Prefab is null??? {prefab}");
265
+ return;
266
+ }
267
+
268
+ if (!_customPool.TryGetValue(prefab, out var entry))
269
+ {
270
+ entry = new PoolEntry()
271
+ {
272
+ id = prefab.name,
273
+ prefab = prefab
274
+ };
275
+ _customPool[prefab] = entry;
276
+ }
277
+
278
+ while (list.Count > count)
279
+ {
280
+ var last = list[list.Count - 1];
281
+ if (last != null) entry.Return(last.gameObject, transform);
282
+ list.RemoveAt(list.Count - 1);
283
+ }
284
+
285
+ while (list.Count < count)
286
+ {
287
+ var c = entry.Get(transform, parent, Vector3.zero);
288
+ list.Add(c.GetComponent<T>());
289
+ }
290
+ }
291
+
292
+ public bool IsReady => _isReady;
293
+
294
+ public void RegisterGroup(PoolGroup group, Transform spawnParent)
295
+ {
296
+ if (group == null) return;
297
+
298
+ _groups[group.id] = group;
299
+ group.Register(this, spawnParent);
300
+ }
301
+
302
+ public void SetSpawnParent(string groupId, Transform spawnParent)
303
+ {
304
+ if (_groups.TryGetValue(groupId, out var group))
305
+ {
306
+ group.Register(this, spawnParent);
307
+ }
308
+ }
309
+
310
+ public GameObject Get(PoolGroup group, string prefabId, Vector3 localPosition = default, Transform customParent = null)
311
+ {
312
+ if (_isDestroyed || group == null) return null;
313
+ if (string.IsNullOrEmpty(prefabId)) return null;
314
+
315
+ var entry = group.entries.Find(e => e.id == prefabId);
316
+ if (entry == null)
317
+ {
318
+ // Try fallback entry if available
319
+ entry = group.fallbackEntry;
320
+ if (entry == null) return null;
321
+ }
322
+
323
+ var p = customParent == null ? group.SpawnParent : customParent;
324
+ var result = entry.Get(transform, p, localPosition);
325
+ if (result != null)
326
+ {
327
+ _instanceTypeMap[result.GetInstanceID()] = entry;
328
+ }
329
+
330
+ return result;
331
+ }
332
+
333
+ public void Return(PoolGroup group, GameObject go)
334
+ {
335
+ if (_isDestroyed || group == null || go == null) return;
336
+
337
+ var instanceId = go.GetInstanceID();
338
+ if (_instanceTypeMap.TryGetValue(instanceId, out var entry))
339
+ {
340
+ entry.Return(go, transform);
341
+ _instanceTypeMap.Remove(instanceId);
342
+ }
343
+ }
344
+
345
+ public IEnumerator Initialize()
346
+ {
347
+ if (_isReady) yield break;
348
+ _isReady = false;
349
+
350
+ #if UNITY_EDITOR
351
+ EditorApplication.playModeStateChanged -= HandlePlayModeChanged;
352
+ EditorApplication.playModeStateChanged += HandlePlayModeChanged;
353
+ #endif
354
+
355
+ foreach (var group in _groups.Values)
356
+ {
357
+ foreach (var entry in group.entries)
358
+ {
359
+ if (entry.prefab == null) continue;
360
+ yield return entry.PreAllocate(transform);
361
+ }
362
+
363
+ // Pre-allocate fallback entry if available
364
+ if (group.fallbackEntry != null && group.fallbackEntry.prefab != null)
365
+ {
366
+ yield return group.fallbackEntry.PreAllocate(transform);
367
+ }
368
+ }
369
+
370
+ _isReady = true;
371
+ }
372
+
373
+ #if UNITY_EDITOR
374
+ protected void HandlePlayModeChanged(PlayModeStateChange changed)
375
+ {
376
+ if (changed == PlayModeStateChange.ExitingPlayMode)
377
+ {
378
+ DestroyPool();
379
+ }
380
+ }
381
+ #endif
382
+
383
+ public void DestroyPool()
384
+ {
385
+ _isDestroyed = true;
386
+
387
+ foreach (var group in _groups.Values)
388
+ {
389
+ foreach (var entry in group.entries)
390
+ {
391
+ entry.DestroyPool();
392
+ }
393
+
394
+ // Destroy fallback entry if available
395
+ if (group.fallbackEntry != null)
396
+ {
397
+ group.fallbackEntry.DestroyPool();
398
+ }
399
+ }
400
+
401
+ _groups.Clear();
402
+
403
+ #if UNITY_EDITOR
404
+ foreach (var kvp in _instanceTypeMap)
405
+ {
406
+ var obj = EditorUtility.InstanceIDToObject(kvp.Key);
407
+ if (obj != null) UnityObject.DestroyImmediate(obj);
408
+ }
409
+ _instanceTypeMap.Clear();
410
+ #endif
411
+ }
412
+ }
413
+ }
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "com.amanotes.prefabpool",
3
+ "version": "1.0.0",
4
+ "displayName": "Amanotes Prefab Pool",
5
+ "description": "Generic object pooling system for Unity",
6
+ "unity": "2022.3",
7
+ "keywords": [
8
+ "pooling",
9
+ "object-pool",
10
+ "performance",
11
+ "optimization"
12
+ ],
13
+ "author": {
14
+ "name": "Amanotes",
15
+ "url": "https://amanotes.com"
16
+ }
17
+ }