Coding tutorial: Object pool pattern

Space battle object pool pattern tutorial

Learn how to code the object pool pattern by pre-allocating memory and reusing objects. Which can greatly improve performance when reusing short lived objects like bullets and particles.

Introduction

This tutorial will show you how to create and manage a pool of bullet objects. For example, this is useful in shooter and bullet hell games which have thousands of bullets on the screen.

The tutorial is written in the Java programming language, and uses the free Processing graphics library and integrated development environment.

The object pool pattern can be especially useful with programming languages which use automatic garbage collection like Java, C#, JavaScript, Python, etc.

Since automatic garbage collection can stall your program and reduce your frame rates. The object pool pattern gives you more control over when the garbage collector comes to reclaim the memory.

The downside of the object pool pattern is that it complicates the life cycle of the object. Meaning you need to reset the variables of the object before you can reuse it. Since its variables are unlikely to match the defaults after repeated use.

There are a few ways to implement the object pool pattern, this tutorial will show you one method.

Timer class

First create a small class called ‘Timer’. We will use this to check on how long the bullet has been alive. The ‘start’ data member and ‘Start’ function record the time when the timer was started.

class Timer
{
  int start = 0;
  
  void Start()
  {
    start = millis();
  }
  
  int GetTime()
  {
    return millis() - start;
  }
}

The ‘GetTime’ function returns the difference between the current time and the time since the timer was started.

Note that the ‘millis’ function in Processing returns the number of milliseconds since running the program.

Bullet class

Next create a class called ‘Bullet’.

The ‘active’ boolean controls whether the bullet is currently active or not. An active bullet will behave like a normal bullet, moving across the screen. Whilst an inactive bullet will do nothing.

We also create a timer data member and declare a constant called ‘lifetime’ which is a value in milliseconds to match the timer. You can of course make the lifetime of each bullet vary, by changing the constant to a variable. Just remember to reset this value in the ‘OnSpawn’ function later if you choose to do so.

class Bullet
{
  boolean active = false;
  Timer timer = new Timer();
  final int lifeTime = 3000;

  void OnSpawn();
  void Die();
  void Update();
  void Render();
}

The bullet class has four functions:

  1. OnSpawn
  2. Die
  3. Update
  4. Render

OnSpawn

The ‘OnSpawn’ function is called when the bullet is spawned. It is used to reset the bullet’s variables back to the defaults of when the object was first instantiated. We also use this function to start the timer, since we want to record the lifetime of the bullet.

  void OnSpawn()
  {
    active = true;
    timer.Start();
  }

Die

The die function is called when the bullet’s life has come to an end. Usually this would mean the bullet has either hit something or timed out.

  void Die()
  {
    active = false;
  }

Update

The update function is called to update the bullet logic. This happens once per frame.

  void Update()
  {
    if(!active) return;
    
    // Move the bullet here
    
    // Check the bullet lifetime
    if(timer.GetTime() > lifeTime)
    {
      Die();
    }
  }

Notice how we early out if the bullet is inactive.

if(!active) return;

Lastly we check if the bullet has expired. We check how much time has passed since the bullet was spawned, and compare this to the lifetime constant we declared earlier.

    // Check the bullet lifetime
    if(timer.GetTime() > lifeTime)
    {
      Die();
    }

I leave the implementation of collision checking of the bullet class to the reader.

Render

The render function will either be called as often as possible or at a fixed rate. Since this is Processing, the frame rate is locked by default.

  void Render()
  {
    if(!active) return;
    
    // Render an image or shape here
  }

Like with the ‘Update’ function, the ‘Render’ function will early out if the bullet is inactive.

if(!active) return;

Bullet pool

Next we will create a class called ‘BulletPool’. The bullet pool will manage a pool of bullets.

class BulletPool
{
  ArrayList<Bullet> bulletList = new ArrayList<Bullet>();
  
  void Init(int number);
  Bullet Spawn();
  void Update();
  void Render();
}

We store the bullets in an ‘ArrayList’, which is a resizable array.

ArrayList<Bullet> bulletList = new ArrayList<Bullet>();

The class has four functions:

  1. Init
  2. Spawn
  3. Update
  4. Render

Init

The ‘Init’ function is used to initialise the class, so this should be called only once.

  void Init(int number)
  {
    bulletList.clear();

    if(number < 1)
    {
      println("Error: Number of bullets is less than 1.");
      println("Setting number of bullets to 1.");
      number = 1;
    }
    
    for(int i=0; i<number; ++i)
      bulletList.add(new Bullet());
  }

We first start by clearing the list, in case this function has been called before. It should ideally be called once in the Processing ‘setup’ function.

bulletList.clear();

Next we will make sure the number of elements is set to at least one. Since we can’t have a pool without any objects!

if(number < 1)
    {
      println("Error: Number of bullets is less than 1.");
      println("Setting number of bullets to 1.");
      number = 1;
    }

Finally we just need to create bullets equal to the number passed in.

for(int i=0; i<number; ++i)
      bulletList.add(new Bullet());

Spawn

The spawn function will return an inactive particle for use, and reset it’s variables by calling the bullet’s ‘OnSpawn’ function.

  Bullet Spawn()
  {
    for(Bullet b : bulletList)
    {
      if(!b.active)
      {
        b.OnSpawn();
        return b;
      }
    }
    
    // If there are no bullets available, use the first bullet
    println("Warning: Ran out of bullets, using the first one");
    Bullet b = bulletList.get(0);
    b.OnSpawn();
    return b;
  }

If there are no bullets available, then we will reuse the first one. However you could code this differently by expanding the pool or throwing an error. Just remember if you expand the pool size, then you will most likely want to determine an upper limit on the number of pool objects, otherwise you will eventually run out of memory!

Update

The update function calls the update function of all the bullets.

  void Update()
  {
    for(Bullet b : bulletList)
    {
      b.Update();
    }
  }

Note we could check if the bullet is active here, instead of in the bullet class if we wanted to.

Render

The render function calls the render function of all the bullets.

  void Render()
  {
    for(Bullet b : bulletList)
    {
      b.Render();
    }
  }

Note we could check if the bullet is active here, instead of in the bullet class if we wanted to.

Using the bullet pool

Now that we have a bullet pool and bullet class, we can now make use of them.

We first want to create an instance of our bullet pool.

BulletPool bulletPool = new BulletPool();

Next we will want to call our bullet pool initialiser function inside of the Processing ‘setup’ function, which gets called once. You should tailor the number of bullets based on how many you need.

bulletPool.Init(1000);

In the Processing ‘draw’ function we need to call the ‘Update’ and ‘Render’ function for the bullet pool. Notice how the code is split up into logic first, then rendering.

// Logic
bulletPool.Update();

// Render
background(200);
bulletPool.Render();

So the ideal place to spawn a bullet, would be before the bullet pool ‘Update’ function is called. A bullet can be spawned by calling the bullet pool ‘Spawn’ function.

Bullet bullet = bulletPool.Spawn();

Finally you can set the properties of the bullet, like position and velocity.

bullet.SetPosition(x, y);
bullet.SetVelocity(1, 0);

Conclusion

Congratulations on completing this tutorial. You have learned how to implement the object pool pattern, by pre-allocating a large number of objects. You now know how to improve performance by reusing many short lived objects like bullets and particles.

Thanks for reading, see you in the next post!

Learn another way to code the object pool pattern in this tutorial. It shows you how to manage a pool of books in Visual Studio, using the C++ programming language.

Leave a comment