Creating Textures for Characters in Autodesk Maya*
Spritesheet Animation in Adobe* Flash*
By Patrick Fram
Among 2D game development platforms, Adobe* Flash* is unique in the way it handles art and animations. MovieClips, timelines, and a built-in vector art editor all serve to make adding art to a Flash* project intuitive. However, in this article, I cover a method of game animation used in more traditional game development: animating with spritesheets.
Introducing the Spritesheet
Spritesheets are an old concept in the history of video game development. The idea is simple: A large image file that contains many smaller images used in the game. A spritesheet is like a library of all the art for the game, and the game “cuts and pastes” sections from the spritesheet to create the graphics at runtime. The sections can be static graphics or frames of animation. Figure 1 shows an example of a type of spritesheet commonly called a chipset. It contains all the art that the game's map would use.
[pic]
Figure 1. A chipset example for an RPG game’s map
Each 16 × 16 pixel square of this image is a different tile, and all the art in the game is built from different combinations of these tiles. You can also see that the water and waterfall tiles come in sets of three; in this case, they are three frame animations. Games that use chipsets for level or map art make heavy use of or re-use the same tile art over and over, allowing for efficient file size and memory use. Just about every game from the 8- and 16-bit era used these methods for game art.
However, spritesheets and chipsets are not an outdated concept. Many game development platforms still use them. Flash* games generally do not, but that’s simply because Flash* has an easier way to accomplish game art. That said, Flash* is perfectly capable of using spritesheet-based art and animations.
One thing to keep in mind is that spritesheet animation is different from using bitmap graphics in a timeline animation. In this article, you create a custom class to use instead of MovieClip or timeline animation—one designed to maximize performance.
Why Use Spritesheets in Flash*?
Spritesheets must be used wisely in Flash*: They are not always going to be the right tool for the job. Spritesheet graphics have the following advantages:
❑ Their primary advantage is the ability to display raster graphics in a fast and reliable way.
❑ Because spritesheets are raster graphics, they will benefit from the anti-alias smoothing in Flash*.
❑ Flash* can display bitmap graphics faster than vector art, because they allow for faster frame rates.
❑ Spritesheets are still the standard for many other development platforms; using them makes porting easier.
❑ When used for components, re-skinning becomes as simple as editing a single spritesheet graphic.
However, spritesheets have their downside:
❑ In most cases, raster images have a larger file size than vector art, meaning that they may make the application bigger and slower to load.
❑ Spritesheets are not as intuitive to use or edit as timeline-based animation.
Creating a PNG Sequence Animation
It may seem counterproductive, but to create a new spritesheet, you start with a Flash* animation. I created a 12-frame animation loop of a little ghost bouncing and placed it on the stage (Figure 2). I made this animation as a graphic, but it doesn't need to be. The only requirement is that it animates when the main timeline is scrubbed in Flash*. MovieClips will animate when you export an .swf file but not within the Flash* editor.
[pic]
Figure 2. A ghost character animation on the stage. Note how the stage fits the boundaries of the animation tightly, and the timeline only runs for the 12 frames it takes for the animation to loop.
From here, you must export the image into a series of images. To do that, click File > Export > Export Movie, and then choose PNG Sequence. This command renders each frame as a separate image and writes it to disk.
Creating a Spritesheet from an Image Sequence
There are several paths to creating a spritesheet out of this sequence. It's possible to open them up in Adobe* Photoshop* or any graphics editor and manually paste them into one image, but that's way more work than needs to be done. Instead, you can take advantage of a free program to do it for you: GlueIT, a freeware tool easily found with a web search. (This article uses GlueIT version 1.06.) Download and open the executable file.
GlueIT is easy to use. Simply click Add and select all the frames of the PNG sequence (see Figure 3).
[pic]
Figure 3. The PNG sequence opened in GlueIT
Next, click GlueIT. A pop-up window shows you what the stitched-together spritesheet will look like; you can adjust the Number of Columns value and click GlueIT again to see the variety of ways to arrange it. In this article, the class you create will be able to handle spritesheets of any combination of rows and columns, so you can choose whatever you want for that value. When you are satisfied with the settings, click Save to save the spritesheet to disk. Figure 4 shows the completed spritesheet.
[pic]
Figure 4. My completed spritesheet
Creating an Image Blitter Class
Now that you have a spritesheet, it's time to teach Flash* how to use it. You do this with a BlittingImage class that you create. Blitting is the process of cutting and pasting bitmap data via code; in this case, you will be copying individual frames from the spritesheet and pasting them to a display object on the stage.
First, create a new Adobe* ActionScript* 3.0 .fla file and import the spritesheet into the library. Then, edit its properties to allow ActionScript* linkage. The PNG must be given a unique class name that you can call via script (see Figure 5).
[pic]
Figure 5. Editing the properties of the spritesheet PNG in Flash*. Notice that the Export for ActionScript check box is selected and that it has been given a unique class name.
This image in your library is now considered a class by Flash*, meaning that you can create instances of it with code. If you look the Base class field, you'll see that Flash* gave it the type BitmapData. That means that this class extends BitmapData, and you can use all of the BitmapData class’s methods with it. This will become important later.
Now, create a new ActionScript* 3.0 file named BlittingImage.as, and enter the code in Listing 1 into it.
Listing 1. BlittingImage.as
package {
import flash.events.Event;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.Sprite;
public class BlittingImage extends Sprite {
//the source spritesheet
private var spritesheet : BitmapData;
//spritesheet layout information
private var cols : uint;
private var rows : uint;
private var totalFrames : uint;
private var frameWidth : uint;
private var frameHeight : uint;
//variables to track and control playback
private var frameDelay : uint;
private var elapsedFrames : uint = 0;
private var currentFrame : uint = 0;
//display objects
private var canvas : BitmapData;
private var canvasBitmap : Bitmap;
//constructor
public function BlittingImage(pSource : BitmapData, pCols : uint, pRows : uint = 1, pFrameDelay : uint = 1) {
//set local variables from function arguments
spritesheet = pSource;
rows = pRows;
cols = pCols;
frameDelay = pFrameDelay;
//calculate total frames and frame sizes
totalFrames = rows * cols;
frameWidth = pSource.width / cols;
frameHeight = pSource.height / rows;
//create the canvas
canvas = new BitmapData(frameWidth, frameHeight, true, 0x00FFFFFF);
canvasBitmap = new Bitmap(canvas);
addChild(canvasBitmap);
gotoAndStop(0);
}
/*sets the current frame and stops playback*/
public function gotoAndStop(i : int) : void {
currentFrame = i;
//find the x and y position of the current frame in the spritesheet
var fY:uint = Math.floor(i/cols);
var fX:uint = i % cols;
//copy and paste the rectangular area of the current frame to the canvas
var copyRect : Rectangle = new Rectangle(fX * frameWidth, fY * frameHeight, frameWidth, frameHeight);
canvas.copyPixels(spritesheet, copyRect, new Point(0, 0));
}
/*starts animation*/
public function start() : void {
if (!hasEventListener(Event.ENTER_FRAME)) {
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
}
private function onEnterFrame(event : Event) : void {
//increment elapsed time
elapsedFrames++;
//increment the current frame if enough frames have elapsed
if (elapsedFrames >= frameDelay) {
elapsedFrames = 0;
//loop back to frame 0 if there are no more frames
if (currentFrame + 1 < totalFrames ) {
gotoAndStop(currentFrame + 1);
} else {
gotoAndStop(0);
}
}
}
/*stops animation*/
public function stop() : void {
if (hasEventListener(Event.ENTER_FRAME)) {
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
}
/*places the canvas's center at the origin*/
public function center() : void {
canvasBitmap.x = frameWidth/-2;
canvasBitmap.y = frameHeight/-2;
}
}
}
There's a lot going on in Listing 1. Let's take a close look at each part.
First, notice that this class extends Sprite; a Sprite class (not to be confused with your spritesheet images) is essentially a MovieClip without a timeline. You can add it to the stage and manipulate it much like you would with a MovieClip. However, because it has no timeline, it is missing the function that MovieClips use to control playback, like gotoAndStop(). Instead, this class created its own playback control functions.
The constructor function creates a new BlittingImage. You can see that it gets passed a few arguments by whatever calls it; it needs a BitmapData instance to use as the source spritesheet and the number of rows and columns it has. From this data, the script calculates the size of the frames. (In a spritesheet, all frames are assumed to be the same size.) Near the end, the script sets up its canvas and canvasBitmap and adds them to its own display list. The canvas is the BitmapData that it will later paste each frame to, and the canvasBitmap is simply the bitmap that displays it. The last thing the constructor does is call gotoAndstop() to frame 0.
The GotoAndStop() function is purposefully mimicking the MovieClip's version. The first thing it does is figure out the coordinates of the current frame. Then, using those coordinates and the known size of each frame, it creates a rectangle. A rectangle is simply geometric information, but the copyPixels function uses it to instruct it where to copy from. Next, the script calls the copyPixels function—a function that all BitmapData instances have. This function copies a rectangular section from one BitmapData to another; in other words, it’s copying a single frame out of the spritesheet and pasting it to the canvas.
The remaining functions are not complicated. Start() adds a listener that fires every frame to facilitate animation. (The stop() function removes this listener to stop animation.) OnEnterFrame() simply handles the animation's looping and playback speed. The frame delay parameter that was set during the constructor determines how many frames in Flash* must elapse before this BlittingImage displays its next frame. And the center() function simply provides a quick way to change the origin of the BlittingImage instance from the top-left corner to the center.
This BlittingImage class is all ready to be used. Go back to the .fla file you imported the spritesheet to. Then, all you need is one frame with the code in Listing 2.
Listing 2. Frame 1 of the timeline
var bi:BlittingImage = new BlittingImage(new GhostSheet(0, 0), 4, 3);
addChild(bi);
bi.start();
stop();
That’s all it takes to use Your BlittingImage class. The first line creates an instance of BlittingImage, calling the constructor function. You pass in a new instance of GhostSheet (or whatever you named your spritesheet in the linkage). Because the spritesheet extends BitmapData, it requires width and height parameters in its constructor, but the values don’t matter, so I passed in 0, 0. (Note that not all versions of Flash* will throw an error if you leave those values blank, but it’s better to be safe.) The next two arguments are the numbers of columns and rows; these may be different for your spritesheet, so adjust accordingly. An optional fourth parameter defaults to 1—the frame delay.
The next few lines just add the BlittingImage instance to the stage and start the animation. If you publish the .fla file, you should see an SWF with the sprite animation looping in the top-left corner of the screen (see Figure 6).
Figure 6. The completed SWF. It looks just like timeline animation, but it’s all spritesheet-based.
Taking It Further
In this example, you learned how to make a class that functions like a basic MovieClip, except using spritesheets. It would not be difficult to extend this concept into a class that functions as a chipset tile, to create game levels and maps out of many instances of the same tile class.
It is also common to use spritesheet-based UI elements, like buttons. The spritesheet may contain just three frames of the three states of the button: up, down, and hovered. Adding some simple MouseEvent listeners would allow the class to control its spritesheet frame based on mouse interaction.
Spritesheet UI elements also make for easy re-skinning. Say, for example, that you needed to port a game for Chinese localization. If all the buttons and UI elements with static text were spritesheet-based, all you would have to do it swap out the spritesheet graphic with a Chinese text version, and the entire skin of the game changes.
Another possibility would be to create a spritesheet that contained images of a product from every angle, with the intention of allowing the user to rotate it dynamically. Such an object would be able to determine which frame to display based on rotational angles, giving it the appearance of 3D.
And one final idea to think about is using a spritesheet to create bitmap fonts. This could get a bit tricky with kerning, but it’s entirely possible to make a spritesheet in which each frame is a text character, combined with another class that could translate string data into a sequence of blitted letter instances.
The spritesheet has been used in games for a long time, and the concept is not going away anytime soon. Knowing how to create spritesheets and use them in Flash* opens a whole range of possibilities and adds even more flexibility to graphics handling in Flash*.
About the Author
Patrick is a 2D animator and Flash* developer who specializes in filling the role of the technical artist. He is a graduate of the Savannah College of Art & Design, holding degrees in animation and sequential art, and is a self-taught programmer. These days, Patrick works as a professional game designer and developer in Atlanta, Georgia.
................
................
In order to avoid copyright disputes, this page is only a partial summary.
To fulfill the demand for quickly locating and searching documents.
It is intelligent file search solution for home and business.
Related searches
- describing characters in a story
- characters in 13 reasons why
- creating an amortization table in excel
- creating a tracking spreadsheet in excel
- characters in tom sawyer
- characters in tom sawyer book
- characters in thirteen reasons why
- characters in mark twain books
- characters in huckleberry finn book
- famous female characters in literature
- female characters in fiction
- 100 best characters in fiction