com.amanotes.prefabpool 1.0.3 → 1.2.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.
- package/CHANGELOG.md +32 -8
- package/Docs/PrefabPool-API.md +1 -1
- package/README.md +1 -1
- package/Runtime/PrefabPool.cs +61 -18
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,21 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0] - 2026-05-20
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `PoolEntry.EnsureCapacity(int count, Transform poolParent)` — pre-fills pool to target count without coroutine overhead
|
|
7
|
+
- `PoolEntry.PoolCount` property — returns current pooled instance count
|
|
8
|
+
- `PoolGroup.FindEntry(string prefabId)` — O(n) linear search replacing LINQ `Find()` with fallback support
|
|
9
|
+
- `PoolGroup.IsGameplay` field — marks group for runtime instantiation warnings
|
|
10
|
+
- Runtime instantiation warning in Editor/Development builds when `IsGameplay` is true
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- `PoolEntry.Get()` / `Return()` uses per-instance `IPoolPrefab[]` cache (`_poolPrefabCache`) — eliminates `GetComponents<IPoolPrefab>()` GC per Get/Return call
|
|
14
|
+
- `PoolEntry.PreAllocate()` yields every 3 instances (`PreAllocBatchSize`) to spread across frames
|
|
15
|
+
- `PrefabPool.Get()` uses `PoolGroup.FindEntry()` instead of LINQ lambda
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- `PoolEntry.ReturnAll()` clears `_poolPrefabCache` to prevent stale references
|
|
19
|
+
|
|
20
|
+
## [1.1.0] - 2026-04-21
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- `IPrefabPool.MarkShutdown()` — marks pool as shut down so Get/Return calls no-op safely
|
|
24
|
+
- `PrefabPool.MarkShutdown()` implementation — sets `_isDestroyed = true`
|
|
25
|
+
|
|
3
26
|
## [1.0.3] - 2026-04-07
|
|
4
27
|
|
|
5
28
|
### Fixed
|
|
6
|
-
- `PoolEntry.Return
|
|
29
|
+
- `PoolEntry.Return` renames pooled GameObjects back to entry `id` in Editor
|
|
7
30
|
|
|
8
31
|
## [1.0.2] - 2026-04-06
|
|
9
32
|
|
|
10
33
|
### Fixed
|
|
11
|
-
- `PoolEntry.Return
|
|
12
|
-
- `PoolEntry.Get
|
|
13
|
-
- `PoolEntry.PreAllocate
|
|
14
|
-
- `PrefabPool.DestroyPool
|
|
15
|
-
- `PrefabPool.Initialize
|
|
16
|
-
- `PoolEntry.PurgeDestroyed
|
|
17
|
-
- `PrefabPool.PurgeStaleInstanceMap
|
|
34
|
+
- `PoolEntry.Return` deactivates before `SetParent` to prevent parent activation error
|
|
35
|
+
- `PoolEntry.Get` explicit `SetActive(true)` for consistent pool state
|
|
36
|
+
- `PoolEntry.PreAllocate` prewarm objects spawned inactive
|
|
37
|
+
- `PrefabPool.DestroyPool` properly destroys `_customPool` entries
|
|
38
|
+
- `PrefabPool.Initialize` added `_isInitializing` guard against double-prewarm
|
|
39
|
+
- `PoolEntry.PurgeDestroyed` removes stale refs on `Register`
|
|
40
|
+
- `PrefabPool.PurgeStaleInstanceMap` cleans up externally-destroyed objects (Editor only)
|
|
18
41
|
|
|
19
42
|
## [1.0.1]
|
|
20
43
|
|
|
44
|
+
### Added
|
|
21
45
|
- Initial release
|
package/Docs/PrefabPool-API.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# PrefabPool API Reference
|
|
2
2
|
|
|
3
|
-
**Package:** `com.amanotes.prefabpool` | **Version:** 1.0
|
|
3
|
+
**Package:** `com.amanotes.prefabpool` | **Version:** 1.2.0 | **Namespace:** `Amanotes.Core`
|
|
4
4
|
|
|
5
5
|
Generic object pooling system to eliminate garbage collection spikes from frequent instantiation/destruction.
|
|
6
6
|
|
package/README.md
CHANGED
package/Runtime/PrefabPool.cs
CHANGED
|
@@ -22,8 +22,24 @@ namespace Amanotes.Core
|
|
|
22
22
|
[NonSerialized] private readonly List<GameObject> _pool = new List<GameObject>();
|
|
23
23
|
[NonSerialized] private PoolGroup _parentGroup;
|
|
24
24
|
|
|
25
|
+
// Per-instance IPoolPrefab cache — components don't change on pooled prefabs
|
|
26
|
+
[NonSerialized] private readonly Dictionary<int, IPoolPrefab[]> _poolPrefabCache = new Dictionary<int, IPoolPrefab[]>(32);
|
|
27
|
+
[NonSerialized] private static readonly List<IPoolPrefab> _tempPoolPrefabs = new List<IPoolPrefab>(8);
|
|
28
|
+
|
|
25
29
|
public void SetParentGroup(PoolGroup parentGroup) => _parentGroup = parentGroup;
|
|
26
30
|
|
|
31
|
+
private IPoolPrefab[] GetCachedPoolPrefabs(GameObject go)
|
|
32
|
+
{
|
|
33
|
+
var instanceId = go.GetInstanceID();
|
|
34
|
+
if (_poolPrefabCache.TryGetValue(instanceId, out var cached)) return cached;
|
|
35
|
+
|
|
36
|
+
go.GetComponents(_tempPoolPrefabs);
|
|
37
|
+
cached = _tempPoolPrefabs.Count > 0 ? _tempPoolPrefabs.ToArray() : System.Array.Empty<IPoolPrefab>();
|
|
38
|
+
_tempPoolPrefabs.Clear();
|
|
39
|
+
_poolPrefabCache[instanceId] = cached;
|
|
40
|
+
return cached;
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
public GameObject Get(Transform poolParent, Transform spawnParent, Vector3 localPosition)
|
|
28
44
|
{
|
|
29
45
|
GameObject resultGameObject = null;
|
|
@@ -44,9 +60,12 @@ namespace Amanotes.Core
|
|
|
44
60
|
bool fromPool = resultGameObject != null;
|
|
45
61
|
if (resultGameObject == null)
|
|
46
62
|
{
|
|
47
|
-
// Create prefab inside the pool's transform
|
|
48
63
|
resultGameObject = UnityObject.Instantiate(prefab, poolParent.position, Quaternion.identity, poolParent);
|
|
49
64
|
resultGameObject.hideFlags = HideFlags.DontSave;
|
|
65
|
+
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
|
66
|
+
if (_parentGroup != null && _parentGroup.IsGameplay)
|
|
67
|
+
Debug.LogWarning($"[Pool] Runtime instantiate '{id}' — prewarm undercount");
|
|
68
|
+
#endif
|
|
50
69
|
}
|
|
51
70
|
|
|
52
71
|
var t = resultGameObject.transform;
|
|
@@ -55,11 +74,8 @@ namespace Amanotes.Core
|
|
|
55
74
|
t.localRotation = Quaternion.identity;
|
|
56
75
|
resultGameObject.SetActive(true);
|
|
57
76
|
|
|
58
|
-
var
|
|
59
|
-
|
|
60
|
-
{
|
|
61
|
-
pp.OnGet();
|
|
62
|
-
}
|
|
77
|
+
var prefabs = GetCachedPoolPrefabs(resultGameObject);
|
|
78
|
+
for (var i = 0; i < prefabs.Length; i++) prefabs[i].OnGet();
|
|
63
79
|
|
|
64
80
|
return resultGameObject;
|
|
65
81
|
}
|
|
@@ -68,11 +84,8 @@ namespace Amanotes.Core
|
|
|
68
84
|
{
|
|
69
85
|
if (gameObject == null) return;
|
|
70
86
|
|
|
71
|
-
var
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
pp.OnReturn();
|
|
75
|
-
}
|
|
87
|
+
var prefabs = GetCachedPoolPrefabs(gameObject);
|
|
88
|
+
for (var i = 0; i < prefabs.Length; i++) prefabs[i].OnReturn();
|
|
76
89
|
|
|
77
90
|
var t = gameObject.transform;
|
|
78
91
|
gameObject.SetActive(false);
|
|
@@ -94,10 +107,26 @@ namespace Amanotes.Core
|
|
|
94
107
|
}
|
|
95
108
|
}
|
|
96
109
|
_pool.Clear();
|
|
110
|
+
_poolPrefabCache.Clear();
|
|
97
111
|
}
|
|
98
112
|
|
|
99
113
|
public void PurgeDestroyed() => _pool.RemoveAll(obj => obj == null);
|
|
100
114
|
|
|
115
|
+
public int PoolCount => _pool.Count;
|
|
116
|
+
|
|
117
|
+
public void EnsureCapacity(int count, Transform poolParent)
|
|
118
|
+
{
|
|
119
|
+
while (_pool.Count < count)
|
|
120
|
+
{
|
|
121
|
+
var instance = UnityObject.Instantiate(prefab, poolParent.position, Quaternion.identity, poolParent);
|
|
122
|
+
instance.hideFlags = HideFlags.DontSave;
|
|
123
|
+
instance.SetActive(false);
|
|
124
|
+
_pool.Add(instance);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private const int PreAllocBatchSize = 3;
|
|
129
|
+
|
|
101
130
|
public IEnumerator PreAllocate(Transform poolParent)
|
|
102
131
|
{
|
|
103
132
|
for (var i = 0; i < prewarmCount; i++)
|
|
@@ -106,6 +135,9 @@ namespace Amanotes.Core
|
|
|
106
135
|
instance.hideFlags = HideFlags.DontSave;
|
|
107
136
|
instance.SetActive(false);
|
|
108
137
|
_pool.Add(instance);
|
|
138
|
+
|
|
139
|
+
if ((i + 1) % PreAllocBatchSize == 0)
|
|
140
|
+
yield return null;
|
|
109
141
|
}
|
|
110
142
|
|
|
111
143
|
yield return null;
|
|
@@ -120,6 +152,7 @@ namespace Amanotes.Core
|
|
|
120
152
|
|
|
121
153
|
[NonSerialized] private IPrefabPool _prefabPool;
|
|
122
154
|
[NonSerialized] private Transform _spawnParent;
|
|
155
|
+
[NonSerialized] public bool IsGameplay;
|
|
123
156
|
|
|
124
157
|
public void Register(IPrefabPool prefabPool, Transform spawnParent)
|
|
125
158
|
{
|
|
@@ -144,6 +177,13 @@ namespace Amanotes.Core
|
|
|
144
177
|
return _prefabPool?.Get(this, prefabId, localPosition, customParent);
|
|
145
178
|
}
|
|
146
179
|
|
|
180
|
+
public PoolEntry FindEntry(string prefabId)
|
|
181
|
+
{
|
|
182
|
+
for (var i = 0; i < entries.Count; i++)
|
|
183
|
+
if (entries[i].id == prefabId) return entries[i];
|
|
184
|
+
return fallbackEntry;
|
|
185
|
+
}
|
|
186
|
+
|
|
147
187
|
public T Get<T>(string prefabId, Vector3 localPosition = default) where T : class
|
|
148
188
|
{
|
|
149
189
|
var go = Get(prefabId, localPosition);
|
|
@@ -168,6 +208,12 @@ namespace Amanotes.Core
|
|
|
168
208
|
GameObject Get(PoolGroup group, string prefabId, Vector3 localPosition = default, Transform customParent = null);
|
|
169
209
|
void Return(PoolGroup group, GameObject gameObject);
|
|
170
210
|
IEnumerator Initialize();
|
|
211
|
+
/// <summary>
|
|
212
|
+
/// Marks the pool as shut down so all subsequent Get/Return calls no-op safely.
|
|
213
|
+
/// Call this before detaching tiles during game shutdown to prevent SetParent errors
|
|
214
|
+
/// when the pool's hierarchy is being deactivated.
|
|
215
|
+
/// </summary>
|
|
216
|
+
void MarkShutdown();
|
|
171
217
|
}
|
|
172
218
|
|
|
173
219
|
public static class PrefabPoolExtensions
|
|
@@ -308,6 +354,8 @@ namespace Amanotes.Core
|
|
|
308
354
|
|
|
309
355
|
public bool IsReady => _isReady;
|
|
310
356
|
|
|
357
|
+
public void MarkShutdown() => _isDestroyed = true;
|
|
358
|
+
|
|
311
359
|
public void RegisterGroup(PoolGroup group, Transform spawnParent)
|
|
312
360
|
{
|
|
313
361
|
if (group == null) return;
|
|
@@ -330,13 +378,8 @@ namespace Amanotes.Core
|
|
|
330
378
|
if (_isDestroyed || group == null) return null;
|
|
331
379
|
if (string.IsNullOrEmpty(prefabId)) return null;
|
|
332
380
|
|
|
333
|
-
var entry = group.
|
|
334
|
-
if (entry == null)
|
|
335
|
-
{
|
|
336
|
-
// Try fallback entry if available
|
|
337
|
-
entry = group.fallbackEntry;
|
|
338
|
-
if (entry == null) return null;
|
|
339
|
-
}
|
|
381
|
+
var entry = group.FindEntry(prefabId);
|
|
382
|
+
if (entry == null) return null;
|
|
340
383
|
|
|
341
384
|
var p = customParent == null ? group.SpawnParent : customParent;
|
|
342
385
|
var result = entry.Get(transform, p, localPosition);
|
package/package.json
CHANGED