Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

Quick Tip: Add a Blurry Trail Effect to Your Bullets

by
This post is part of a series called Shoot-'Em-Up.
Build a Stage3D Shoot-'Em-Up: Interaction
Starling Particle Effects for Stage3D Shooter Games

In this Quick Tip you will learn how to use BitmapData's copyPixels() method to create a very fast blurry trail effect for the bullets in your shoot-'em-up games.


Final Result Preview

Let's take a look at the final result we will be working towards:

Use the arrow keys or WASD to move the ship, and hit the space bar to fire bullets towards the mouse cursor.


Step 1: Introduction and Basics of Blitting

We'll quickly (really quickly) go through the very basic idea used in blitting before we move on, since we will be using it in this Quick Tip.

Copying pixels on the screen is the core of blitting. In AS3, it is done by copying a rectangular region of pixels of a BitmapData to another BitmapData, using BitmapData.copyPixels().

Copying pixels from an image

The image above illustrates exactly that. We copy the pixels of a rectangular region from a BitmapData and put it into another one.

The idea we will be exploring in this Quick Tip is to copy everything that needs a blur effect applied into a container and apply post-blitting effects to create the effect we want.


Step 2: The Bitmap Container

There is already a very basic code for a space shooter game already done in the source files, since this isn't the focus of this post. There is only a ship that moves with the WASD or arrow keys. The code is very commented and is very basic, though, so you probably won't have any problems understanding it. It uses embedded images for the images in your game, but you can also use sprites with a very slight twist on a function we will later create (we'll discuss this in a moment).

Let's jump into Main.as and create a Bitmap that will contain every bullet and object that needs to be blurred. Add it before anything else in the children list.

private var _container:Bitmap;
private var _containerData:BitmapData;

private function init(e:Event = null):void 
{
	removeEventListener(Event.ADDED_TO_STAGE, init);
	
	// Creating player ship and the vector that will contain the bullets
	_playerShip = new PlayerShip();
	
	_bullets = new Vector.<PlayerBullet>();
	//
	
	// Initializing the container
	_containerData = new BitmapData(550, 400, true, 0xFFFFFFFF);
	_container = new Bitmap(_containerData);
	//
	
	addChild(_container);
	addChild(_playerShip);
	
	// Listener for the game loop
	addEventListener(Event.ENTER_FRAME, onEnterFrame);
}

So far, everything has been very simple. We've only created the container and added it to the display list.


Step 3: Drawing Bullets in the Container

In this step what we need to do is to draw the bullets in the container every frame. We'll do that in the onEnterFrame() function of the Main class.

private function onEnterFrame(e:Event):void 
{
	_playerShip.update();
	
	// Updating every bullet
	for (var i:int = 0; i < _bullets.length; i++)
	{
		_bullets[i].update();
		
		_containerData.copyPixels(Bitmap(_bullets[i].getChildAt(0)).bitmapData, Bitmap(_bullets[i].getChildAt(0)).bitmapData.rect, new Point(_bullets[i].x, _bullets[i].y));
	}
	//
}

The only line that matters is line 10. In that line, we draw the pixels of every bullet (by accessing the BitmapData of the bullet's child, which is the BitmapData containing the pixels of the embedded image) into their position. If you aren't using embedded images on your game, you can use BitmapData.draw() instead. This method is a bit slower, but it will work the same way.

We pass the whole rectangle of the bullet's BitmapData because we want to draw all of it. You can play with this rectangle and the position to draw to create very fun results (for example, a position based on a periodic function such as Math.sin() to create an interesting trail effect, even though the bullet only goes in a straight line, or only drawing the "fire" of a rocket bullet by passing a smaller rectangle in order to create the trail only with the shot).

When you compile and run your game, you'll get something like this after shooting a few bullets:

Trails aren't disappearing

However, that's not what we really want. We want to add a blurry trail effect, so what to do?


Step 4: Adding the Blur Effect

This is the last step. All we have left to do is to apply the blur effect in the BitmapData that holds all the images from the bullets. In order to do that, we will use a ColorMatrixFilter.

private var _colorMatrixFilter:ColorMatrixFilter;

private function init(e:Event = null):void 
{
	removeEventListener(Event.ADDED_TO_STAGE, init);
	
	// Creating player ship and the vector that will contain the bullets
	_playerShip = new PlayerShip();
	
	_bullets = new Vector.<PlayerBullet>();
	//
	
	// Initializing the container
	_containerData = new BitmapData(550, 400, true, 0);
	_container = new Bitmap(_containerData);
	//
	
	// Initializing the matrix filter
	_colorMatrixFilter = new ColorMatrixFilter([ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0.99, 0 ]);
	//
	
	addChild(_container);
	addChild(_playerShip);
	
	// Listener for the game loop
	addEventListener(Event.ENTER_FRAME, onEnterFrame);
}

private function onEnterFrame(e:Event):void 
{
	_playerShip.update();
	
	// Updating every bullet
	for (var i:int = 0; i < _bullets.length; i++)
	{
		_bullets[i].update();
		
		_containerData.copyPixels(Bitmap(_bullets[i].getChildAt(0)).bitmapData, Bitmap(_bullets[i].getChildAt(0)).bitmapData.rect, new Point(_bullets[i].x, _bullets[i].y));
	}
	//
	
	// Adding the blur effect on the container
	_containerData.applyFilter(_containerData, _containerData.rect, new Point(0, 0), new BlurFilter(2, 2, 1));
	_containerData.applyFilter(_containerData, _containerData.rect, new Point(0, 0), _colorMatrixFilter);
	//
}

The ColorMatrixFilter works by manipulating every pixel in the BitmapData according to the values in the matrix filter. Take a look at the init() function. We create a new ColorMatrixFilter in there, passing an array with a bunch of values in it. These values will create the transformation matrix of the matrix filter, allowing us to manipulate the pixels in the image.

The filter works basically like this: each component of the resulting color (red, green, blue and alpha) are calculated by multiplying the source components by the respective numbers in the respective row of the matrix and summing them up, along with the fifth value of the row.

For example, if we take the matrix filter we created in the code as our example matrix filter and we are applying it to a pixel with the values "red = 50, green = 10, blue = 200, alpha = 128", the resulting red component of the pixel will be "red = (50 * 1) + (10 * 0) + (200 * 0) + (128 * 0) + 0 = 50", because the first row of our matrix is "1 0 0 0 0". The alpha component will be "alpha = (50 * 0) + (10 * 0) + (200 * 0) + (128 * 0.99) + 0 = 126", because the last row of our matrix is "0 0 0 0.99 0".

Do you see what happens now? Every frame we multiply each pixel's alpha by 0.99, making it slightly more transparent, in order to create the trail effect. If you wish to read more about the ColorMatrixFilter, you can refer to the documentation.

The blur effect is taken care of by applying a simple BlurFilter in the BitmapData.

Compile the game now and you will get our desired effect!


Step 5: And That's It

You just learned how to apply a ColorMatrixFilter in order to create a blurry trail effect, using the very fast BitmapData.copyPixels() method! With this, you can add the blur effect to every object you want and not worry about Flash Player slowing down because you are adding too many children with blur filters on the stage. Lots of cool things can be built with this principle; you just have to be creative.

Thank you for reading! If you have any questions, please comment!

Advertisement