25th Feb 2011

Tip #3 – Flixel – Bullet Manager Part 1

If you are coding a shoot-em-up, or even a platformer with guns, then you’ll have a need for the player to be able to fire bullets. Or the enemies to fire at you. This tip is about creating a Bullet Manager class. The class is responsible for the launch, update, pooling and re-use of bullets.

Object Pool

Creating new objects in Flash is expensive. By “new objects” I mean code such as:

var bullet:Bullet = new Bullet(); 1 var bullet : Bullet = new Bullet ( ) ;

… which creates a brand new object an assigns it to bullet.

And by “expensive” I mean it takes time for Flash to process the request for the new object, assign memory to it and create it. If you are firing off tens of bullets every few seconds this can take its toll. And if you don’t actively clean-up the objects created you can quickly run out of resources.

To mitigate this problem we create a “pool”. This is a pool of resources (in our case bullets) that the Bullet Manager can dip in to. It will look for a free bullet, and recycle it for use in the game. When the bullet has finished doing what bullets do best, it will free itself up for use again. By using a pool you avoid creating new objects on the fly, and help keep memory in check.

Meet FlxGroup

Thankfully flixel has a class you can use to make this process simple. It’s called FlxGroup. You can add objects to a group, there are plenty of functions for getting the next available resource, and you can even perform group to group collision. Objects in a group are all rendered on the same layer, so are easy to position within your game. The first task is to create a pool of bullets to draw from.

In this example we’ve got a class called Bullet.as. Bullet extends FlxSprite with a few extra values such as damage and bullet type.

package { import org.flixel.FlxSprite; public class Bullet extends FlxSprite { [Embed(source = '../assets/bullet.png')] private var bulletPNG:Class; public var damage:int = 1; public var speed:int = 300; public function Bullet() { super(0, 0, bulletPNG); // We do this so it's ready for pool allocation straight away exists = false; } public function fire(bx:int, by:int):void { x = bx; y = by; velocity.y = -speed; exists = true; } override public function update():void { super.update(); // Bullet off the top of the screen? if (exists && y < -height) { exists = false; } } } } </code> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package { import org . flixel . FlxSprite ; public class Bullet extends FlxSprite { [ Embed ( source = '../assets/bullet.png' ) ] private var bulletPNG : Class ; public var damage : int = 1 ; public var speed : int = 300 ; public function Bullet ( ) { super ( 0 , 0 , bulletPNG ) ; // We do this so it's ready for pool allocation straight away exists = false ; } public function fire ( bx : int , by : int ) : void { x = bx ; y = by ; velocity . y = - speed ; exists = true ; } override public function update ( ) : void { super . update ( ) ; // Bullet off the top of the screen? if ( exists && y < - height ) { exists = false ; } } } } < / code >

The important part is that the bullet sets exists to false when created to make it immediately available for use. When fired the bullet will travel up the screen. The check in the update method simply sets the bullet to not exist once it has passed y 0 (the top of our screen).

So far, so simple. Next up is the BulletManager.as class. This extends FlxGroup. It begins by creating a pool of 40 bullet objects and adding them to the group ready for use. As all of them have exists equal to false none will render yet.

package { import org.flixel.*; public class BulletManager extends FlxGroup { public function BulletManager() { super(); // There are 40 bullets in our pool for (var i:int = 0; i < 40; i++) { add(new Bullet); } } public function fire(bx:int, by:int):void { if (getFirstAvail()) { Bullet(getFirstAvail()).fire(bx, by); } } } } </code> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package { import org . flixel . * ; public class BulletManager extends FlxGroup { public function BulletManager ( ) { super ( ) ; // There are 40 bullets in our pool for ( var i : int = 0 ; i < 40 ; i ++ ) { add ( new Bullet ) ; } } public function fire ( bx : int , by : int ) : void { if ( getFirstAvail ( ) ) { Bullet ( getFirstAvail ( ) ) . fire ( bx , by ) ; } } } } < / code >

It has one extra method called fire. This gets the first available bullet from the pool using getFirstAvail (the first bullet with exists equal to false) and then launches it from the given x/y coordinates by calling the bullets fire function.

Are you feeling lucky?

I insert the Bullet Manager into the games Registry (see Tip #1 if you don’t know what the Registry is) so my Player class has easy access to it. My Player class is a simple sprite with keyboard controls to move it around. When you press CTRL it will fire a bullet:

Registry.bullets.fire(x + 5, y); 1 Registry . bullets . fire ( x + 5 , y ) ;

The Bullet Manager handles the request and launches a bullet up the screen. The +5 after the x value is just to visually align the bullet with the middle of the space ship, otherwise it’d appear off to the left.

At the beginning of the Bullet Manager class I hard-coded in a limit of 40 bullets. That is enough for my game. As you can see in the screen shot above I’m only using 32 bullets out of a pool size of 40.

This value may not be suitable for your game. I don’t know what value you need, only you do. Perhaps you are coding the next Ikaruga, in which case you probably need 40 bullets per pixel The thing is, you can tweak this as needed. And you can tweak it in-game too. It’s not a value that should change dynamically, but it could easily change from level to level as the game progresses.

Download

This example is about as simple as I could make it, but hopefully you can see the benefits already. In the next tip I’ll add group collision detection and something for you to shoot at.

Download the source code and SWF from the new Google Code Project page.