A Game A Month #1: Update – Nearly finished!

A Game A Month #1: Update – Nearly finished!

Object pooling in a LibGDX game

As stated in the last blogpost, object pooling is very important to keep the overhead of the java garbage collector low. It allows us to respawn objects that are “dead” instead of creating new instances. The SpawnPool class can manage as much pools as you want. For example enemies, missiles, items, etc.

The Object Pooling of this game consists of three files: SpawnPool.java, SpawnObject.java and SpawnType.java.

It works as follows: Suppose we have this new class Obstacle.java, which we want to be spawned many times. At first we create an instance of the SpawnPool. In Gameplay.java, we add the following line somewhere above the constructor:

public static SpawnPool spawnPool = new SpawnPool();

We define it as static, as we want to access it from many other classes.

Then we create a new ArrayList which will be our pool for the obstacles.

ArrayList<SpawnObject> obstacles = new ArrayList<SpawnObject>();

We add this obstacle pool to the SpawnPool instance (in the Gameplay.java init() method):

spawnPool.add(obstacles, SpawnType.Obstacle);

SpawnPool holds all pools that have been added in a HashMap, where the Key is the given SpawnType (here: Obstacle)

The scond parameter at spawnPool.add(…) is also the class name which defines the type of this particular pool. The SpawnPool needs to know the class name of the object it has to spawn. This line of code says: Add this ArrayList obstacles which holds SpawnObjects of class Obstacle.

Furthermore, we need to take a look at the SpawnType enum. In SpawnType are listed all classes that need to be spawnable. So we have to add the Obstacle class here, too:

public enum SpawnType {..., ...., Obstacle} 

Since our pool is an ArrayList of type SpawnObject, we have to implement the SpawnObject interface and following code into Obstacle.java to make the obstacles spawnable and killable:

public class Obstacle extends GameObject implements SpawnObject {

    private boolean spawned;
    
    public Obstacle(String arg) { //constructor
        //SpawnPool.java needs this parameter of type String
        //for creating an instance
        super();
    }

    public void init(int type, float posX, float posY) {
        //set animation and more, according to type
        //set position
    }

    @Override
    public void setSpawned(boolean spawned) {
        this.spawned = spawned;
    }

    @Override
    public boolean isSpawned() {
        return spawned;
    }

    @Override
    public void kill(SpawnPool pool) {
        //despawn
        pool.returnToPool(this);
    }

    @Override
    public void update(float delta) {
        //update position and animation
        super.update(delta);

        //handle own udpates:
}

The kill() method calls returnToPool() of SpawnPool.java, which marks this object as not spawned anymore. This is the way we despawn objects.

Now let’s spawn an obstacle! All we need to do is call the spawnPool.getFromPool() method in Gameplay.java. If the pool of the given type (here: Obstacle) is empty, this method dynamically creates a new instance of the type and adds it to the according pool.

//get enemy from pool
Obstacle o = (Obstacle) spawnPool.getFromPool(SpawnType.Obstacle);
//initialize with given obstacle type and position
o.init(type, posX, posY);

Spawning Objects: getFromPool()

This is how the getFromPool() method works:

public SpawnObject getFromPool(SpawnType type) {
    //Get pool of the given spawntype
    ArrayList<SpawnObject> pool = pools.get(type);
    if (pool == null) {
        System.err.println("Pool of type " + type.name() +
                " doesn't exist. Maybe forgot to create an" +
                " ArrayList instance for that pool?");
        return null;
    }
    //try to get a free object to spawn
    SpawnObject found = null;
    for (int i = 0; i<pool.size(); ++i) {
        //find one that is not spawned yet
        if (!pool.get(i).isSpawned()) {
            found = pool.get(i);
            break;
            // else continue for-loop to find another spawn
        }
    }
    if (found != null) {
        found.setSpawned(true);
        return found;
    }

    //No free spawn found, so create a new instance of 
    //that spawntype
    System.out.println("Pool of " + type.name() + 
            " contains no unspawned object. " +
            "Creating new Instance...");
    SpawnObject spawn = createSpawnObject(type);

    if (spawn == null) {
        System.err.println("Instance of Spawn of type " 
                + type.name() + " could not be created");
        return null;
    }

    //Mark this instance as spawned
    spawn.setSpawned(true);

    //And add it to the pool
    pools.get(type).add(spawn);

    //Debug
    //printPoolSize();

    return spawn;
}

private SpawnObject createSpawnObject(SpawnType type) {

    System.out.println(" - creating instance of " +
            "type.name(): " + type.name());

    //Prepare classname
    String className = nameOfPackage + type.name();
    try {
        Class<?> c = Class.forName(className);
        //debug
        //System.out.println(" - Class: " + c.getName());

        Constructor<?> ctor = c.getConstructor(String.class);
        //create new instance (there has to be at least 
        //one arg, otherwise it doesn't work...)
        Object object = ctor.newInstance("");
        SpawnObject created = (SpawnObject) object;
        return created;

    } catch(Exception e) {
        System.err.println(e);
        System.err.println("Type name: " + type.name());
        System.err.println("Class name: " + className);
    }
    return null;
}

Update and render

To update and render all spawned objects, we should call the update() and draw() methods of the spawns (at Gameplay.java):

...
update(float delta){
    for (SpawnObject o: obstacles) {
        if (o.isSpawned())
            o.update(delta);
    }
}

draw(SpriteBatch sb) {
    for (SpawnObject o: obstacles) {
        if (o.isSpawned())
            o.draw(sb);
    }
}

Feel free to check out the whole SpawnPool class on Github.

Collisions

Pooling objects this way has a helpful sideeffect: Since we have own Lists for every type of spawn objects, we can use them for managing collisions, too.

We can simply use the pools for checking collisions between

  • enemies and missiles (of player)
  • the player and enemies
  • the player and obstacles
  • the player and missiles (of enemies)

That’s all!

In my opinion this is a very clean way for managing and respawning many different game objects. Maybe there are better ways to solve this, but it works! Give me your feedback and stay tuned for more!

Meanwhile check my Twitter for updates!

3 thoughts on “A Game A Month #1: Update – Nearly finished!

      1. 🙂
        Getting people to a blog isn’t easy. But here’s still little content. When you’ve published more articles you’re going to get more traffic from Google. Until then, just stick it out.

Leave a Reply

Your email address will not be published.