com.amanotes.prefabpool 1.0.1 → 1.0.2

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 ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## [1.0.2] - 2026-04-06
4
+
5
+ ### Fixed
6
+ - `PoolEntry.Return`: deactivate GameObject before `SetParent` to prevent `Cannot set the parent of the GameObject while activating or deactivating the parent GameObject` error during scene shutdown
7
+ - `PoolEntry.Get`: explicit `SetActive(true)` to match — objects are now consistently inactive in pool, active when in use
8
+ - `PoolEntry.PreAllocate`: prewarm objects are now spawned inactive, consistent with the return state
9
+ - `PrefabPool.DestroyPool`: `_customPool` (Resize<T>) entries are now properly destroyed on shutdown
10
+ - `PrefabPool.Initialize`: added `_isInitializing` guard to prevent double-prewarm if called concurrently
11
+ - `PoolEntry.PurgeDestroyed`: new method, called on `Register` to remove stale destroyed-object refs when a PoolGroup ScriptableObject is reused across scene loads
12
+ - `PrefabPool.PurgeStaleInstanceMap`: cleans up `_instanceTypeMap` entries for externally-destroyed objects (Editor only)
13
+
14
+ ## [1.0.1]
15
+
16
+ - Initial release
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: 9d04aecc1aeab42d8b4f2c41cd72ccd5
3
+ TextScriptImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
@@ -0,0 +1,366 @@
1
+ # PrefabPool API Reference
2
+
3
+ **Package:** `com.amanotes.prefabpool` | **Version:** 1.0.1 | **Namespace:** `Amanotes.Core`
4
+
5
+ Generic object pooling system to eliminate garbage collection spikes from frequent instantiation/destruction.
6
+
7
+ ---
8
+
9
+ ## Core Interfaces
10
+
11
+ ### `IPrefabPool`
12
+
13
+ Main interface for object pool management.
14
+
15
+ ```csharp
16
+ public interface IPrefabPool
17
+ {
18
+ bool IsReady { get; } // Pool initialization status
19
+
20
+ void RegisterGroup(PoolGroup group, Transform spawnParent);
21
+ void SetSpawnParent(string groupId, Transform spawnParent);
22
+ GameObject Get(PoolGroup group, string prefabId, Vector3 localPosition = default, Transform customParent = null);
23
+ void Return(PoolGroup group, GameObject gameObject);
24
+ IEnumerator Initialize(); // Pre-allocate pooled objects
25
+ }
26
+ ```
27
+
28
+ **Example:**
29
+ ```csharp
30
+ // Setup pool
31
+ var pool = gameObject.AddComponent<PrefabPool>();
32
+ var group = new PoolGroup
33
+ {
34
+ id = "enemies",
35
+ entries = new List<PoolEntry>
36
+ {
37
+ new PoolEntry { id = "basic", prefab = enemyPrefab, prewarmCount = 10 }
38
+ }
39
+ };
40
+ pool.RegisterGroup(group, transform);
41
+ StartCoroutine(pool.Initialize());
42
+
43
+ // Use pool
44
+ var enemy = pool.Get(group, "basic");
45
+ pool.Return(group, enemy);
46
+ ```
47
+
48
+ ---
49
+
50
+ ### `IPoolPrefab`
51
+
52
+ Lifecycle callback interface for pooled objects.
53
+
54
+ ```csharp
55
+ public interface IPoolPrefab
56
+ {
57
+ void OnGet(); // Called when retrieved from pool
58
+ void OnReturn(); // Called when returned to pool
59
+ }
60
+ ```
61
+
62
+ **Implementation:**
63
+ ```csharp
64
+ public class PooledTile : MonoBehaviour, IPoolPrefab
65
+ {
66
+ public void OnGet()
67
+ {
68
+ gameObject.SetActive(true);
69
+ ResetState();
70
+ }
71
+
72
+ public void OnReturn()
73
+ {
74
+ gameObject.SetActive(false);
75
+ }
76
+ }
77
+ ```
78
+
79
+ ---
80
+
81
+ ## Core Classes
82
+
83
+ ### `PrefabPool`
84
+
85
+ Main implementation of IPrefabPool.
86
+
87
+ ```csharp
88
+ public class PrefabPool : MonoBehaviour, IPrefabPool
89
+ {
90
+ public bool IsReady { get; } // True after Initialize() completes
91
+
92
+ public void RegisterGroup(PoolGroup group, Transform spawnParent);
93
+ public void SetSpawnParent(string groupId, Transform spawnParent);
94
+ public GameObject Get(PoolGroup group, string prefabId, Vector3 localPosition = default, Transform customParent = null);
95
+ public void Return(PoolGroup group, GameObject gameObject);
96
+ public IEnumerator Initialize();
97
+ public void Resize<T>(GameObject prefab, List<T> list, int count, Transform parent) where T : Component;
98
+ public void DestroyPool();
99
+ }
100
+ ```
101
+
102
+ **Example:**
103
+ ```csharp
104
+ // Dynamic list resizing
105
+ List<Enemy> enemies = new List<Enemy>();
106
+ pool.Resize(enemyPrefab, enemies, 10, transform);
107
+ // enemies.Count == 10
108
+ ```
109
+
110
+ ---
111
+
112
+ ### `PoolGroup`
113
+
114
+ Organizes related pool entries.
115
+
116
+ ```csharp
117
+ [Serializable]
118
+ public class PoolGroup
119
+ {
120
+ public string id; // Group identifier
121
+ public List<PoolEntry> entries; // Pool entries in this group
122
+ public PoolEntry fallbackEntry; // Used when prefabId not found
123
+ public Transform SpawnParent { get; } // Spawn parent transform
124
+
125
+ public GameObject Get(string prefabId, Vector3 localPosition = default, Transform customParent = null);
126
+ public T Get<T>(string prefabId, Vector3 localPosition = default) where T : class;
127
+ public void Return(GameObject gameObject);
128
+ }
129
+ ```
130
+
131
+ **Example:**
132
+ ```csharp
133
+ // Get with component type
134
+ var enemy = enemyGroup.Get<Enemy>("basic_enemy");
135
+ var handler = tileGroup.Get<IInputHandler>("tile_short");
136
+
137
+ // Fallback entry
138
+ var group = new PoolGroup
139
+ {
140
+ id = "tiles",
141
+ entries = new List<PoolEntry> { /* specific tiles */ },
142
+ fallbackEntry = new PoolEntry { id = "default", prefab = defaultPrefab, prewarmCount = 20 }
143
+ };
144
+ ```
145
+
146
+ ---
147
+
148
+ ### `PoolEntry`
149
+
150
+ Configuration for single pooled prefab.
151
+
152
+ ```csharp
153
+ [Serializable]
154
+ public class PoolEntry
155
+ {
156
+ public string id; // Prefab identifier
157
+ public GameObject prefab; // Prefab to pool
158
+ public int prewarmCount; // Pre-allocate count (0-10)
159
+
160
+ public GameObject Get(Transform poolParent, Transform spawnParent, Vector3 localPosition);
161
+ public void Return(GameObject gameObject, Transform poolParent);
162
+ public void DestroyPool();
163
+ public IEnumerator PreAllocate(Transform poolParent);
164
+ }
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Material Pooling
170
+
171
+ ### `MaterialPool`
172
+
173
+ Static utility for pooling material instances.
174
+
175
+ ```csharp
176
+ public static class MaterialPool
177
+ {
178
+ public static Material Take(Material sourceOrClone, int borrowId);
179
+ public static void Return(int borrowId);
180
+ }
181
+ ```
182
+
183
+ **Example:**
184
+ ```csharp
185
+ // Borrow material
186
+ Material mat = MaterialPool.Take(originalMaterial, GetInstanceID());
187
+ renderer.material = mat;
188
+ mat.SetAlpha(0.5f);
189
+
190
+ // Return on destroy
191
+ void OnDestroy()
192
+ {
193
+ MaterialPool.Return(GetInstanceID());
194
+ }
195
+ ```
196
+
197
+ ---
198
+
199
+ ### `MaterialExtensions`
200
+
201
+ Extension methods for material operations.
202
+
203
+ ```csharp
204
+ public static class MaterialExtensions
205
+ {
206
+ public static void SetAlpha(this Material m, float a); // Set alpha channel
207
+ }
208
+ ```
209
+
210
+ **Example:**
211
+ ```csharp
212
+ material.SetAlpha(0.5f); // 50% transparent
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Extension Methods
218
+
219
+ ### `PrefabPoolExtensions`
220
+
221
+ Extension methods for list operations with pooling.
222
+
223
+ ```csharp
224
+ public static class PrefabPoolExtensions
225
+ {
226
+ public static void DestroyLast<T>(this List<T> list) where T : Component;
227
+ public static T ClonePrefab<T>(this GameObject prefab, Transform parent) where T : Component;
228
+ public static void ResetAndClear<T>(this List<T> list, GameObject prefab, PrefabPool pool = null) where T : Component;
229
+ public static void Resize<T>(this List<T> list, int count, GameObject prefab, Transform parent, PrefabPool pool) where T : Component;
230
+ }
231
+ ```
232
+
233
+ **Example:**
234
+ ```csharp
235
+ // Resize list using pool
236
+ List<Tile> tiles = new List<Tile>();
237
+ tiles.Resize(10, tilePrefab, transform, pool);
238
+ // tiles.Count == 10
239
+ ```
240
+
241
+ ---
242
+
243
+ ## Common Patterns
244
+
245
+ ### Pattern 1: Basic Pool Setup
246
+
247
+ ```csharp
248
+ // When to use: Standard object pooling
249
+ var pool = gameObject.AddComponent<PrefabPool>();
250
+
251
+ var enemyGroup = new PoolGroup
252
+ {
253
+ id = "enemies",
254
+ entries = new List<PoolEntry>
255
+ {
256
+ new PoolEntry { id = "basic_enemy", prefab = enemyPrefab, prewarmCount = 10 }
257
+ }
258
+ };
259
+
260
+ pool.RegisterGroup(enemyGroup, transform);
261
+ StartCoroutine(pool.Initialize());
262
+
263
+ // Spawn
264
+ var enemy = enemyGroup.Get<Enemy>("basic_enemy");
265
+
266
+ // Despawn
267
+ enemyGroup.Return(enemy.gameObject);
268
+ ```
269
+
270
+ ### Pattern 2: Pooled Component with Lifecycle
271
+
272
+ ```csharp
273
+ // When to use: Custom initialization/cleanup logic
274
+ public class PooledEnemy : MonoBehaviour, IPoolPrefab
275
+ {
276
+ private Rigidbody rb;
277
+
278
+ void Awake()
279
+ {
280
+ rb = GetComponent<Rigidbody>();
281
+ }
282
+
283
+ public void OnGet()
284
+ {
285
+ gameObject.SetActive(true);
286
+ rb.velocity = Vector3.zero;
287
+ }
288
+
289
+ public void OnReturn()
290
+ {
291
+ gameObject.SetActive(false);
292
+ }
293
+ }
294
+ ```
295
+
296
+ ### Pattern 3: Dynamic List Resizing
297
+
298
+ ```csharp
299
+ // When to use: Variable number of active objects
300
+ List<Tile> activeTiles = new List<Tile>();
301
+
302
+ void UpdateTileCount(int count)
303
+ {
304
+ pool.Resize(tilePrefab, activeTiles, count, transform);
305
+
306
+ for (int i = 0; i < activeTiles.Count; i++)
307
+ {
308
+ activeTiles[i].UpdatePosition(i);
309
+ }
310
+ }
311
+ ```
312
+
313
+ ### Pattern 4: Material Pooling
314
+
315
+ ```csharp
316
+ // When to use: Dynamic color/alpha changes without creating material instances
317
+ public class ColoredTile : MonoBehaviour
318
+ {
319
+ private Material instanceMaterial;
320
+ private int borrowId;
321
+
322
+ void Start()
323
+ {
324
+ borrowId = GetInstanceID();
325
+ instanceMaterial = MaterialPool.Take(GetComponent<Renderer>().sharedMaterial, borrowId);
326
+ GetComponent<Renderer>().material = instanceMaterial;
327
+ }
328
+
329
+ public void SetColor(Color color)
330
+ {
331
+ instanceMaterial.color = color;
332
+ }
333
+
334
+ void OnDestroy()
335
+ {
336
+ MaterialPool.Return(borrowId);
337
+ }
338
+ }
339
+ ```
340
+
341
+ ### Pattern 5: Fallback Entry
342
+
343
+ ```csharp
344
+ // When to use: Default prefab when specific ID not found
345
+ var group = new PoolGroup
346
+ {
347
+ id = "tiles",
348
+ entries = new List<PoolEntry>
349
+ {
350
+ new PoolEntry { id = "special", prefab = specialPrefab, prewarmCount = 5 }
351
+ },
352
+ fallbackEntry = new PoolEntry { id = "default", prefab = defaultPrefab, prewarmCount = 20 }
353
+ };
354
+
355
+ // Gets special prefab
356
+ var special = group.Get("special");
357
+
358
+ // Gets default prefab (fallback)
359
+ var unknown = group.Get("unknown_id");
360
+ ```
361
+
362
+ ---
363
+
364
+ ## See Also
365
+
366
+ - [PrefabPool README](../README.md)
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: c642e9208f6d946acaff8b89526ce88d
3
+ TextScriptImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
package/Docs.meta ADDED
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 7775acaa127c04e849f099a57796fd3f
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
package/README.md CHANGED
@@ -1,105 +1,116 @@
1
- # Prefab Pool
1
+ # PrefabPool
2
2
 
3
- Generic object pooling system for Unity.
3
+ **Package:** `com.amanotes.prefabpool`
4
+ **Version:** 1.0.1
5
+
6
+ Generic object pooling system for Unity with lifecycle callbacks and material pooling support.
7
+
8
+ ---
4
9
 
5
10
  ## Features
6
11
 
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
12
+ - 🎮 Simple Get/Return API pattern
13
+ - 🎯 Automatic prefab instantiation and reuse
14
+ - 🎨 Lifecycle callbacks via IPoolPrefab interface
15
+ - 🚀 Pre-warming support for performance
16
+ - 🔧 Material pooling for dynamic instances
17
+
18
+ ---
19
+
20
+ ## Documentation
21
+
22
+ **Complete documentation:** [`Docs/PrefabPool-API.md`](Docs/PrefabPool-API.md)
23
+
24
+ ---
13
25
 
14
26
  ## Installation
15
27
 
16
- Add to your `manifest.json`:
28
+ Add the package to your Unity project via `Packages/manifest.json`:
29
+
17
30
  ```json
18
31
  {
19
32
  "dependencies": {
20
- "com.amanotes.prefabpool": "1.0.0"
21
- }
33
+ "com.amanotes.prefabpool": "1.0.1"
34
+ },
35
+ "scopedRegistries": [
36
+ {
37
+ "name": "npmjs",
38
+ "url": "https://registry.npmjs.org/",
39
+ "scopes": [
40
+ "com.amanotes.prefabpool"
41
+ ]
42
+ }
43
+ ]
22
44
  }
23
45
  ```
24
46
 
47
+ ---
48
+
25
49
  ## Quick Start
26
50
 
27
51
  ```csharp
28
- using Amanotes.PrefabPool;
29
-
30
- public class Example : MonoBehaviour
52
+ public class PoolExample : MonoBehaviour
31
53
  {
54
+ private PrefabPool pool;
55
+ private PoolGroup enemyGroup;
56
+
32
57
  void Start()
33
58
  {
34
- var pool = new PrefabPool();
35
- var group = CreatePoolGroup();
36
- pool.RegisterGroup(group, transform);
59
+ pool = gameObject.AddComponent<PrefabPool>();
37
60
 
38
- // Get object from pool
39
- var obj = pool.Get<SpriteRenderer>("enemy");
61
+ enemyGroup = new PoolGroup
62
+ {
63
+ id = "enemies",
64
+ entries = new List<PoolEntry>
65
+ {
66
+ new PoolEntry { id = "basic_enemy", prefab = enemyPrefab, prewarmCount = 10 }
67
+ }
68
+ };
40
69
 
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();
70
+ pool.RegisterGroup(enemyGroup, transform);
71
+ StartCoroutine(pool.Initialize());
59
72
  }
60
73
 
61
- public void OnReturn()
74
+ void SpawnEnemy()
62
75
  {
63
- // Called when returned to pool
64
- gameObject.SetActive(false);
76
+ var enemy = enemyGroup.Get<Enemy>("basic_enemy");
77
+ enemy.transform.position = GetSpawnPosition();
65
78
  }
66
79
  }
67
80
  ```
68
81
 
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
- ```
82
+ ---
84
83
 
85
84
  ## API Reference
86
85
 
87
- ### IPrefabPool Interface
86
+ ### Key Classes
88
87
 
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
- ```
88
+ **IPrefabPool**
89
+ - Purpose: Core interface for pool management
90
+ - Key methods: `RegisterGroup()`, `Get()`, `Return()`, `Initialize()`
95
91
 
96
- ### IPoolPrefab Interface
92
+ **PrefabPool**
93
+ - Purpose: Main implementation of IPrefabPool
94
+ - Key methods: `RegisterGroup()`, `Get()`, `Return()`, `Resize<T>()`
97
95
 
98
- ```csharp
99
- void OnGet(); // Called when retrieved
100
- void OnReturn(); // Called when returned
101
- ```
96
+ **PoolGroup**
97
+ - Purpose: Organizes related pool entries
98
+ - Key methods: `Get()`, `Get<T>()`, `Return()`
99
+
100
+ **IPoolPrefab**
101
+ - Purpose: Lifecycle callbacks for pooled objects
102
+ - Key methods: `OnGet()`, `OnReturn()`
103
+
104
+ **See:** [Complete API Reference](Docs/PrefabPool-API.md)
105
+
106
+ ---
107
+
108
+ ## Dependencies
109
+
110
+ - Unity 2022.3+
111
+
112
+ ---
102
113
 
103
- ## License
114
+ ## Version History
104
115
 
105
- Copyright © Amanotes
116
+ See [CHANGELOG.md](CHANGELOG.md)
@@ -0,0 +1,3 @@
1
+ using System.Runtime.CompilerServices;
2
+
3
+ [assembly: InternalsVisibleTo("Amanotes.Core.PrefabPool.Tests")]
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: dbb42ee06dab541b2a794f805c10c871
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant: