
Zoomers – Crawling Zebes since 1986.
The Phaser.Group class can be used for various purposes like bulk actions (i.e. enabling gravity on all bodies in the group). It can also be used for pools. In the Metroid Remake, I use a custom pool class which is an extended Phaser.Group. All sprites, except Samus, is pooled. When an enemy is destroyed, its exists-property is just set to false, and when I need an enemy of the same kind I reset stuff like the health property and then I set the exists-property to true again.
As in my previous tutorial, I let the code do most of the explaining. Please note that this is a reduced part of the code I use in the Metroid Remake with the purpose to be as clear as possible. It’s not possible to “just run” the code. For instance, the spawn-function is a custom function described in my previous tutorial. Without actually creating a spawn function, the call will just generate an error.
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 44 45 46 47 |
/* * Pool.js * Pool.constructor(game, spriteType, instances, name) * game - Reference to the game object (Phaser.Game) * name - The name of the group to create (i.e. "bullets") (String) * spriteType - We're making a pool for sprites only. * SpriteType is the class (i.e. "Bullet"). (Phaser.Sprite) * instances - A number to create directly on construction. If you know you would probably * need 20 bullets simultaneously, you enter 20. (integer) * * Pool.create(x,y,data) * x,y - position * data - Custom data that I pass on to the spawn function * (i.e. "ice" for ice bullets) (any data type) */ class Pool extends Phaser.Group { constructor(game, spriteType, instances, name) { /* I call super (Phaser.Group) on which the Pool is extended.*/ super(game, game.world, name); this.game = game; this.spriteType = spriteType; // Needed when creating new objects in the pool if (instances > 0) { // We don't need to add anything to the group let sprite; for (var i = 0; i < maxInstances; i++) { sprite = this.add(new spriteType(game)); // Add new sprite } } return this; } create(x, y, data) { // Find the first child that has a false exist property: let obj = this.getFirstExists(false); if (!obj) { // We failed to find an availble child, so we create one now and add it to the pool. obj = new this.spriteType(this.game); this.add(obj, true); } /* We call the childs spawn method and return the object to whatever triggered this. The spawn method will handle stuff like position, resetting the health property and setting exists to true. The spawned object will live even if the returned reference is ignored:*/ return obj.spawn(x, y, data); } } export default Pool; |
So, now when we got a Pool class we can create new pools. This probably sits in a state’s create method:
1 2 3 4 |
// First, let's make some bullets. We'll need at least 20 of them at the same time. bulletsPool = new Pool(game, Bullet, 20, "Bullets"); // Zoomers are the spiky enemies. We might need them so we prepare a pool but no children. zoomersPool = new Pool(game, Zoomer, 0, "Zoomers"); |
All done! Let’s test the pools:
1 2 3 4 5 6 |
/*Fire a bullet of wave beam-type from the player position with a velocity of 100 px/sec to the right:*/ bulletsPool.create(player.x,player.y,{velocity: {x: 100, y: 0}, type: "waveBeam"}); /*Spawn a Zoomer where it's supposed to hatch. The Zoomers accepts data in string format, we are making a yellow one. Could be in the state's create method or in the update loop:*/ zoomersPool.create(hatchingPoint.x, hatchingPoint.y, "yellow"); |
When you want to kill a sprite in a pool you just set its exists-property to false and it will be available for reuse in the pool. For the bullets, it would be when they hit something or goes off screen. For the enemies you might set the exist-property to false when the health property goes down to <=0.
I hope you have enjoyed this tutorial. If you have questions or remarks, please let me know in the comments. Thank you for reading.