Here’s the second half of the story – how to recycle dead bullets. I added a bullet cleanup system that checks for collisions with platforms, and for bullets that have gone past the visible edge of the world.
protected void process(Entity e) {
BulletComponent bulletComponent = Components.getBulletComponent(e);
if (!bulletComponent.active()) {
return;
}
// If the bullet is touching a platform, deactivate
PhysicsComponent physicsComponent = Components.getPhysicsComponent(e);
if (PhysicsContactChecker.isTouching(physicsComponent, Label.Platform) != null) {
Bullet bullet = bulletComponent.getBullet();
bullet.deactivate();
return;
}
// If the bullet is past the right edge of the screen, deactivate
SpatialComponent spatialComponent = Components.getSpatialComponent(e);
Vector2 position = spatialComponent.getPosition();
float width = spatialComponent.getSpatial().getWidth();
if (position.x - width / 2 > camera.position.x + camera.viewportWidth * camera.zoom / 2) {
Bullet bullet = bulletComponent.getBullet();
bullet.deactivate();
return;
}
}
To make this work, I had to add some more data to the Bullet class. I spent quite some time this morning working on this and wondering why it wasn’t working. Once a bullet was deactivated, it appeared that it would never get activated again. I finally realized that it was activating, but then immediately deactivating. And the reason is the contact / collision data. I’m maintaining contact data in the physics component, outside of box2d. When I deactivate the box2d body, the contacts are deleted in the box2d world, but I wasn’t clearing out my own contact data. As a result, when I put the bullet back into the game, even though I’d moved it to a new position, it still had the old contact data from when it collided with the platform. So the collision code fired again, and the bullet got recycled immediately.
Once I figured that out, the fix was simple – when deactivating a bullet, clear the contact data in the associated physics component. This requires saving the physics component in the bullet. I might come back and re-think this at some point, it feels like there might be too much data shared between various classes. Anyway, here’s the new Bullet class:
protected BulletPool pool;
protected boolean active;
protected PhysicsComponent physicsComponent;
public void activate(Vector2 position) {
active = true;
Body body = physicsComponent.getBody();
body.setTransform(position, 0);
body.setActive(true);
}
public void deactivate() {
physicsComponent.getContact().clearAllContacts();
Body body = physicsComponent.getBody();
body.setTransform(-10, -10, 0);
body.setActive(false);
pool.free(this);
active = false;
}
public boolean active() {
return active;
}
}
When deactivating, clear contacts, mark the body inactive, and move it out of the way. Activating the body does the inverse – mark it active, and place it where it needs to be. With this approach, I don’t really need to mark the renderable invisible – an inactive bullet will never be picked up by the camera.
This seems to work fairly well. I added a log to check the high water mark of my bullet pool. Even when I’m being obnoxious with the ‘fire’ key, it’s hard to cross 20-25 bullets. That should be a manageable number, both for Artemis and for box2d.
Related posts:

[...] life cycle is really short. Ziggy made two blog posts about this topic some weeks ago here and here, however we followed a slightly different approach and we will explain it in this [...]