8bitrocket.com
2Jul/082

Tutorial: AS3. The basics of tile sheet animation (or blitting).

In this article we will discuss the anatomy of a basic AS3 Flash blit operation, which is useful for rendering game graphics at high speeds. The information in this tutorial can be applied to almost any 2d development language that allows the use of bitmaps and transparency to render a game screen (Java, Objective C, etc). We will also do a little history lesson on Sprites and Bit Block Transfer, but not enough to bore you too much.

First, why use tile sheets and blitting to animating in the first place?
Flash has always given budding game developers an easy method to do animation. You just need to drop clips on the time line in consecutive frames and then play those frames in order. It was the basic foundation for the tool all the way back when it was a utility called FutureSplash (that and an impressive vector engine). So, why make it all complicated by throwing out the built-in animation rendering methods to use bitmap tiles sheets and a blit canvas to animate? Speed and optimization is the answer. While it is true that a poorly coded tile sheet blitting engine can be pretty lackluster in the performance department (I have experience creating some terrible humdingers), blitting, when done well, can improve game performance immensely. I have done some independent research into different rendering methods and have found that performance of 100's or even 1000's of independent objects on the screen can be greatly improved through the use of blitting from a tile sheet. Someone once argued with me that this might be good for a platform game, but would be terrible for an Asteroids like game. So I created this set of tutorials to prove them wrong. I then improved performance further with an experimental game timer loop.

The basics #1: What exactly is a tile sheet anyway?
A tile sheet is just an image (bmp,gif,jpg,png are common types) that has been broken up into individual regions. Each region is usually the same size, but contrary to what some people think, the regions don't have to be square and the dimensions don't have to be a multiples of 8 (8,16,32,64,128,256). It certainly makes it easier to use a sheet of 32x32 tiles that will fit evenly on an image in nice, neat rows and columns, but it is not necessary. Also, it is more efficient for computers to do math with integers in powers of 2. In any case, a basic tile sheet looks something like the image below. This was taken from the Ari Feldman Spribelib Library.

In the above image, I have turned on the grid lines so you can see how the individual helicopter images in Ari Feldman's Heli.bmp file can be separated into individual tiles. I had to do this by hand because I couldn't find any documentation that outlines what the actual tile size is for the images in this file. I came up with 132x132. I actually cropped Ari's file to get clean tiles.The actual sheet is 640 pixels wide with some extra space after the 4th copter frame. We actually only need the first 528 (132 x4) pixels in the 640 width of the image. For our basic example, we are just going to use the first 4 frames any how, so we really only *need* a tile sheet that is 528 x 132. With that in mind, when we get to the code section, we will import Ari's original image which is 640x400, but only utilize the first 528 x 132 pixels. You can think of these 4 individual frames are the key frames for your animated helicopter. I will use the term key frame form time to time in this tutorial. When I do, I am referring to an individual 132x132 region on this tile sheet and not the classic key frame from the Flash time line.

A tile sheet can be any image and the tiles can be anywhere on the sheet. When designing a tile sheet, it is best to make sure that individual frames that make up your animation be located in the same relative position inside each tile region, As you can see above, all of the helicopter frames are flush with the bottom left of each tile. It is not necessary to have them be in THAT location in your sheets, but they should all be in the same relative location. I usually choose to center my tiles, but to each his own.

What is this blitting thing you are always blathering on about?
I don't think blitting or to blit is a real word. You probably won't find it in any dictionary, but even if it means nothing to Oxford, it means a lot to game developers (and programmers alike). To BLIT is to take a map of bits (usually a 2d array or single linked list) and move them around in memory really fast. The Amiga and later Atari STE used this methodology in hardware to create some stunning 16 bit era games. Most early second generation game systems used tile sheets and blitting of some sort to create animated characters and backgrounds.The actual word blit comes from the Xerox Alto computer's (1974) ability to do this kind of data movement. It was termed a BIT BLOCK TRANSFER or BIT BLIT for short. It has been shortened it even further to just BLIT. It used to be that BLITTING and SPRITES were two different methods of accomplishing the same thing. Sprites were actually hardware generated on top of the bitmapped display, while BLITS were written right to the display buffer with a hardware or software rendering techniques.

Huh? So, genius, what exactly is a SPRITE then?
Good question.
Sprites, in their original incarnation, were made popular on the Atari (called Player Missile Graphics in the Atari world, Objs in the Nintendo realm), T.I. Commodore 64, and Nintendo (among others) computers and video game systems of the late 70's and early 80's. They actually were first devised by a company called Signetics in the '70's and used in some early game systems (the Atari 2600 had three - 2 players and a ball). Texas Instruments was the first company to use the term sprite. No matter where it came from, a sprite is simply an extra set of display memory (usually based on display scan lines), independent from the bitmapped screen. It could be controlled independently from the screen background, and some early implementations even included collision detection, multi-coloring, layering and other features. A Sprite was part of the hardware of the computer and was specifically used to display important game objects independently from the background. Usually the hardware offered only a limited number of rather small sprites.

How were Sprites and Bit Blitting Different?
Many popular mid 80's game systems and computers (like the Atari 520 ST and Apple IIGS) had no hardware sprites. This required game developers to use tile sheets and BLITTING techniques in software to achieve some of the same features of hardware sprites. The main difference between the two was that the hardware Sprites were written to the display separately than the the rest of the bitmapped screen. Some later computers (The Amiga and Atari STE) actually had hardware blitting capabilities that combined the best of both worlds - Fast Bit Block Transfer rendered to a bitmapped screen. Usually, systems with hardware blitting were replicating sprite capabilities with custom chips that were essentially moving bits around really fast in memory, but still displaying the screen as one big bitmap (no separate sprite channels independent of the screen). On systems with no hardware sprites or hardware blitting capabilities (or systems with too few of either) software techniques were used to replicate the same thing. These software techniques placed maps of bits on to the screen in a certain order, many times a second to archive a BLITTED, animated screen. This is essentially what we do in Flash when we are doing a BLIT. We are replicating a classic software BLIT. Tile sheets (or other image map representations) were used with both hardware and software driven sprites and or blitted memory maps of bits. In Flash, we will use tile sheets too (but there are certainly other methods of storing blit data). No matter what system is used (hardware sprites, hardware blitting, or software systems), BLITTING to the screen requires the game developer to exercise control over the screen rendering to achieve the effects he/she wants to achieve.

What about Flash?
In Flash, a SPRITE is a built-in class. It is rendered by the Flash player engine so the game developer doesn't have to worry about manually refreshing the screen when a sprite is moved (or changed) on the screen. Unlike classic hardware sprites, these software only sprites actually are re-drawn to the display background every frame and are not provided by hardware. Flash sprites share a name only with classic hardware sprites and are essentially a Movieclip without a timeline. Flash uses a technique called screen invalidation to mark areas of the screen that need to be refreshed rather than refreshing the entire screen with the vector renderer every frame. Flash uses the vector renderer for all sprites unless they are specifically cached as a bitmap. In that case, if the sprite changes (or animates in some way), it will need to be re-cached each frame. This will cause the processor to do double duty. With a lot of objects moving and animating on the screen, the Flash vector renderer will have to invalidate almost every portion of the screen and re-draw (and in cases of vectors cached as bitmaps, re-cache) repeatedly. Since the Flash vector renderer is not an efficient method of displaying a bitmapped screen in this manner, we will explore a more efficient method to render sprites-like objects in the simple BLITTING technique below.

What's the difference between a tile sheet and a sprite sheet?
You will hear both used interchangeable. To me, they are essentially the same thing - an image file logically and physically split into regions that can be used to display objects on the screen and animate them. Some tile based games use a sheet full of background tiles for rendering. They might have a separate sheet for sprite frames. I have mixed background tiles on the same sheet as sprite frames. It doesn't really matter to me. In fact, it actually saves memory to combine them into one.

Is a tile sheet necessary for Flash Blitting?
No, an physical file of images is not necessary (imported or loaded at run-time). I have used all kinds of sources for my blitting. I have even cached all the frames of timeline animation into arrays of BitmapData by looping through the frames one by one before the game starts. Retro Blaster was my first attempt at doing that. In this example though, we will use a tile sheet as our source.

Confused? No matter, I like all of that history and theory crap, but you just need to know how to do this in AS3.
In AS3, we can easily do a BLIT to the screen from a tile sheet. All we need is a tile sheet (duh Jeff, thanks for that bit of wisdom), a few built in Flash objects, and a little technique.

Setting up the .fla file
There are just a couple things that we need to do to set up our .fla file for blitting.
1. Import a tile sheet to the library and make sure it is set to export. Ours has a linkage name of heli_sheet.
2. Make sure the stage properties have a document class assigned. Ours is called Main.

What you need to know about basic Flash Blitting.
The pseudo code for a basic blit for our helicopter looks something like this:

[cc lang="javascript" width="550"]
ON EVERY FRAME
Draw the background to the screen
Check to see if we need a new animation frame for the heli
Do this by checking is animationCount >3
if yes, increase animationIndex by 1, set animationCount to 0
if animationindex > 4 set animationIndex to 0
if no, increase animationCount by 1
Find rectangle region of tile sheet for current heli frame
Copy that rectangle region to screen at heli coordinates
REPEAT

[/cc]

When this code is run, we first clear the entire screen by writing the background over whatever was there before. On the first pass, nothing is on the screen, but on the second and all subsequent passes, this will give us a clean slate to blit our objects to.


In the above diagram, the yellow square region represents the portion of the tile sheet that will be copied to the game screen. On the first frame pass, the region in the rectangle 0,0,132,132 is copied to position 50,50 on the game screen. The 0,0 is the top left hand corner of the region on the tile sheet. The first 132 is the number of pixels to copy in the x direction (or width) and the second 132 is the number of pixels to copy in the y direction (or the height) of the region. In between rendering helicopter frames, the background black box is copied to the canvas. This acts like an eraser and is needed so no remnants of the last frame will remain on the screen when moving to the next image on the tile sheet. The renderer repeats this operation for the other three key frames in our tile sheet (remember in the example we are only using the top 4 frames in the tile sheet for simplicity). It then loops back to the first key frame. In our actual implementation, we will render each copter key frame for 3 times in succession so the animation doesn't appear to be moving too quickly. This will be controlled by our animationDelay variable that will be set to 3.

Special note on optimization: We are only rendering one game element in this example, so it would be perfectly acceptable to create a more advanced render technique by which the helicopter is NOT rendered again on the 3 frames in between. You would do this with a sleep operation that would just leave the current frame on the screen until 3 frames are counted, then erase with the background and copy the next key frame from the tile sheet to the screen. Our version doesn't do that because I assume there will be many more objects on the screen (and probably a more complicated background), all moving about, and that would necessitate the erasing of the screen between every frame.

That's basically all there is to it. Essentially we are keeping track of how many frames to wait in between animation changes (essentially key frames). When we have reached the last key frame + 1 (our animationIndex counter variable), we go back to the first key frame, and we set our count back to 0;

Flash includes a tool kit that we utilize to make this all happen. This tool kit includes 5 basic elements:
1. The BitmapData object
2. The Bitmap object
3. The displayList
4. The Rectangle object
5. The Point object

All five of these work in conjunction to help us create our own display renderer, utilizing just the parts of the Flash renderer that we need. Those parts are the Bitmap object and the DisplayList. The Bitmap object is the simplest container that Flash gives us for displaying bitmap images on the screen. The DisplayList is simply our method of showing this Bitmap object on the screen. Our Bitmap object must contain a BitmapData object as its data for display. We use the Rectangle and Point objects to define what and where to display our BitmapData object on the Bitmap added to the DisplayList.

It is much simpler than it sounds. Here is the code for our Main() constructor. It sets everything up. We'll go through it line by line:

[cc lang="javascript" width="550"]
public function Main() {

tileWidth=132;
tileHeight=132;
heliTilesLength=4;
animationIndex=0;
animationCount=0;
animationDelay=3;
heliX=50;
heliY=50;

backgroundBD=new BitmapData(400,400,false,0x000000);
backgroundRect=new Rectangle(0,0,400,400);
backgroundPoint = new Point(0, 0);
canvasBD=new BitmapData(400,400,false,0x000000);
canvasBitmap=new Bitmap(canvasBD);
tileSheet=new heli_sheet(640,400);
heliRect=new Rectangle(0,0,132,132);
heliPoint = new Point(heliX, heliY);

addChild(canvasBitmap);
addEventListener(Event.ENTER_FRAME, gameLoop);
}

[/cc]

The first few lines set up the variables we will use in the display of our animated helicopter. The tileWidth and tileHeight are both 132. The number of animation frames we will use or heliTilesLength is 4. Our animationIndex is the current animation frame we are displaying. We have 4, and our count will be 0-based. This means we will start at 0 and count up to 3 rather than count from 1 to 4. This is important because we need a zero based number for calculating the location of the frame to copy from our tile sheet. The animationCount is simply the number of frame the current tile has been displayed. Each of our 4 heli tiles will be displayed for 3 frames. We hold that number in our animationDelay variable. The helicopter will remain stationary in this example at 50x and 50y. Those are held in the heliX and heliY variables respectively.

The background is created next. We create an instance of the BitmapData class called backgroundBD. The 400, 400 are the width and height respectively. The false indicates that this image will not have any transparency, the 0x000000 fills it with the color black. We next create a rectangle object that represents the size of the background, and a point object that is set to the upper left-hand corner of the backgroundBD. The point and the rectangle are used when we copy the background to the screen.

The canvasBD is another BitmapData object. It will act as the canvas that we will copy all of our images to. The canavasBitmap is the only display object on the screen. It is a Bitmap object. Bitmap objects contain a BitmapData property. Ours will be set to the canvasBD object. That way, when we make changes to the canvasBD object, they will be reflected on the screen immediately.

Our tileSheet is a copy of the heli_sheet imported bitmap in the library. It contains the 4 frames of animation that we will use. It also contains a 5th frame and a lot of blank space. We will not be using the 5th frame in this example because moving to a second row in the tile sheet is a more advanced topic. The heliRect is set to the size of our tiles for animating the helicopter, and the heliPoint is set to the upper x and y coordinates of the helicopter (50,50). These are used when copying the heli to the canvsBD.

The last part of this Main() method adds the canvasBitmap to the display list (this put it on the screen, ready to render the canvasBD object). It also creates an EventListener for the EnterFrame event that will call our gameLoop method on each frame.

The gameLoop method

[cc lang="javascript" width="550"]

public function gameLoop(e:Event) {
drawBackground();
drawHeli();
}

[/cc]

The gameLoop in this example is very simple. The function of a traditional game loop is to repeatedly update the objects on the screen, check for collisions and then draw the objects to the screen. Ours is much more simple. We don't have any game logic running for collisions or updating the position of our helicopter, so we only need to do two things each frame - draw the background and then draw the helicopter to the screen.

The drawBrackground method

[cc lang="javascript" width="550"]

private function drawBackground():void {
canvasBD.copyPixels(backgroundBD,backgroundRect, backgroundPoint);
}

[/cc]

The drawBackground method uses the copyPixels function of the canvasBD BitmapData object. The first parameter, backgroundBD indicates that the source for our pixel copy will be the backgroundBD bitmapData object. Remember, backgroundRect is 0,0,400,400. That rectangle is the second parameter. It indicates that we will start at he 0,0 position on the backgroundBD and then copy all of the pixels for a size of 400 width and 400 height. The backgroundPoint is set to 0,0, meaning will start the copy at the 0,0 position on the canvasB.

The drawHeli method

[cc lang="javascript" width="550"]
private function drawHeli():void {

if (animationCount == animationDelay) {
animationIndex++;
animationCount = 0;
if (animationIndex == heliTilesLength){
animationIndex = 0;
}
}else{
animationCount++;
}

heliRect.x = int((animationIndex % heliTilesLength))*tileWidth;
heliRect.y = int((animationIndex / heliTilesLength))*tileHeight;
canvasBD.copyPixels(tileSheet,heliRect, heliPoint);
}

[/cc]

This is the most complicated method we will see today and it really is pretty simple. There are just a couple relatively complicated concepts to understand in this code. The first is that we are controlling the key frame changes in animation with the variables animationCount, animationDelay and animationIndex. The animationCount variable is updated on each frame. If it is equal to the animationDelay, then we are ready to move to the next frame. We do this by increasing the animationIndex by 1. This index starts at 0, representing the first frame on the tile sheet, and goes up to 3, representing the 4th frame on the tile sheet. After we have displayed the 4th frame, and after the animationDelay, we will then go back to frame 0 again and repeat. This creates a seamless animation loop.

The last thing we need to do is tell our program what part of the tileSheet represents the current key frame of animation we would like to display. We need to calculate the rectangle that represents our helicopter on every frame (this is on area of optimization, as we can move this to only be calculated when the key frame changes). We do this by first setting the x property of the heliRect to be 0: int(0 % 4) *132=0. We also the y property of out heliRect to be 0: int(0/4)*132=0. That puts our first key frame pixels to start at 0,0 on our tileSheet. Let's see that diagram again:

The yellow region represents that rectangle region we have just defined. After the 3 frame animationDelay, we move on to the next frame of animation. In that instance, the x property of the the heliRect would be calculated like this:
int(1 % 4)=1.
1*132=132
. So, we need to our copy on pixel 132 of the tile sheet for the second key frame.
The y property would remain 0: int(1/4)*132=0. This region is represented by the yellow rectangle covering the second frame in our tileSheet.

The final thing we do is copy all of these pixels to the canvasBD from the tileSheet. We pass in the tileSheet, and the rectangle we created above - heliRect, along with a point that represents the location of the game screen for the helicopter (50,50) - heliPoint

These steps are repeated for key frames 3 and 4 and then the program loops back to frame 1 and starts the process over indefinitely.

That's really all there is to it. You can get much more detailed information on actually creating a game using these principals by follow the 4 parts of this tutorial and the game timer loop in this one.

Here is the final version working:

Here is the .fla and source files to download.

We have updated this tutorial to add transparency. Read it here.

0saves
If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.
Comments (2) Trackbacks (0)
  1. Great tutorial, its very clear that you know a lot

  2. Thanks a lot for this awesome tutorial! I also modified it so that it can read the next row of thesprite sheet. Mine is 4 columns x 7 rows.

    // private fields:

    const xTiles:int=4;

    const yTiles:int=7;

    //inside private function drawHeli() I mod this part:

    if ((animationIndex%xTiles == 0) && (animationIndex >0) && animationCount==0 ) {

    yTilescount++;

    }

    heliRect.x = int((animationIndex % xTiles))*tileWidth;

    heliRect.y= int ((yTilescount %yTiles))*tileHeight;

    There are probably more elegant ways to do it. Please share yours.
    Thanks!


Leave a comment

No trackbacks yet.

This site is protected by Comment SPAM Wiper.