ObjectPooling_in_ParticleSystems_featured

Have you ever wondered how a Particle System can benefit from Object Pooling? If you’re not familiar with what that is, basically it’s a fancy programming term for reusing recyclable objects in a system that requires many instances with a certain lifespan.

Some great examples of particles types that could benefit from Object Pooling (although some violent!) are: blood splatters, bones, explosion debris, sparks, smoke, rain, etc.

I’ve recently had the opportunity to recreate such a system for our most recent AIR Mobile game, and I think this one will be much better!

Here is a simple example.

Click to add more emitters, and use the “SPACE ” key to stop all emitters.

This area requires
Adobe FlashPlayer version 11.2 or above.
iOS Devices are not currently supported.

You can hit SPACE  repeatedly to see how the Object Pool refills over time.

How does it work?

Normally, how does an object get created?

You use the new keyword to instantiate it… right!

You may have noticed that some Class constructors require parameters, but generally for integrating recyclable objects – it’s much easier to stick to objects with parameterless  constructors.

The difference between creating and reusing an existing object is pretty easy to understand. When you require an object of a certain type, you will request it from a PoolManager. This guy will then look inside it’s pools of “available” objects to see if it can give you one, and if not – it will just create many more and give you one of those!

Of course, when I say that the PoolManager creates more objects – it has no choice to use the new keyword. We basically hide the “instantiation” of objects in the PoolManager class.

So if we continue to request objects, isn’t the Pool just going to keep on expanding?
Well… Yes, absolutely! That is precisely why we need to implement a returning mechanism.

When an object is no longer needed, instead of nullifying it and leaving it to the Garbage-Collector… return it to the PoolManager. He will then mark it as “available” for future use; next time you make a request for that object type.

How does the Particle System use it?

Every time a request for more particles is made, the ParticleSystem grabs a given amount of available particles of the type specified (in the PoolManager class), assigns each particle their initial data (position, color, velocity, random values), and stores them in a list that gets iterated on every frames. This cycle applies various forces to the particles, decreases the lifecycle of each one, and moves it’s visual representation on the screen.

Once a particle’s lifetime reaches zero or below, it is ready to go back in the pool.

You may want to trigger methods on the particle when it gets reused and recycled, such as added() and removed() respectively. This would be useful, for example… to add the visual DisplayObject of the particle on the Stage and then remove it once it is recycled.

Is it really that Simple?

It can be! But… you may want to go the extra mile and consider how each particles will animate over time, which systems and/or groups they belong to, and whether or not it should continue to reappear infinitely.

To give you an idea of the design of my Particle System, here’s a diagram that explains the important classes used.

Basically, all the developer needs to do is make requests on the ParticleManager. Everything else is handled internally. Particles are created by telling the manager how many are needed, and passing the “preset” properties for where they need to appear. The preset may also indicate to the ParticleGroup if it should play infinitely, and how many particles to emit every given # of seconds. The properties may also hold variance / random-ranges for many aspects of the particles.

Start and End values for color, alpha, rotation and scale may very well be something you want to implement as well.

Last but not least, to eliminate the groups with infinite particles emitting out of’em, I’ve made them assignable with an ID number, that way I can quickly find the group and tell it to stop (while resuming the particles already emitted).

Code Sample

For the curious minds, here is how the above system is used.

package com.bigp.particlesystem {
	import com.bigp.particlesystem.Particle;
	import com.bigp.particlesystem.core.ParticleForce;
	import com.bigp.particlesystem.core.ParticleGroup;
	import com.bigp.particlesystem.core.ParticleManager;
	import com.bigp.particlesystem.core.ParticleSystem_Base;
	import bigplugin.utils.KeyUtils;
	import bigplugin.utils.ObjectUtils;
	import bigplugin.utils.PoolingUtils;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.ui.Keyboard;
	import flash.utils.getTimer;
	/**
	 * ...
	 * @author Pierre Chamberlain
	 */
	[SWF(width="640",height="480",backgroundColor="0x999999",frameRate="60")]
	public class Test extends Sprite {
		public static const _DEBUG_LABEL_TEXT:String = "Hit Space to stop the groups of particle emitters and View stats.";
		
		public static var STAGE:Stage;
		public static var INST:Test;
		
		private var manager:ParticleManager;
		private var properties:Object;
		
		private var lastClick:int =	-1;
		private var debugLabel:TextField;
		
		public function Test() {
			stage ? init() : addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		private function init(e:Event=null):void {
			e && removeEventListener(Event.ADDED_TO_STAGE, init);
			
			INST =	this;
			STAGE =	this.stage;
			
			KeyUtils.initInst(STAGE);
			KeyUtils.INST.bindKey(Keyboard.SPACE, onSpaceDown);
			
			properties =	{
				x:				stage.stageWidth * .5,
				y:				stage.stageHeight * .5,
				infinite:		{amount: 2, delay: 0.7},
				velocityY:		-5,
				randomX:		20,
				randomY:		15,
				colorStart:		0xff0000,
				colorEnd:		0x0000ff,
				lifetime:		6,
				id:			1
			}
			
			PoolingUtils.initInst();
			
			manager =	new ParticleManager();
			manager.registerSystemToType(
				new ParticleSystem_Base( onParticleUpdate ),
				Particle,
				ParticleForce.makeList(
					0, 10, ParticleForce.OPERATION_ADD
				)
			);
			
			stage.mouseChildren =	false;
			
			stage.addEventListener(Event.ENTER_FRAME, onUpdate);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, onClick);
			
			addDebugLabel();
		}
		
		private function addDebugLabel():void {
			debugLabel =				new TextField();
			debugLabel.selectable =		false;
			debugLabel.autoSize =		TextFieldAutoSize.LEFT;
			debugLabel.defaultTextFormat =	new TextFormat("Arial", 9, 0, true);
			debugLabel.text =			_DEBUG_LABEL_TEXT;
			
			debugLabel.x =		4;
			debugLabel.y =		4;
			
			stage.addChild(debugLabel);
		}
		
		public function onSpaceDown():void {
			manager.stopGroupByID(1);
			
			debugLabel.text =	_DEBUG_LABEL_TEXT + " - " + PoolingUtils.INST.report();
		}
		
		private function onParticleUpdate(pParticle:Particle, pGroup:ParticleGroup, pSystem:ParticleSystem_Base):void {
			if ((pParticle.x > stage.stageWidth && pParticle.velocityX > 0) ||
				(pParticle.x < 0 && pParticle.velocityX < 0)) {
				pParticle.velocityX *= -.9;
				pParticle.x = pParticle.x < 0 ? 0 : stage.stageWidth;
			}
			
			if ((pParticle.y > stage.stageHeight && pParticle.velocityY > 0) ||
				(pParticle.y < 0 && pParticle.velocityY < 0)) {
				pParticle.velocityY *= -.9;
				pParticle.y = pParticle.y < 0 ? 0 : stage.stageHeight;
			}
		}
		
		private function onClick(e:MouseEvent):void {
			if (lastClick>-1 && (getTimer() - lastClick) < 200) {
				return;
			}
			
			lastClick =	getTimer();
			
			properties.x =	e.stageX;
			properties.y =	e.stageY;
			
			manager.generateType( Particle, 1, ObjectUtils.clone(properties) );
		}
		
		private function onUpdate(e:Event):void {
			manager.update();
		}
	}
}

If you would like the full source-code, do leave a comment below. I’ll post it depending on how many are interested to see it.

I hope this tutorial gave you a better idea of how to recycle your objects and how Object Pooling can be used to store tons of particles!

Thanks for reading!