Learn how to create a sprite sheet in TexturePacker and use it in a simple Starling application.
- Setting up a Starling project
- Use TexturePacker for your sprite sheet
- Include the sprite sheet in your project
Full source code is available on GitHub
Introduction
In this tutorial you are going to learn how to create a sprite sheet in TexturePacker and use it to create an animation of a walking character in a simple Starling application.
What skill level is expected of you?
-
Using TexturePacker is extremely easy.
-
However the Starling implementation requires an intermediate knowledge of
-
ActionScript3
-
Starling framework
-
Object Oriented Programming
A sprite sheet (also known as texture atlas) is typically a large bitmap image that includes multiple images (also called sprites). We are going to use our sprite sheet to construct the frames of an animation, a background image and two buttons.
Also, if you want you can download an archive with all the project’s files from GitHub.
Let’s begin our journey with some prepping.
Preparation
You know what they say: preparation is 50% of the work, so before starting, let’s get all our tools ready.
Much like a recipe, here’s a list of the ingredients we’ll need to prepare our tasty dish:
- FlashDevelop IDE
- Starling Framework
- TexturePacker
Let’s start from the top of our list: first things first!
FlashDevelop
FlashDevelop is an amazing free IDE for ActionScript3. If you don’t have it installed on your machine, you can download the latest version of FlashDevelop here
Once FlashDevelop is installed, launch it and create a new project by clicking on the Project menu: Project > New Project… > Air Mobile AS3 App
Name it tutorial01
Setting up our Starling project
In order to set up a Starling project in FlashDevelop, we need to perform a few steps that can be a bit tedious. Bear with me though: when you are done it will be very easy to set up another Starling project and you’ll be able to reuse the same setup by just copying and pasting the project’s files from this tutorial.
Adding the Starling libraries
Let’s download the Starling framework.
Unzip the archive on your hard drive and go to the *starling\bin* directory where you will find the compiled Starling library: starling.swc .
Copy starling.swc to the *tutorial01\lib* folder in the project you just created.
Then open FlashDevelop’s Project Manager panel (View > Project Manager), navigate to the starling.swc library in the *tutorial01\lib* folder, right-click on it and select Add To Library .
You will notice that the name of the library ( starling.swc ) will turn blue, to indicate that FlashDevelop now knows this is a code library.
Adapting the default project
While we are in the Project Manager, let’s look into the *tutorial01\src* folder and delete the file Main.as ; this is an ActionScript class that FlashDevelop creates automatically every time a new project is created, but we won’t need it as we are going to create our own starting class in the next step.
Go back to the Starling framework you downloaded earlier, navigate to the Starling Scaffold Mobile project ( *samples\scaffold_mobile\src* ) and copy the following two files:
- Scaffold_Mobile.as
- Constants.as
to our project’s *tutorial01\src* folder.
Setting the document's main class
In the Project Manager right click on Scaffold_Mobile.as and choose Document class :
Notice that now the Scaffold_Mobile.as icon shows a tiny green arrow: that’s FlashDevelop telling us that Scaffold_Mobile.as is now our main class, the one that’s going to be executed first when we launch the application.
More cleanup
In the Project Manager panel, double click on `Scaffold_Mobile.as to open the class in the code editor. Delete lines 22 to 24:
// We embed the "Ubuntu" font. Beware: the 'embedAsCFF'-part IS REQUIRED!!!
[Embed(source="/fonts/Ubuntu-R.ttf", embedAsCFF="false", fontFamily="Ubuntu")]
private static const UbuntuRegular:Class;
as we won’t be needing the Ubuntu font for our project.
Line 40 should be commented out if you are working with Windows 8:
Starling.multitouchEnabled = true; // useful on mobile devices
Otherwise you won't be able to control your application with the mouse.
Preparing the structure for the sprite sheets
Now let’s create a new folder called textures in our project’s /bin/ folder, and inside /bin/textures/ , let’s create two extra folders named 1x and 2x .
At this point your project’s directory structure should look like this:
We will need these folders when we create our sprite sheets, as we will generate two different versions to allow for devices with different resolutions. To use Apple’s terminology, our 2x sprite sheet will be suitable for Retina displays, whereas our 1x sprite sheet will work for displays with lower resolutions (i.e. iPhone3). More on this later when we use TexturePacker.
Lastly, let’s copy the whole system folder from the Starling Scaffold Mobile project ( \samples\scaffold_mobile\system ) to our project’s main directory. This folder contains some default images that the application will use as default backgrounds at the very beginning while everything else is loading. In order for this folder to be targeted correctly by the code in Scaffold_Mobile.as we need to tell FlashDevelop its classpath.
In FlashDevelop go to: Project > Properties… > Classpaths tab > Add Classpath…
then select the system folder. Your project’s classpaths should now look like this:
The next step is to create a new class called Root.as , this class will contain some boilerplate code to handle asset loading in Starling. In FlashDevelop’s Project Manager, right click on the /src/ folder and choose Add > New Class… .
A new window will open up, name your new class Root
and choose starling.display.sprite
as Base class.
Open the Root.as class in FlashDevelop’s editor (double click on it in the Project Manager panel) and replace its content with the following code:
package
{
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;
import starling.utils.AssetManager;
public class Root extends Sprite
{
private static var sAssets:AssetManager;
public function Root()
{
// nothing to do here -- Scaffold_Mobile will call "start" immediately.
}
public function start(background:Texture, assets:AssetManager):void
{
// the asset manager is saved as a static variable; this allows us to easily access
// all the assets from everywhere by simply calling "Root.assets"
sAssets = assets;
// The background is passed into this method for two reasons:
//
// 1) we need it right away, otherwise we have an empty frame
// 2) the Startup class can decide on the right image, depending on the device.
addChild(new Image(background));
// The AssetManager contains all the raw asset data, but has not created the textures
// yet. This takes some time (the assets might be loaded from disk or even via the
// network), during which we display a progress indicator.
assets.loadQueue(function onProgress(ratio:Number):void
{
if (ratio == 1)
{
// When all the assets are loaded we finally add our content to the stage
var scene:Scene = new Scene();
addChild(scene);
}
});
}
public static function get assets():AssetManager { return sAssets; }
}
}
This is a standard boilerplate class that handles what happens after all our assets are loaded. The code should be self-explanatory but it is also fully commented to give you an understanding of its inner workings.
Finally, let’s locate Run.bat in FlashDevelop’s Project Manager, double click on it and add the following code at line 18:
::set SCREEN_SIZE=NexusOne
::set SCREEN_SIZE=iPhoneRetina
set SCREEN_SIZE=iPhone
This will ensure our application resolution is going to be the same as an iPhone3 (320×480 pixels) when testing the app locally — when running the app on a mobile device, the app will detect the device resolution and adapt accordingly.
Phew, this was quite long, wasn’t it? The good thing is, in the future you can just use this setup as your starting point for your Starling applications!
Now let’s move on to something more exciting and start creating our content!
TexturePacker
TexturePacker is an application that allows us to create sprite sheets in an extremely easy and intuitive way. If you don’t already have it installed on your computer, you can download and install a trial version from here:
First, we are going to need some graphics to create our sprite sheet. For your convenience I have created an archive with all the graphics that we’ll need for this tutorial.
Download it from GitHub.
Adding sprites to your sprite sheet
Extract the content of the archive into a folder external to your project, let’s name this folder graphics .
Now launch TexturePacker and drag and drop your graphics folder into TexturePacker’s Sprites Panel .
This simple action ensures that TexturePacker picks all the images contained in your graphics folders and includes them in a single sprite sheet. Moreover, whenever you need to add more images to your sprite sheet, all you have to do is to add them to the graphics folder and TexturePacker will automatically add them to the same sprite sheet. Sweet!
All our images are .PNGs but TexturePacker reads many different graphics formats: .jpg, .bmp and even .swf!
If you have been looking closely at your spritesheet you might have noticed that although the walking guy animation is made of 16 frames (16 .PNGs in the “graphics” folder), our spritesheet only shows 8 frames. Why?
Did you notice how some of the images have a little stack of paper icon next to them?
That is because some frames of our walking animation contain exactly the same bitmap information. TexturePacker is a smart puppy and when it finds identical images it stores them just once in the sprite sheet: no need to include the same information twice and waste precious memory!
Setting output parameters
Now let’s tweak some settings so that we can export our sprite sheet to the correct locations in our project.
First of all, let’s make sure TexturePacker exports everything in a format compatible with our framework: from the Data Format drop-down menu choose Sparrow/Starling .
Before we move ahead, let me go into a tiny bit of theory to understand what we are doing next. As said earlier, a sprite sheet is a very large bitmap image that includes a bunch of sprites. In order for our application to use it correctly though, we need to tell it what the boundaries of each individual image are within the sprite sheet (if you are familiar with HTML, much like it’s done in HTML with image maps).
TexturePacker will do this for you automatically by creating two files:
- the texture atlas (the big bitmap image)
- a (xml) file that contains the information about the images’ boundaries
This is a huge time saver: imagine having to write a data file with all the images coordinates and dimensions by hand!
So, in the next step we tell TexturePacker where to save these two files within our project directory structure.
First, let’s take care of the data file. Click on the
button next to Data filename and
navigate to the 2x folder in your project which we created earlier
( /bin/textures/2x/ ). In the File name field type atlas
and then click on the Save button:
The name of the Texture File is filled automatically.
Creating low resolution versions of your sprites automatically
Done? Awesome. Now we need to tell TexturePacker that we want two different versions of the texture atlas: one at full resolution and the other at half the original resolution.
The code in the Scaffold_Mobile.as
class of our project will automatically pick one or the other based on the mobile device resolution.
The good thing about having a low-resolution version of our sprite sheet is that we will avoid storing a lot of bitmap data in the
mobile device's memory, which, especially for older models, is always very limited.
Let’s click the button with the cog icon next to AutoSD
to open the AutoSD-Settings panel and click on the + sign on the right side of the Scale field:
Then use the settings shown in the picture below and click on the cog icon
again to close the AutoSD-Settings panel.
In the last step we have to replace the directory name 2x
in the file path with {v}
.
TexturePacker will replace this markup with the variant name we have defined in the AutoSD settings:
What we are doing here is telling TexturePacker to save a full-resolution version of the sprite sheet in our tutorial01/bin/textures/2x/ folder and a half-resolution version (scale: 0.5) in the tutorial01/bin/textures/1x/ folder.
Now that we’re all set, click on the big
button in the top toolbar and TexturePacker will automatically save two texture atlases: one in full resolution (2x) and the other at half they resolution (1x) in the respective folders we created in our project earlier.
If you have done everything correctly your project’s textures folder should now look like this:
Back to coding!
Now that we have our sprite sheets all set up, we are going to write a new class to use all those pretty images!
Much like we have done before for the Root.as
class, we add a new class called Scene
: in FlashDevelop’s Project Manager,
right click on the src folder and choose Add > New Class… .
A dialog window will pop up, name your new class “Scene” and choose starling.display.sprite
as base class.
Then open the Scene.as
class in the editor and add the following code:
package
{
import starling.core.Starling;
import starling.display.Button;
import starling.display.Image;
import starling.display.MovieClip;
import starling.display.Sprite;
import starling.events.Event;
public class Scene extends Sprite
{
private var guy:MovieClip;
private var startButton:Button;
private var stopButton:Button;
public function Scene()
{
var background:Image = new Image(Root.assets.getTexture("background"));
addChild(background);
guy = new MovieClip(Root.assets.getTextures("walking_guy"), 12);
guy.x = (Constants.STAGE_WIDTH - guy.width)/2;
guy.y = 220;
addChild(guy);
Starling.juggler.add(guy);
guy.stop();
startButton = new Button(Root.assets.getTexture("start_button"));
startButton.name = "start";
startButton.x = (Constants.STAGE_WIDTH - startButton.width)/2;
startButton.y = 400;
addChild(startButton);
stopButton = new Button(Root.assets.getTexture("stop_button"));
stopButton.name = "stop";
stopButton.x = (Constants.STAGE_WIDTH - stopButton.width)/2;
stopButton.y = 400;
stopButton.visible = false;
addChild(stopButton);
addEventListener(Event.TRIGGERED, onButtonTriggered);
}
private function onButtonTriggered(event:Event):void
{
var button:Button = event.target as Button;
if (button.name == "start")
{
startButton.visible = false;
stopButton.visible = true;
guy.play();
}
else if (button.name == "stop")
{
startButton.visible = true;
stopButton.visible = false;
guy.stop();
}
}
}
}
This is a very simple class that should be pretty self-explanatory for an average to advanced ActionScript programmer.
Let’s take a quick overview, to see what each block of code does:
private var guy:MovieClip;
private var startButton:Button;
private var stopButton:Button;
We set the main objects for our scene as private variables: one MovieClip and two buttons.
guy = new MovieClip(Root.assets.getTextures("walking_guy"), 12);
guy.x = (Constants.STAGE_WIDTH - guy.width)/2;
guy.y = 220;
addChild(guy);
We create a new MovieClip called guy
using all the walking_guy
sprites from the texture atlas as frames and setting its speed to 12 frames per second.
Note how we don’t specify the name of each single frame image, as long as we named each frame using a progressive number as suffix
(i.e. walking_guy01
, walking_guy02
etc.), Starling will pick them all up in the exact order specified by their suffix.
Starling.juggler.add(guy);
guy.stop();
We add the newly created MovieClip to the main juggler so that it can be animated, then we immediately stop it. For those who are not familiar with the Starling animation system,
a juggler
is a class in charge of animating objects (like a MovieClip
, for example)
which makes sure their animations are executed at the correct speed (12 fps in our case).
startButton = new Button(Root.assets.getTexture("start_button"));
startButton.name = "start";
startButton.x = (Constants.STAGE_WIDTH - startButton.width)/2;
startButton.y = 350;
addChild(startButton);
stopButton = new Button(Root.assets.getTexture("stop_button"));
stopButton.name = "stop";
stopButton.x = (Constants.STAGE_WIDTH - stopButton.width)/2;
stopButton.y = 350;
stopButton.visible = false;
addChild(stopButton);
We create 2 new buttons for starting and stopping the animation using 2 sprites from our sprite sheet for the graphics. Note that we reference the right textures for each button by the name of the .png file we used in the sprite sheet. Also, we hide the stop button so that the start button is the only one showing initially.
addEventListener(Event.TRIGGERED, onButtonTriggered);
We add an event listener that listens for pressed buttons.
private function onButtonTriggered(event:Event):void
{
var button:Button = event.target as Button;
if (button.name == "start")
{
startButton.visible = false;
stopButton.visible = true;
guy.play();
}
else if (button.name == "stop")
{
startButton.visible = true;
stopButton.visible = false;
guy.stop();
}
}
This function is called every time a button is pressed and depending on the button’s name, it either plays or stops the walking guy MovieClip. In any case, it hides the button that has been pressed and reveals the other.
Excellent, now that we have all of our code in place, let’s compile the project and make sure it all works as it should. In FlashDevelop go to the Project menu and choose Test Project (or press F5). The application will now compile and you should be able to control the walking guy animation with the stop and start buttons.
Testing your app on an actual mobile device from here is not too hard, but the process goes beyond the scope of this tutorial. You can find all the necessary instructions in your project’s folder: when you created a new Air Mobile AS3 app project, FlashDevelop automatically created two text files: AIR_Android_readme.txt and AIR_iOS_readme.txt . Read them carefully, they’re both around 7 steps long, and in painstaking detail (with web links, references, and cited blogs) tell you exactly how to get your app working on your mobile device.
Conclusions
The application we have built is a simple one, but you can see how easy it was to handle graphic assets with the help of TexturePacker. Our spritesheets are perfectly suited for multi-resolution development, which is very handy in a market with literally hundreds of mobile devices around. I hope you have enjoyed this tutorial!