Posts Tagged ‘Texture Packing’

Sprite Sheets – Working Out Their Size

So I’ve been working with Elise making a 2D point and click / hidden object game. We’ve been slowly piecing our engine together looking at the tools we can leverage to help create the content, as well as revising our own existing tools to do some work for us.

Recently, I’ve been revising our sprite sheet generator. It’s probably one of the oldest and first things we’ve had in our arsenal of tools, and it certainly has been there before the great options we have these days as indie developers, such as TexturePacker. If you haven’t heard of it and your a 2D game developer, I really suggest you check it out.

Up til now, when we integrate our sprite sheet generator as part of our pipeline process, we specify a fixed size, throw all the images in, and sort using a Binary Sorting algorithm, and hope it all fit. If not, we make the sheet bigger. It was a very manual process, and our best size guessing algorithm rarely guessed the best size. In fact upon revising the code I wondered what the hell I was even trying to achieve in the first place. Either I was programming late at night or using someones suggestion from the internet.

Regardless, I threw out the old code and re-wrote it, and here’s what I came up with.

public const int MinOptimizeSize = 8;
public const int MaxOptimizeSize = 8192;

public static Size CalculateOptimalSize(SpriteSheet sheet)
// no sources?
if (sheet.Sources.Count == 0) return new Size(MinOptimizeSize, MinOptimizeSize);

// work out the sum of all source areas
int areaSum = 0;
foreach (SpriteSource src in sheet.Sources) areaSum += src.Area;

// if there is no area, then return something small
if (areaSum == 0) return new Size(MinOptimizeSize, MinOptimizeSize);

// find the size
int guessWidth = MinOptimizeSize, guessHeight = MinOptimizeSize;
int power = MinOptimizeSize;
while (power <= MaxOptimizeSize)
// compute the area of the power!
int powerArea = power * power;

// if the power area greater the the sum of all source areas?
if (powerArea >= areaSum)
// set the guess size
guessWidth = power;
guessHeight = power;

// find the minimum height to fit the area
while (guessHeight * (power / 2) > areaSum)
power /= 2;

// update the height result
guessHeight = power;

// next power
power *= 2;

// return the size result
return new Size(guessWidth, guessHeight);

Sorry about the formatting, WordPress keeps removing it!

Basically what it’s doing is finding the total area of all source images and finding the minimum sized power of 2 square to fit it all. It then works backwards focusing on only the height, halving the height to see if the sum of the source areas can fit in in that. When it can no longer fit, the previous height is the one that will fit it all.

There are some cases where this result isn’t going to be able to fit everything perfectly, especially with arbitrary sized sprites, or you have sprites that are seriously huge. But for a starting place it’ll be close.

Once you have that size, there are additional checks you may want to do after you’ve worked it out. Such as evaluating the width and height of your sources images. By doing this you can determine the following.

  • Whether to swap the width and height values if you have more tall sprites then you have wide ones. This is because you are more likely to fit tall sprite sources inside a 512 x 1024 then your are in a 1024 x 512 sprite sheet.
  • Checking the size of the of each sprite source against the size of the “guessed” fit. This is because you may find that one or more of your image sources may exceed the dimensions of the “guess” itself, requiring you to make early adjustments.

There are a lot of other things you can do but that goes into your sorting method for your sprite sheet, which is another topic on its on. But the methods discussed here should give you relatively useful results for determine the initial size to fit your source images. And with that, to those who have stumbled in here, I hope you find it useful! Feel free to leave any comments or other suggestions below.