Object Pooling

Hello guys i’m trying to make object pooling system to spawn bullets and save performance but i have object pooling system evertyhing works but one this isn’t when i’m shooting object pooling is still active and doesn’t reseting position and active status and bullet is freezing and have delay when i’m shooting

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectPool : MonoBehaviour
{
    public static ObjectPool instance;
    [SerializeField] private GameObject bulletPrefab;
    private List<GameObject> objects = new List<GameObject>();
    private int amountToPool = 10;

    private void Awake()
    {
        if(instance == null)
        {
            instance = this;
        }
    }

     void Start()
    {
        for(int i = 0; i < amountToPool; i++)
        {
            GameObject obj = Instantiate(bulletPrefab);
            obj.SetActive(false);
            objects.Add(obj);
        }
    }

    public GameObject GetPooledObjects()
    {
      for(int i = 0; i < objects.Count; i++)
        {
            if (!objects[i].activeInHierarchy)
            {
                return objects[i];
            }
        }
      return null;
    }
}
using System.Collections;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    [Header("Prefab References")]
    [SerializeField] private float speed = 10f;
    [SerializeField] private Rigidbody2D rb;
   

    void Start()
    {
        rb.velocity = transform.up * speed;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Enemy"))
        {
            collision.GetComponent<Enemy>().DamageTake();
            gameObject.SetActive(false);
        }
    }
}
using Unity.Burst.CompilerServices;
using UnityEngine;
using UnityEngine.UI;

public class Shooting : MonoBehaviour
{
    [Header("Prefab References")]
    [SerializeField] private Transform firePoint;
    [SerializeField] private AudioSource shoot;
    [SerializeField] private AudioSource reload;
    [SerializeField] private UIManager uiManager;
   
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Shoot();
            shoot.Play();
        }

        if (Input.GetKeyDown(KeyCode.R))
        {
            ReloadAmmo();
            reload.Play();
        }
    }

    private void Shoot()
    {
        GameObject bullet = ObjectPool.instance.GetPooledObjects();

        if (bullet != null)
        {
            bullet.transform.position = firePoint.position;
            bullet.transform.rotation = firePoint.rotation;
            bullet.SetActive(true);

            Debug.Log("Shooting");
        }
        else
        {
            Debug.Log("Bullet is null");
        }

        uiManager.DecreaseAmmo();

        if (!uiManager.HasAmmo())
        {
            Debug.Log("Out of ammo! Stop shooting.");
        }
    }

    public void ReloadAmmo()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            uiManager.ReloadAmmo();
            Debug.Log("Ammo reloaded");
        }
    }
}

The pattern with an object pool is that you release the objects back into the pool when done with them.

Take Unity’s built in Object Pool: Unity - Scripting API: ObjectPool<T0>

It has Get() and Release() methods. So you should have a proper method of releasing bullets back into the pool.

Or just use Unity’s built in Object pool. No need to reinvent the wheel.

And also get ready for Kurt to tell you not to use pooling.

4 Likes

Thanks

Please use the Scripting forum. Object pooling is not a 2D specific subject. I’ll go ahead and remove all the 2D tags like tilemap, 2d physics (etc) that are added too.

I’ll move your thread.

Thanks.

Before you wreck your project with pooling for what likely won’t make any difference unless you have many thousands of bullets…

The costs and issues associated with object pooling / pools:

https://discussions.unity.com/t/892797/10

https://discussions.unity.com/t/833104/2

In very rare extremely-high-count object circumstances I have seen small benefits from pooling.

In 100% of ALL circumstances, object pooling is a source of constant bugs and edge case disasters.

In 100% of ALL circumstances, object pooling is a source of constant bugs and edge case disasters.

In 100% of all circumstances one can safely ignore postings that contain unsubstantiated claims such as as the one above. Object pooling is used in more than just Unity projects or C#. A standard Unity solution was created because pooling is in fact beneficial in may cases. Solutions using pooling run all the time, they are running now and without constant bugs.

There are bugs in if/then and switch statements and off-by one array errors. Nobody stops using those conventions because somebody, somewhere messed up.

2 Likes

In fairness:

  • Object pooling can quite easily cause bugs when used on a large scale, because it is usually the case that whenever you introduce a new variables to any class that is pooled, you have to remember that it is pooled, and you have to remember to go to the method responsible for resetting its state and add the code in to reset those new variables. Also holding onto references to any objects that use pooling becomes dangerous, which can be tricky to handle.
  • Having lots of if-else logic is commonly considered a code smell and something to be avoided. There are code analysis tools that figure out the cyclomatic complexity of methods in a code base and can warn the user if there is too much branching. There are also many books and articles written with advise about how to refactor your code to avoid that.

I think there is truth to the claim that using the object pool pattern can come with a costs, and I think it is good to keep this in mind.

However, for something like bullets, I think using the object pool pattern is pretty much just perfect. It should be relatively easy to reset the states of simple bullets, and it’s unlikely that you’ll have to go revisit that code that many times during the development of the project.

But when all entities, tiles and blocks in the world use object pooling, and you can have entities nested inside other entities, and entities can pass entities to other entities, and it’s a multiplayer game so the server can at any point decide that any entities in the world should no longer exist… in such a situation, in my experience, the use of object pooling can cause new bugs to pop up continuously, like once every couple of months or so.

1 Like

Tley, you’re right. I’m going to amend that in my blurb.

It really is more like high 90%s to me.

The main thing I try to conveny is:

For most people who:

  • want to implement pooling
  • have an issue understanding / implementing pooling

… those people are almost 100% guaranteed NOT working in a context that would benefit from pooling, with the odd exception of someone working on a bullet hell game for older Android devices, for example.

Agreed… and still the problem is clearly conveying that “if you add any more state variables you must reset them” requirement. Too many times someone will attach already-existing code to an object that later became pooled… and now we have a bug!

The first instantaneous bullet bug example that comes to mind is you add a “tracker / heatseeker” to your bullet that lets you target and track a specific enemy… now that needs to be reset or bad stuff happens when a formerly-tracking bullet is recycled in non-tracking mode.

1 Like

I hate this extremism. When you add a new statement to any scripts, you are risking introducing new bugs so you should not add new statements to any scripts 100% of the time.

Come on guys…

4 Likes

And can we seriously not have this conversation every time pooling comes up?

Lets just help people with their problems instead of having the same tired old debate.

5 Likes

These conversations should be had, because people keep getting distracted with random new things, instead of completing their game.

1 Like

Wouldn’t call pooling a “random system”. It’s a well known pattern that solves a common problem, and is trivial to implement. Especially since Unity gives you a working one out of the box.

2 Likes

All the SQL queries that return unexpected results are due to someone either not understanding SQL well or with an inadequate understanding of the database schema. We do not abandon SQL regardless of its perceived difficulties (real or imagined). One typically assigns the task to someone who does in fact know the system and/or the SQL is reviewed (and adequately tested) before being released. 2D, 3D and VR games/projects are not different in that regard.

Nobody was born knowing SQL and similarly nobody is born knowing software patterns. One learns them typically by implementing them. Let’s let people learn especially when it isn’t our project to worry about. :smile:

I haven’t tried it yet, but couldn’t you just use one script with a list of float3/vector3, a foreach raycast and a VFX to display the bullets.

Wouldn’t that be much faster then say 10k gameobjects using a pool?

Faster? Yes. Instancing is also faster. But if you need for example physics… There are always multiple considerations when you choose any kind of technology or methodology. ObjectPools are for rapid game objects instantiation and destruction when you need to alleviate the garbage collection pressure.

2 Likes

If physics are a problem, you can use the c# job version. It’s much faster and generates no garbage.

So much for not getting distracted by random new things…

2 Likes

I’m not at the computer so I can afford to be distracted.

A quick diversion back to your original post… I will suggest that you resist to define “working” as most of the stuff is doing what you want except for some stuff. :slight_smile: There are some questionable things in the code you’ve posted which a) is likely the cause of the trouble you are having and b) likely to be the cause of problems in the future.

We can begin with your definition of the ObjectPool. Basically a Singleton but not using an effective method of construction. instance (lowercase naming is atypical) but more importantly it is public. So it can be re-assigned by other code. You probably won’t do that but it can be done so it shouldn’t be public.

Your list of pool items is named objects but they are bullets right? You’ve defined them as a list of GameObject but could a list of Bullet be handier and more expressive?

When you want to get an object from the pool the method is named GetPooledObjects (plural). First it only returns one so the name is slightly off but generally speaking one doesn’t want to highlight that the object is in fact pooled. You ask the pool for an object and it returns one. GetBullet would do that, other code doesn’t need to know if retrieved from the pool or spawned a new one or whatever. “Hide unnecessary details”. Walking the list as an array is a bit clumsy in my view.

1 Like

Your Bullet class applies velocity in Start. I’m not certain that is the place to apply it won’t it start moving immediately?

In your Shooting class (typically the name should be a noun) Update checks for KeyCode.R and then calls ReloadAmmo which checks for KeyCode.R. It won’t reload in that case but the sound plays in either case.

If you call Shoot and you are out of bullets (you get a null) you still call DecreaseAmmo. Don’t know what that method does but seemingly it wouldn’t decrease if it couldn’t fire. Not sure why the pool of bullets wouldn’t be the authority for whether was any ammo left. And similar to reload it looks like it will play the shoot audio even if you have not shot anything.

In any case… I hope you see this as informational. Pooling may not have been your main concern here. Getting the logic correct and “then” improving performance is the way to go.

1 Like