Skip to content

Commit e422831

Browse files
committed
Added Unitys native object pool implementation. Updated readme. Refactored object pooling code
1 parent b55896e commit e422831

32 files changed

+808
-97
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using UnityEngine;
4+
5+
namespace ObjectPool.Gun
6+
{
7+
//Parent bullet class to avoid code duplication
8+
public class BulletBase : MonoBehaviour
9+
{
10+
private readonly float bulletSpeed = 10f;
11+
12+
private readonly float deactivationDistance = 30f;
13+
14+
15+
16+
protected void MoveBullet()
17+
{
18+
transform.Translate(bulletSpeed * Time.deltaTime * Vector3.forward);
19+
}
20+
21+
22+
23+
protected bool IsBulletDead()
24+
{
25+
bool isDead = false;
26+
27+
//The gun is at 0
28+
if (Vector3.SqrMagnitude(Vector3.zero - transform.position) > deactivationDistance * deactivationDistance)
29+
{
30+
isDead = true;
31+
}
32+
33+
return isDead;
34+
}
35+
}
36+
}

Assets/Patterns/18. Object Pool/Gun/Object pools/BulletBase.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Patterns/18. Object Pool/Gun/Scripts/GunController.cs renamed to Assets/Patterns/18. Object Pool/Gun/Object pools/GunController.cs

+14-18
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,29 @@
22
using System.Collections.Generic;
33
using UnityEngine;
44

5+
56
namespace ObjectPool.Gun
67
{
78
public class GunController : MonoBehaviour
89
{
9-
//Pick which object pool you want to use
10-
public BulletObjectPool bulletPool;
10+
//Pick which object pool you want to use by activating it in the hierarchy and drag it to its uncommented slot
11+
12+
//Simplest possible object pool
13+
//public BulletObjectPoolSimple bulletPool;
1114

15+
//Optimized object pool
1216
//public BulletObjectPoolOptimized bulletPool;
1317

18+
//Unity's native object pool
19+
public BulletObjectPoolUnity bulletPool;
20+
1421

1522
//Private
16-
private float rotationSpeed = 60f;
23+
private readonly float rotationSpeed = 60f;
1724

1825
private float fireTimer;
1926

20-
private float fireInterval = 0.1f;
27+
private readonly float fireInterval = 0.1f;
2128

2229

2330

@@ -35,7 +42,7 @@ void Start()
3542

3643
void Update()
3744
{
38-
//Rotate gun
45+
//Rotate gun with A and D keys
3946
if (Input.GetKey(KeyCode.A))
4047
{
4148
transform.Rotate(Vector3.up, -rotationSpeed * Time.deltaTime);
@@ -46,17 +53,15 @@ void Update()
4653
}
4754

4855

49-
//Fire gun
56+
//Fire gun with spacebar
5057
if (Input.GetKey(KeyCode.Space) && fireTimer > fireInterval)
5158
{
5259
fireTimer = 0f;
5360

54-
GameObject newBullet = GetABullet();
61+
GameObject newBullet = bulletPool.GetBullet();
5562

5663
if (newBullet != null)
5764
{
58-
newBullet.SetActive(true);
59-
6065
newBullet.transform.forward = transform.forward;
6166

6267
//Move the bullet to the tip of the gun or it will look strange if we rotate while firing
@@ -72,14 +77,5 @@ void Update()
7277
//Update the time since we last fired a bullet
7378
fireTimer += Time.deltaTime;
7479
}
75-
76-
77-
78-
private GameObject GetABullet()
79-
{
80-
GameObject bullet = bulletPool.GetBullet();
81-
82-
return bullet;
83-
}
8480
}
8581
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using UnityEngine;
4+
5+
namespace ObjectPool.Gun
6+
{
7+
//Parent object pool class to avoid code duplication
8+
public class ObjectPoolBase : MonoBehaviour
9+
{
10+
//How many bullets do we start with when the game starts
11+
protected const int INITIAL_POOL_SIZE = 10;
12+
13+
//Sometimes it can be good to put a limit to how many bullets we can instantiate or we might get millions of them
14+
protected const int MAX_POOL_SIZE = 20;
15+
}
16+
}

Assets/Patterns/18. Object Pool/Gun/Object pools/ObjectPoolBase.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Patterns/18. Object Pool/Gun/Scripts/Optimized/BulletObjectPoolOptimized.cs renamed to Assets/Patterns/18. Object Pool/Gun/Object pools/Optimized/BulletObjectPoolOptimized.cs

+14-13
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,21 @@
44

55
namespace ObjectPool.Gun
66
{
7-
//Has to inherit from MonoBehaviour so we can use Instantiate()
8-
public class BulletObjectPoolOptimized : MonoBehaviour
7+
//This object pool is slightly more complicated to understand but has better performance
8+
public class BulletObjectPoolOptimized : ObjectPoolBase
99
{
1010
//The bullet prefab we instantiate
1111
public MoveBulletOptimized bulletPrefab;
1212

1313
//Store the pooled bullets here
14-
//Instead of GameObject, use MoveBulletOptimized so we dont need a million GetComponent because we need access to that script
15-
private List<MoveBulletOptimized> bullets = new List<MoveBulletOptimized>();
16-
17-
//How many bullets do we start with when the game starts
18-
private const int INITIAL_POOL_SIZE = 10;
19-
20-
//Sometimes it can be good to put a limit to how many bullets we can isntantiate or we might get millions of them
21-
private const int MAX_POOL_SIZE = 20;
14+
private readonly List<MoveBulletOptimized> bullets = new ();
2215

2316
//First available bullet, so we don't have to search a list to find it
2417
//Instead we create a linked-list where all unused bullets are linked together
2518
private MoveBulletOptimized firstAvailable;
2619

2720

21+
2822
private void Start()
2923
{
3024
if (bulletPrefab == null)
@@ -50,10 +44,11 @@ private void Start()
5044
}
5145

5246
//The last one terminates the linked-list
53-
bullets[bullets.Count - 1].next = null;
47+
bullets[^1].next = null;
5448
}
5549

5650

51+
5752
//Generate a single new bullet and put it in the list
5853
private void GenerateBullet()
5954
{
@@ -68,6 +63,7 @@ private void GenerateBullet()
6863
}
6964

7065

66+
7167
//A bullet has been deactivated so we need to add it to the linked list
7268
public void ConfigureDeactivatedBullet(MoveBulletOptimized deactivatedObj)
7369
{
@@ -78,6 +74,7 @@ public void ConfigureDeactivatedBullet(MoveBulletOptimized deactivatedObj)
7874
}
7975

8076

77+
8178
//Try to get a bullet
8279
public GameObject GetBullet()
8380
{
@@ -90,7 +87,7 @@ public GameObject GetBullet()
9087
GenerateBullet();
9188

9289
//The new bullet is last in the list so get it
93-
MoveBulletOptimized lastBullet = bullets[bullets.Count - 1];
90+
MoveBulletOptimized lastBullet = bullets[^1];
9491

9592
//Add it to the linked list by reusing the method we use for deactivated bullets, so it will now be the first bullet in the linked-list
9693
ConfigureDeactivatedBullet(lastBullet);
@@ -106,7 +103,11 @@ public GameObject GetBullet()
106103

107104
firstAvailable = newBullet.next;
108105

109-
return newBullet.gameObject;
106+
GameObject newBulletGO = newBullet.gameObject;
107+
108+
newBulletGO.SetActive(true);
109+
110+
return newBulletGO;
110111
}
111112
}
112113
}

Assets/Patterns/18. Object Pool/Gun/Scripts/Optimized/MoveBulletOptimized.cs renamed to Assets/Patterns/18. Object Pool/Gun/Object pools/Optimized/MoveBulletOptimized.cs

+4-8
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,20 @@
44

55
namespace ObjectPool.Gun
66
{
7-
public class MoveBulletOptimized : MonoBehaviour
7+
public class MoveBulletOptimized : BulletBase
88
{
9-
private float bulletSpeed = 10f;
10-
11-
private float deactivationDistance = 30f;
12-
139
//Needed to optimize object pooling
1410
[System.NonSerialized] public MoveBulletOptimized next;
1511
//Instead of using this dependency you could use the Observer pattern because other things may happen when the bullet dies
1612
[System.NonSerialized] public BulletObjectPoolOptimized objectPool;
1713

1814

1915
void Update()
20-
{
21-
transform.Translate(Vector3.forward * bulletSpeed * Time.deltaTime);
16+
{
17+
MoveBullet();
2218

2319
//Deactivate the bullet when it's far away
24-
if (Vector3.SqrMagnitude(transform.position) > deactivationDistance * deactivationDistance)
20+
if (IsBulletDead())
2521
{
2622
//In the optimized version, we have to tell the object pool that this bullet has been deactivated
2723
objectPool.ConfigureDeactivatedBullet(this);

Assets/Patterns/18. Object Pool/Gun/Bullet Prefab.prefab renamed to Assets/Patterns/18. Object Pool/Gun/Object pools/Simple/Bullet Prefab Simple.prefab

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ GameObject:
1111
- component: {fileID: 747956090910683963}
1212
- component: {fileID: 747956090910683964}
1313
m_Layer: 0
14-
m_Name: Bullet
14+
m_Name: Bullet Prefab Simple
1515
m_TagString: Untagged
1616
m_Icon: {fileID: 0}
1717
m_NavMeshLayer: 0

Assets/Patterns/18. Object Pool/Gun/Scripts/Slow/BulletObjectPool.cs renamed to Assets/Patterns/18. Object Pool/Gun/Object pools/Simple/BulletObjectPoolSimple.cs

+19-20
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,16 @@
44

55
namespace ObjectPool.Gun
66
{
7-
//Has to inherit from MonoBehaviour so we can use Instantiate()
8-
public class BulletObjectPool : MonoBehaviour
7+
//Simplest possible object pool
8+
public class BulletObjectPoolSimple : ObjectPoolBase
99
{
1010
//The bullet prefab we instantiate
11-
public GameObject bulletPrefab;
12-
13-
//Store the pooled bullets here
14-
private List<GameObject> bullets = new List<GameObject>();
15-
16-
//How many bullets do we start with when the game starts
17-
private const int INITIAL_POOL_SIZE = 10;
11+
public MoveBullet bulletPrefab;
1812

19-
//Sometimes it can be good to put a limit to how many bullets we can isntantiate or we might get millions of them
20-
private const int MAX_POOL_SIZE = 20;
13+
//Store the pooled bullets here
14+
private readonly List<GameObject> bullets = new ();
2115

16+
2217

2318
private void Start()
2419
{
@@ -35,28 +30,30 @@ private void Start()
3530
}
3631

3732

33+
3834
//Generate a single new bullet and put it in list
3935
private void GenerateBullet()
4036
{
41-
GameObject newBullet = Instantiate(bulletPrefab, transform);
37+
GameObject newBullet = Instantiate(bulletPrefab.gameObject, transform);
4238

4339
newBullet.SetActive(false);
4440

4541
bullets.Add(newBullet);
4642
}
4743

4844

49-
//Try to get a bullet
45+
46+
//Get a bullet from the pool
5047
public GameObject GetBullet()
5148
{
5249
//Try to find an inactive bullet
53-
for (int i = 0; i < bullets.Count; i++)
50+
foreach (GameObject bullet in bullets)
5451
{
55-
GameObject thisBullet = bullets[i];
56-
57-
if (!thisBullet.activeInHierarchy)
58-
{
59-
return thisBullet;
52+
if (!bullet.activeInHierarchy)
53+
{
54+
bullet.SetActive(true);
55+
56+
return bullet;
6057
}
6158
}
6259

@@ -66,7 +63,9 @@ public GameObject GetBullet()
6663
GenerateBullet();
6764

6865
//The new bullet is last in the list so get it
69-
GameObject lastBullet = bullets[bullets.Count - 1];
66+
GameObject lastBullet = bullets[^1];
67+
68+
lastBullet.SetActive(true);
7069

7170
return lastBullet;
7271
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using UnityEngine;
4+
5+
namespace ObjectPool.Gun
6+
{
7+
public class MoveBullet : BulletBase
8+
{
9+
void Update()
10+
{
11+
MoveBullet();
12+
13+
//Deactivate the bullet when it's far away
14+
if (IsBulletDead())
15+
{
16+
gameObject.SetActive(false);
17+
}
18+
}
19+
}
20+
}

Assets/Patterns/18. Object Pool/Gun/Object pools/UnityNative.meta

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)