Skip to content

Commit dc14561

Browse files
Merge pull request #2 from CoderGamester/develop
0.2.0
2 parents cf9f4d6 + 2e0d3ec commit dc14561

7 files changed

Lines changed: 318 additions & 148 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@ 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+
## [0.2.0] - 2020-01-19
8+
9+
- Added new *ObjectPool* & *GameObjectPool* pools to allow to allow to use object pools independent from the *PoolService*. This allows to have different pools of the same type in the project in different object controllers
10+
- Added new interface *IPoolEntityClear* that allows a callback method for entities when they are cleared from the pool
11+
- Added new unit tests for the *ObjectPool*
12+
13+
**Changed**:
14+
- Now the PoolService.Clear() does not take any action parameters. To have a callback when the entity is cleared, please have the entity implement the *IPoolEntityClear* interface
15+
716
## [0.1.1] - 2020-01-06
817

918
- Added License

Runtime/PoolService.cs

Lines changed: 171 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,29 @@
11
using System;
22
using System.Collections.Generic;
3+
using UnityEngine;
4+
using Object = UnityEngine.Object;
35

46
// ReSharper disable once CheckNamespace
57

68
namespace GameLovers.Services
79
{
810
/// <summary>
9-
/// This interface allows pooled objects to be notified when it is spawned
10-
/// </summary>
11-
public interface IPoolEntitySpawn
12-
{
13-
/// <summary>
14-
/// Invoked when the Entity is spawned
15-
/// </summary>
16-
void OnSpawn();
17-
}
18-
19-
/// <summary>
20-
/// This interface allows pooled objects to be notified when it is despawned
21-
/// </summary>
22-
public interface IPoolEntityDespawn
23-
{
24-
/// <summary>
25-
/// Invoked when the entity is despawned
26-
/// </summary>
27-
void OnDespawn();
28-
}
29-
30-
/// <summary>
31-
/// Simple pool implementation that can handle any type of entity objects
11+
/// This service allows to manage multiple pools of different types.
12+
/// The service can only a single pool of the same type.
3213
/// </summary>
3314
public interface IPoolService
3415
{
3516
/// <summary>
3617
/// Initializes a new pool with the given <paramref name="initialSize"/>
3718
/// It invokes the <paramref name="instantiator"/> function every time a new entity is created in the pool
3819
/// </summary>
39-
void InitPool<T>(int initialSize, Func<T> instantiator);
20+
void InitPool<T>(int initialSize, Func<T> instantiator) where T : new();
4021

4122
/// <summary>
4223
/// Initializes a new pool with the given <paramref name="initialSize"/> and a sample entity given back in the <paramref name="instantiator"/>
4324
/// It invokes the <paramref name="instantiator"/> function every time a new entity is created in the pool
4425
/// </summary>
45-
void InitPool<T>(int initialSize, T sampleEntity, Func<T, T> instantiator);
26+
void InitPool<T>(int initialSize, T sampleEntity, Func<T, T> instantiator) where T : Object;
4627

4728
/// <summary>
4829
/// Checks if exists a pool of the given type already exists or needs to be initialized with
@@ -53,65 +34,34 @@ public interface IPoolService
5334
/// <inheritdoc cref="HasPool{T}"/>
5435
bool HasPool(Type type);
5536

56-
/// <summary>
57-
/// Spawns and returns an entity of the given type <typeparamref name="T"/>
58-
/// This function does not initialize the entity. For that, have the entity implement <see cref="IPoolEntitySpawn"/> or do it externally
59-
/// This function throws a <exception cref="StackOverflowException" /> if the pool is empty
60-
/// </summary>
37+
/// <inheritdoc cref="IObjectPool{T}.Spawn"/>
6138
T Spawn<T>();
6239

63-
/// <summary>
64-
/// Despawns the given <paramref name="entity"/> and returns it back to the pool to be used again later
65-
/// This function does not reset the entity. For that, have the entity implement <see cref="IPoolEntityDespawn"/> or do it externally
66-
/// </summary>
40+
/// <inheritdoc cref="IObjectPool{T}.Despawn"/>
6741
void Despawn<T>(T entity);
6842

69-
/// <summary>
70-
/// Despawns all active spawned entities of the given type <typeparamref name="T"/> and returns them back to the pool to be used again later
71-
/// This function does not reset the entity. For that, have the entity implement <see cref="IPoolEntityDespawn"/> or do it externally
72-
/// </summary>
43+
/// <inheritdoc cref="IObjectPool{T}.DespawnAll"/>
7344
void DespawnAll<T>();
7445

75-
/// <summary>
76-
/// Clears the pool of the given type <typeparamref name="T"/>
77-
/// It calls <paramref name="clearAction"/> with every entity remaining in the pool and managed by the pool
78-
/// </summary>
79-
void Clear<T>(Action<T> clearAction);
46+
/// <inheritdoc cref="IObjectPool{T}.Clear"/>
47+
void Clear<T>();
8048
}
8149

8250
/// <inheritdoc />
8351
public class PoolService : IPoolService
8452
{
85-
private readonly Dictionary<Type, IPoolStack> pools = new Dictionary<Type, IPoolStack>();
53+
private readonly Dictionary<Type, IObjectPool> _pools = new Dictionary<Type, IObjectPool>();
8654

8755
/// <inheritdoc />
88-
public void InitPool<T>(int initialSize, Func<T> instantiator)
56+
public void InitPool<T>(int initialSize, Func<T> instantiator) where T : new()
8957
{
90-
InitPool(initialSize, instantiator.Invoke(), newEntity => instantiator.Invoke());
58+
_pools.Add(typeof(T), new ObjectPool<T>(initialSize, instantiator));
9159
}
9260

9361
/// <inheritdoc />
94-
public void InitPool<T>(int initialSize, T sampleEntity, Func<T, T> instantiator)
62+
public void InitPool<T>(int initialSize, T sampleEntity, Func<T, T> instantiator) where T : Object
9563
{
96-
if (pools.ContainsKey(typeof(T)))
97-
{
98-
throw new InvalidOperationException($"The pool of type {typeof(T)} was already initialized");
99-
}
100-
101-
var pool = new PoolStack<T>
102-
{
103-
Stack = new Stack<T>(),
104-
SpawnedEntities = new List<T>(),
105-
SampleEntity = sampleEntity,
106-
Instatiator = instantiator
107-
};
108-
109-
pools.Add(typeof(T), pool);
110-
111-
for (int i = 0; i < initialSize; i++)
112-
{
113-
pool.Stack.Push(instantiator.Invoke(sampleEntity));
114-
}
64+
_pools.Add(typeof(T), new GameObjectPool<T>(initialSize, sampleEntity, instantiator));
11565
}
11666

11767
/// <inheritdoc />
@@ -123,88 +73,195 @@ public bool HasPool<T>()
12373
/// <inheritdoc />
12474
public bool HasPool(Type type)
12575
{
126-
return pools.ContainsKey(type);
76+
return _pools.ContainsKey(type);
12777
}
12878

12979
/// <inheritdoc />
13080
public T Spawn<T>()
13181
{
132-
var pool = GetPool<T>();
133-
T entity = pool.Stack.Count == 0 ? pool.Instatiator.Invoke(pool.SampleEntity) : pool.Stack.Pop();
134-
var poolEntity = entity as IPoolEntitySpawn;
135-
136-
pool.SpawnedEntities.Add(entity);
137-
poolEntity?.OnSpawn();
138-
139-
return entity;
82+
return GetPool<T>().Spawn();
14083
}
14184

14285
/// <inheritdoc />
14386
public void Despawn<T>(T entity)
14487
{
145-
var pool = GetPool<T>();
146-
var poolEntity = entity as IPoolEntityDespawn;
147-
148-
pool.Stack.Push(entity);
149-
pool.SpawnedEntities.Remove(entity);
150-
poolEntity?.OnDespawn();
88+
GetPool<T>().Despawn(entity);
15189
}
15290

15391
/// <inheritdoc />
15492
public void DespawnAll<T>()
15593
{
156-
var pool = GetPool<T>();
157-
158-
foreach (T entity in pool.SpawnedEntities)
159-
{
160-
var poolEntity = entity as IPoolEntityDespawn;
94+
GetPool<T>().DespawnAll();
95+
}
16196

162-
pool.Stack.Push(entity);
163-
poolEntity?.OnDespawn();
97+
/// <inheritdoc />
98+
public void Clear<T>()
99+
{
100+
GetPool<T>().Clear();
101+
_pools.Remove(typeof(T));
102+
}
103+
104+
private IObjectPool<T> GetPool<T>()
105+
{
106+
if (!_pools.TryGetValue(typeof(T), out IObjectPool pool))
107+
{
108+
throw new ArgumentException("The pool was not initialized for the type " + typeof(T));
164109
}
165-
166-
pool.SpawnedEntities.Clear();
110+
111+
return pool as IObjectPool<T>;
167112
}
113+
}
114+
115+
/// <summary>
116+
/// This interface allows pooled objects to be notified when it is spawned
117+
/// </summary>
118+
public interface IPoolEntitySpawn
119+
{
120+
/// <summary>
121+
/// Invoked when the Entity is spawned
122+
/// </summary>
123+
void OnSpawn();
124+
}
125+
126+
/// <summary>
127+
/// This interface allows pooled objects to be notified when it is despawned
128+
/// </summary>
129+
public interface IPoolEntityDespawn
130+
{
131+
/// <summary>
132+
/// Invoked when the entity is despawned
133+
/// </summary>
134+
void OnDespawn();
135+
}
136+
137+
/// <summary>
138+
/// This interface allows pooled objects to be notified when they are cleared from the pool
139+
/// </summary>
140+
public interface IPoolEntityCleared
141+
{
142+
/// <summary>
143+
/// Invoked when the entity is cleared
144+
/// </summary>
145+
void OnCleared();
146+
}
147+
148+
/// <summary>
149+
/// Simple object pool implementation that can handle any type of entity objects
150+
/// </summary>
151+
public interface IObjectPool
152+
{
153+
/// <summary>
154+
/// Clears the pool
155+
/// This function does not clear the entity. For that, have the entity implement <see cref="IPoolEntityCleared"/> or do it externally
156+
/// </summary>
157+
void Clear();
158+
159+
/// <summary>
160+
/// Despawns all active spawned entities and returns them back to the pool to be used again later
161+
/// This function does not reset the entity. For that, have the entity implement <see cref="IPoolEntityDespawn"/> or do it externally
162+
/// </summary>
163+
void DespawnAll();
164+
}
165+
166+
/// <inheritdoc />
167+
public interface IObjectPool<T> : IObjectPool
168+
{
169+
/// <summary>
170+
/// Spawns and returns an entity of the given type <typeparamref name="T"/>
171+
/// This function does not initialize the entity. For that, have the entity implement <see cref="IPoolEntitySpawn"/> or do it externally
172+
/// This function throws a <exception cref="StackOverflowException" /> if the pool is empty
173+
/// </summary>
174+
T Spawn();
175+
176+
/// <summary>
177+
/// Despawns the given <paramref name="entity"/> and returns it back to the pool to be used again later
178+
/// This function does not reset the entity. For that, have the entity implement <see cref="IPoolEntityDespawn"/> or do it externally
179+
/// </summary>
180+
void Despawn(T entity);
181+
}
168182

183+
/// <inheritdoc />
184+
public abstract class ObjectPoolBase<T> : IObjectPool<T>
185+
{
186+
private readonly Stack<T> _stack = new Stack<T>();
187+
private readonly IList<T> _spawnedEntities = new List<T>();
188+
private readonly Func<T, T> _instantiator;
189+
private readonly T _sampleEntity;
190+
191+
protected ObjectPoolBase(int initSize, T sampleEntity, Func<T, T> instantiator)
192+
{
193+
_sampleEntity = sampleEntity;
194+
_instantiator = instantiator;
195+
196+
for (var i = 0; i < initSize; i++)
197+
{
198+
_stack.Push(instantiator.Invoke(sampleEntity));
199+
}
200+
}
201+
169202
/// <inheritdoc />
170-
public void Clear<T>(Action<T> clearAction)
203+
public void Clear()
171204
{
172-
var pool = GetPool<T>();
173-
174-
for (var i = 0; i < pool.Stack.Count; i++)
205+
for (var i = 0; i < _stack.Count; i++)
175206
{
176-
T entity = pool.Stack.Pop();
207+
var entity =_stack.Pop() as IPoolEntityCleared;
177208

178-
clearAction?.Invoke(entity);
209+
entity?.OnCleared();
179210
}
180211

181-
pool.SpawnedEntities.Clear();
182-
pools.Remove(typeof(T));
212+
_spawnedEntities.Clear();
183213
}
184214

185-
private PoolStack<T> GetPool<T>()
215+
/// <inheritdoc />
216+
public T Spawn()
186217
{
187-
if (!pools.TryGetValue(typeof(T), out IPoolStack poolStack))
188-
{
189-
throw new ArgumentException("The pool was not initialized for the type " + typeof(T));
190-
}
218+
var entity = _stack.Count == 0 ? _instantiator.Invoke(_sampleEntity) : _stack.Pop();
219+
var poolEntity = entity as IPoolEntitySpawn;
220+
221+
_spawnedEntities.Add(entity);
222+
poolEntity?.OnSpawn();
223+
224+
return entity;
225+
}
226+
227+
/// <inheritdoc />
228+
public void Despawn(T entity)
229+
{
230+
var poolEntity = entity as IPoolEntityDespawn;
231+
232+
_stack.Push(entity);
233+
_spawnedEntities.Remove(entity);
234+
poolEntity?.OnDespawn();
235+
}
191236

192-
if (poolStack is PoolStack<T> pool)
237+
/// <inheritdoc />
238+
public void DespawnAll()
239+
{
240+
for (var i = 0; i < _spawnedEntities.Count; i++)
193241
{
194-
return pool;
242+
Despawn(_spawnedEntities[i]);
195243
}
196-
197-
throw new ArgumentException("The pool was not properly initialized for the type " + typeof(T));
244+
245+
_spawnedEntities.Clear();
198246
}
199-
200-
private interface IPoolStack {}
247+
}
248+
249+
/// <inheritdoc />
250+
public class ObjectPool<T> : ObjectPoolBase<T> where T : new()
251+
{
252+
public ObjectPool(int initSize, Func<T> instantiator) : base(initSize, instantiator(), newEntity => instantiator.Invoke())
253+
{
254+
}
255+
}
201256

202-
private class PoolStack<T> : IPoolStack
257+
/// <inheritdoc />
258+
/// <remarks>
259+
/// <see cref="IObjectPool"/> implementation for objects of type <see cref="Object"/>
260+
/// </remarks>
261+
public class GameObjectPool<T> : ObjectPoolBase<T> where T : Object
262+
{
263+
public GameObjectPool(int initSize, T sampleEntity, Func<T, T> instantiator) : base(initSize, sampleEntity, instantiator)
203264
{
204-
public Stack<T> Stack;
205-
public List<T> SpawnedEntities;
206-
public Func<T, T> Instatiator;
207-
public T SampleEntity;
208265
}
209266
}
210267
}

0 commit comments

Comments
 (0)