Skip to content

Commit 026457d

Browse files
committed
Fixed objects not getting unpooled when they were reactivated without going through the component pool API (enabling component or activating game object)
1 parent fdfaa3b commit 026457d

File tree

6 files changed

+79
-40
lines changed

6 files changed

+79
-40
lines changed

CHANGELOG.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this package will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## [1.0.9] - 2024-02-11
8+
9+
### Fixed
10+
11+
- Fixed objects not getting unpooled when they were reactivated without going through the component pool API (enabling component or activating game object).
12+
713
## [1.0.8] - 2024-02-01
814

915
### Changes
@@ -16,12 +22,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1622

1723
- Added preprocessor directives for unit tests.
1824

19-
## [1.0.6] - 2024-01-23
20-
21-
### Fixed
22-
23-
- Fixed objects not getting unpooled when they were reactivated without going through the component pool API (enabling component or activating game object).
24-
2525
## [1.0.3] - 2022-11-21
2626

2727
### Fixed

Editor/CustomEditors.meta

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Editor/PropertyDrawers.meta

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Internal/PoolContainer.cs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ namespace Bert.Pool.Internal
77
/// Pool of <see cref="PoolObject"/> instances.
88
/// </summary>
99
/// <typeparam name="T">Type of component.</typeparam>
10-
internal sealed class PoolContainer<T> : IPoolObjectManager
10+
internal sealed class PoolContainer<T> : IPoolObjectManager
1111
where T : Component
1212
{
1313
private static readonly string PoolInstanceSuffix = $" (Pool: {typeof(T).Name})";
14-
14+
1515
private const int DefaultCapacity = 8;
1616

1717
private readonly List<(PoolObject poolInstance, T component)> _instances = new List<(PoolObject, T)>(DefaultCapacity);
@@ -55,17 +55,17 @@ public T GetInstance(T source, Vector3 position, Quaternion rotation, Transform
5555
/// </summary>
5656
public T CreateInstance(Object source, Vector3 position, Quaternion rotation, Transform parent)
5757
{
58-
T instance = (T) Object.Instantiate(source, position, rotation, parent);
58+
T instance = (T)Object.Instantiate(source, position, rotation, parent);
5959
instance.gameObject.SetActive(true);
60-
60+
6161
#if UNITY_EDITOR
6262
instance.name = GetPoolInstanceName(instance.name);
6363
#endif
6464

6565
var poolObject = instance.gameObject.AddComponent<PoolObject>();
6666
poolObject.SetManager(this);
6767
poolObject.Index = _instances.Count;
68-
68+
6969
_instances.Add((poolObject, instance));
7070

7171
return instance;
@@ -89,29 +89,33 @@ public void DestroyInstances()
8989
/// <inheritdoc />
9090
public void UnPool(PoolObject poolObject)
9191
{
92+
// Un-pooled object's position in the list will be swapped with the first pooled instance.
93+
int swapIndex = _instances.Count - _poolCount;
94+
9295
--_poolCount;
96+
97+
// Manually un-pooling objects without going through the API should be less common, so an extra check is added to avoid unnecessary swaps.
98+
if (poolObject.Index != swapIndex)
99+
{
100+
Swap(poolObject.Index, swapIndex);
101+
}
93102
}
94103

95104
/// <inheritdoc />
96105
public void Pool(PoolObject poolObject)
97106
{
98107
++_poolCount;
99-
int targetIndex = _instances.Count - _poolCount;
100-
int activeIndex = poolObject.Index;
101-
108+
102109
// Swap pooled object with last active instance in the list.
103-
(_instances[activeIndex], _instances[targetIndex]) = (_instances[targetIndex], _instances[activeIndex]);
104-
105-
_instances[activeIndex].poolInstance.Index = activeIndex;
106-
_instances[targetIndex].poolInstance.Index = targetIndex;
110+
Swap(poolObject.Index, _instances.Count - _poolCount);
107111
}
108112

109113
/// <inheritdoc />
110114
public void Destroy(PoolObject poolObject)
111115
{
112116
// Instances are pooled on disable so they need to be un-pooled here.
113117
--_poolCount;
114-
118+
115119
int destroyedIndex = poolObject.Index;
116120
int lastIndex = _instances.Count - 1;
117121

@@ -121,6 +125,14 @@ public void Destroy(PoolObject poolObject)
121125
_instances.RemoveAt(lastIndex);
122126
}
123127

128+
private void Swap(int x, int y)
129+
{
130+
(_instances[x], _instances[y]) = (_instances[y], _instances[x]);
131+
132+
_instances[x].poolInstance.Index = x;
133+
_instances[y].poolInstance.Index = y;
134+
}
135+
124136
private static string GetPoolInstanceName(string name)
125137
{
126138
int endIndex = name.Length - "(Clone)".Length;
@@ -130,4 +142,4 @@ private static string GetPoolInstanceName(string name)
130142
return name.Substring(0, endIndex) + PoolInstanceSuffix;
131143
}
132144
}
133-
}
145+
}

Tests/Runtime/PoolContainerTests.cs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ internal class PoolContainerTests
1313
{
1414
private PoolContainer<MockMonoBehaviour> _container;
1515
private MockMonoBehaviour _source;
16-
16+
1717
private MockMonoBehaviour GetInstance() => _container.GetInstance(_source, Vector3.zero, Quaternion.identity, null);
18-
18+
1919
[SetUp]
2020
public void Setup()
2121
{
2222
_container = new PoolContainer<MockMonoBehaviour>();
23-
_source = new GameObject(nameof(MockMonoBehaviour)).AddComponent<MockMonoBehaviour>();
23+
_source = new GameObject(nameof(MockMonoBehaviour)).AddComponent<MockMonoBehaviour>();
2424
}
2525

2626
[TearDown]
@@ -96,23 +96,40 @@ public void DeactivatingGameObject_PoolsInstance()
9696

9797
Object.DestroyImmediate(instance.gameObject);
9898
}
99-
99+
100100
[UnityTest]
101-
public IEnumerator ActivatingGameObject_ShouldUnPoolInstance()
101+
public IEnumerator ActivatingGameObjectManually_ShouldUnPool_InstanceFromPool()
102102
{
103-
var instance = GetInstance();
104-
instance.gameObject.SetActive(false);
103+
var instance1 = GetInstance();
104+
var instance2 = GetInstance();
105+
var instance3 = GetInstance();
106+
107+
// Deactivating the instances in reverse order will preserves their order in the collection.
108+
instance3.gameObject.SetActive(false);
109+
instance2.gameObject.SetActive(false);
110+
instance1.gameObject.SetActive(false);
105111

106112
yield return null;
107113

108-
instance.gameObject.SetActive(true);
109-
110-
// New instance should be created since the previous instance was un-pooled.
111-
var newInstance = GetInstance();
112-
UnityEngine.Assertions.Assert.AreNotEqual(instance, newInstance);
114+
// Activating the instance without going through the API should still un-pool it.
115+
instance2.gameObject.SetActive(true);
116+
117+
// Ensures that the correct instance is returned after un-pooling the other instance manually.
118+
var expected1 = GetInstance();
119+
var expected3 = GetInstance();
120+
121+
// Ensure the manually un-pooled instance doesn't get returned.
122+
var instance4 = GetInstance();
113123

114-
Object.DestroyImmediate(instance.gameObject);
115-
Object.DestroyImmediate(newInstance.gameObject);
124+
UnityEngine.Assertions.Assert.AreEqual(instance1, expected1);
125+
UnityEngine.Assertions.Assert.AreEqual(instance3, expected3);
126+
UnityEngine.Assertions.Assert.AreNotEqual(instance3, instance4);
127+
Assert.That(_container.InstanceCount, Is.EqualTo(4));
128+
129+
Object.DestroyImmediate(instance1.gameObject);
130+
Object.DestroyImmediate(instance2.gameObject);
131+
Object.DestroyImmediate(instance3.gameObject);
132+
Object.DestroyImmediate(instance4.gameObject);
116133
}
117134

118135
[Test]
@@ -155,4 +172,4 @@ public IEnumerator DestroyInstances_ShouldDestroyInstances()
155172
}
156173
}
157174

158-
#endif
175+
#endif

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "com.jeffbert.unity-component-pool",
3-
"version": "1.0.7",
3+
"version": "1.0.9",
44
"displayName": "Component Pool",
55
"description": "Pooling system designed to work with Unity components. Reduces memory allocations by reusing disabled components.",
66
"unity": "2019.4",

0 commit comments

Comments
 (0)