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!”
Nice post, Phil! I love reading these detailed posts about how you solve things 🙂
Thank you! It makes me happy that someone is reading this!
Phil
🙂
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.