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()
.
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:
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!
Comments