com.amanotes.prefabpool 1.1.0 → 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 +17 -0
- package/Docs/PrefabPool-API.md +1 -1
- package/README.md +1 -1
- package/Runtime/PrefabPool.cs +53 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
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
|
+
|
|
3
20
|
## [1.1.0] - 2026-04-21
|
|
4
21
|
|
|
5
22
|
### Added
|
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);
|
|
@@ -338,13 +378,8 @@ namespace Amanotes.Core
|
|
|
338
378
|
if (_isDestroyed || group == null) return null;
|
|
339
379
|
if (string.IsNullOrEmpty(prefabId)) return null;
|
|
340
380
|
|
|
341
|
-
var entry = group.
|
|
342
|
-
if (entry == null)
|
|
343
|
-
{
|
|
344
|
-
// Try fallback entry if available
|
|
345
|
-
entry = group.fallbackEntry;
|
|
346
|
-
if (entry == null) return null;
|
|
347
|
-
}
|
|
381
|
+
var entry = group.FindEntry(prefabId);
|
|
382
|
+
if (entry == null) return null;
|
|
348
383
|
|
|
349
384
|
var p = customParent == null ? group.SpawnParent : customParent;
|
|
350
385
|
var result = entry.Get(transform, p, localPosition);
|