Tuesday, May 22, 2018

Creating Sprites with libGDX texturepacker

One of the differences between using libGDX and and not using a game engine is that libGDX uses sprite sheets. This is good, because in OpenGL, binding textures is fairly resource expensive and it is better to bind one large resource once than many small resources.

There are plenty of tutorials online about creating sprites, and I am certainly not much of an artist, so I will leave that to others to explain. But, I will say this, if you are like me and end up with a GIF file or have each of your individual sprites saved as layers in a single image there is a trick to getting them out without saving individually.

To separate layers of an image file into separate images the easy way, save the image file as an OpenRaster file (.ora extension). GIMP can do this for free, and likely most other image editing software can as well. After saving, open the .ora file using compression software (7zip or WinRAR), open the 'data' folder, and you will see each individual layer saved as a separate .png file. Easy.

Otherwise, it is important that each of your animation frames is in numerical (or alphabetical) order so that the sprite sheet will be created in the proper order. In my case, I number each frame like 001.png, 002.png, 003.png and so on.

For this tutorial, I made a simple 12 frame animation of a dot running (the app I am making is called Dot's Dots*, right?). You can see it in GIF form below, but I won't provide the images or the sprite sheet for you, because I think it is important that you try this for yourself. And honestly, you don't even need to make it 12 frames, you could have a 3-4 frame animation of a stick man walking and it will be fine.



Now, hopefully you created a simple animation of a few frames. In order to create your sprite sheet the first thing you should do is download the texture packer JAR file. Save it somewhere convenient, and use the following command at the command line to create the sprite sheet:

// OS X / Linux java -cp runnable-texturepacker.jar com.badlogic.gdx.tools.texturepacker.TexturePacker [inputDir] [outputDir] [packFileName]

// WINDOWS java -cp runnable-texturepacker.jar com.badlogic.gdx.tools.texturepacker.TexturePacker [inputDir] [outputDir] [packFileName]

[inputDir] is where you saved your individual image files, [outputDir] is where you would like the sprite sheet to be saved and [packFileName] is going to be the name of the output file.

In my case, I want to take my image files and save the sprite sheet directly into my apps assets folder. Because I am using Linux, I would type this:

java -cp runnable-texturepacker.jar com.badlogic.gdx.tools.texturepacker.TexturePacker 'Images/Sprites/DotsDots' '/home/netbook/AndroidStudioProjects/dotsdots/android/assets' dotsprite

After the texture packer runs, you will find two types of file in the output directory. There will be the .png files, which will be the actual sprite sheets, and there will be the .atlas file. The .atlas file is a text file that tells libGDX the location of each individual image on the sprite sheet.

If you want to just make a simple sprite sheet, feel free to stop reading here. I will discuss slightly little more technical stuff below, but it likely won't affect sprite or animation performance.

Note that the texturepacker defaults to sprite sheets that are 1024px x 1024px. In 2018 that seems fairly paltry, and if you want to change the sheet size, or any other options, you just need to include a file called 'pack.json' in the same directory as your individual image files. The pack.json file can have the following options:

{

pot: true,
paddingX: 2,
paddingY: 2,
bleed: true,
bleedIterations: 2,
edgePadding: true,
duplicatePadding: false,
rotation: false,
minWidth: 16,
minHeight: 16,
maxWidth: 1024,
maxHeight: 1024,
square: false,
stripWhitespaceX: false,
stripWhitespaceY: false,
alphaThreshold: 0,
filterMin: Nearest,
filterMag: Nearest,
wrapX: ClampToEdge,
wrapY: ClampToEdge,
format: RGBA8888,
alias: true,
outputFormat: png,
jpegQuality: 0.9,
ignoreBlankImages: true,
fast: false,
debug: false,
combineSubdirectories: false,
flattenPaths: false,
premultiplyAlpha: false,
useIndexes: true,
limitMemory: true,
grid: false,
 scale: [ 1 ],
scaleSuffix: [ "" ],
scaleResampling: [ bicubic ]

}

You only need to include the options you want to change, any unlisted options will just return to the default settings. As for the sprite sheet size, I believe that on most modern devices that sheets of 4096px x 4096px will work fine. However, old devices, particularly those using old versions of OpenGL, will not work. I thought about this personally, and decided that it was more important for me to maintain backwards compatibility than it is to minimize texture binding resource use, so I decided to stay with the default. Of course that decision is up to each developer, so you can decide on your own.

Next, if your images don't fit all onto one sprite sheet, it might be a good idea to separate them into subdirectories in your input directory. By doing this, you can ensure that the texturepacker saves related files on the same sprite sheets. Because the libGDX texturepacker tries to create the most efficient packing result, related sprites could be spread across several sprite sheets, which would be very inefficient.

Finally, if you want to run the texturepacker as part of your app, it can be done. We won't cover it here, because it seems to be of limited value to hobbyist developers that are likely working alone, but the libGDX wiki discusses it in detail.

*I have no idea what Dot's Dots is going to turn out to be, I am creating it on the fly as part of this tutorial. Shhhh!

No comments:

Post a Comment