Under the hood: Spritesheets in Construct 2

2
Official Construct Post
Ashley's avatar
Ashley
  • 29 May, 2012
  • 1,659 words
  • ~7-11 mins
  • 4,415 visits
  • 2 favourites

Construct 2 has typically exported object's animation frames as separate PNG files. Spritesheets - a new feature in the latest beta r92 - means Construct 2 now exports bigger images with several animation frames arranged on each. For example, here's a single big PNG file with some animation frames from Space Blaster arranged on it:

Spritesheeting can help speed up the game loading time. However, in development a number of interesting issues came up. This resulted in us taking a slightly different direction to other tools which also implement spritesheeting. For example, rather than use one giant image with absolutely everything on it, Construct 2 instead exports a series of smaller tiles with parts of different object's animations. This blog post explains why it's done this way, and what we had to take in to consideration.

Technical limitations

Due to the way graphics processors work, images are usually actually held in memory in a power-of-two size (e.g. 128x128, 256x256, 512x512, etc). For example a 150x150 image will have to be placed on a 256x256 surface, with the rest of the pixels transparent. Notice that means it uses a lot more memory: out of the whole 256x256 area, only about a third of the pixels are actually used for the image. The other two-thirds is simply wasted, but still sit there taking up memory! If you have a long animation with 150x150 frames, it can end up wasting several megabytes of memory - and just for one animation.

As a result sprite sheets have to be a power-of-two size, otherwise it will be wasting space that could have been used. A nice additional benefit is it's possible to reduce wasted memory - those 150x150 frames can now sit side-by-side on a single power-of-two texture. Many modern desktop systems can actually deal with non-power-of-two size images, but they might still use power-of-two images behind the scenes, and mobile devices generally still must use power-of-two sizes as well. So the only candidate sizes for sprite sheets are power-of-two sizes.

Suppose you want to put an entire game's images on to one sprite sheet. How big can you go? Again, more technical limitations: not all systems support 4096x4096 images (often on mobile). Therefore the biggest size that can even be considered is 2048x2048, else some systems will fail to display anything at all.

Memory can still end up being wasted with sprite sheets. If a 1024x1024 sheet is only half filled, half the pixels are wasted memory again. If this happens, Construct 2 will instead slice the sprite sheet in to two or three 512x512 sprite sheets to reduce wasted space.

Progress feedback

A problem with placing everything on one giant texture is the game progress bar becomes useless. Space Blaster could just about be squeezed in to one giant 1024x1024 image weighing in at 2 megabytes. Unfortunately the browser does not say how much of an image has been loaded - only whether it has finished loading. With lots of images the progress bar advances gradually as each individual animation frame is loaded. With one giant image, it does not advance at all until it's finished loading. This means the progress bar sits at zero, then after a while jumps to to 100% and immediately starts the game.

It might seem trivial to worry about progress bars. But consider users on slow connections: if they are sitting looking at a progress bar reading zero and not moving at all, they may be inclined to think it is not working and leave the game before it finishes loading. In reality it was gradually downloading a 2mb image, but with no feedback that things were actually moving, you lost a player.

This means it is probably best to split images over a few sprite sheets rather than one mega-sheet. That would help players see the loading really is working.

Image compression

I've previously blogged about image compression in Construct 2, which you may want to read to understand this section better. You can choose three formats for each animation frame individually: PNG-32, PNG-8 or JPEG. This is combined with some sophisticated processing during export that helps reduce the download size of your game as much as possible. However, it raises an interesting question: if a sprite sheet contains a set of images with different formats set, what format should the sprite sheet be saved in?

It's easy to say "whichever is the best quality". But this answer could substantially increase the download size of the game. Remember PNG-8 is often less than half the size of PNG-32, and JPEG even less than that. If a lot of your game's images are PNG-8 and JPEG, a single image using PNG-32 could cause everything to be saved as PNG32... causing the download size to more than double! It would be a shame if everyone had to wait twice as long for your game to load because it used sprite sheets!

The next obvious thing to do is have a PNG-32 spritesheet, and another PNG-8 spritesheet. However, this can still cause big increases in the download size! As mentioned in the previous image compression blog post, Construct 2 counts the colors in images on export. It automatically converts PNG-32 images to PNG-8 if they use fewer than 256 colors, even if the setting is still PNG-32. I think this is a really nice feature: it's a hands-free saving, significantly reducing the download size without you having to do anything at all. However, some animations mix low-color (under 256 colors) and high-color (over 256 colors) frames. This means as individual frames, some would come out as PNG-8 and others as PNG-32. When combined in to a single sprite sheet, the entire sheet has over 256 colors, so would export as PNG-32. It's even possible that every frame in an animation could individually be low-color, but taken all together they'd have over 256 colors (since each frame could have a different 256 colors). Then they end up being saved in PNG-32 which can be over twice as big.

I was really surprised by the effect of this. Despite the fact a sprite sheet of a number of frames should compress better than each frame saved to a separate file, the effect was significant enough to make the Space Blaster export bigger than not using sprite sheets at all! One example was one of the explosion animations, where the last frames tended to be very dark and be able to save to PNG-8, but sprite sheeting saved everything as PNG-32. I was not comfortable releasing the feature if it would make your games even bigger to download - that seems to me to be an anti-feature.

So the end result is a middle ground: a sprite sheet per sprite. A single sprite sheet will at most contain all the animation frames for one object. We also went with a maximum sprite sheet size of 512x512, which is smaller than the biggest supported size. This makes it more likely long animations are spread over multiple sprite sheets, which has two effects. Firstly, the loading bar problem is alleviated, since there are more images to download and therefore more progress updates. Secondly it is more likely an entire sprite sheet can be saved as PNG-8, which helps reduce the download size. For animations like the explosion with lots of dark low-color frames at the end, the last sprite sheet or two can still be separately color-counted and saved as PNG-8 where possible. Finally, JPEG images are never sprite sheeted and are always saved as individual .jpg files.

Since multiple frames in one PNG file compresses better than several separate files, you also can get around a 10-15% reduction in the total size of exported images. Since there are also fewer separate images, fewer HTTP requests need to be made during loading, which makes the download even more efficient. Space Blaster without sprite sheets is 276 images, requiring 276 separate HTTP requests; with 512x512 sprite sheets, it's only 49.

Other objects

Currently only sprites are assembled in to sprite sheets. This is mainly for backwards compatibility. Since only Sprite is affected no other plugins using images (either official or third party) need to be changed. There's not much more to be gained, anyway - objects like Tiled Background and Particles tend to use fewer images which are completely different (as opposed to lots of very similar animation frames), and it is much more difficult to deal with the image format problem in that case.

Performance

Often performance is cited as a reason to use sprite sheets: apparently, the graphics card does not have to swap textures so often while rendering, allowing it to work more efficiently. However I was unable to measure any performance difference at all between any system. Individual images, a single mega-sheet and our middle ground of 512x512 sheets all scored about the same on our performance test, even on mobile. So I think performance is simply not an issue either way here.

Benefits

In the end this turned out to be a lot more complicated than I anticipated! Done wrong, spritesheeting can make your game take longer to download and frustatingly not display any progress at all during that time. Our middle ground hopefully covers everything:

  • The total download time is reduced since sprite sheets compress better, PNG-32 or PNG-8 is still used where appropriate, and the download makes fewer HTTP requests
  • Runtime memory use is reduced by eliminating the unused space around power-of-two sized images.
  • Players still see loading progress so they don't get fed up and quit.
  • Third party plugins using images aren't broken (apart from any which alter Sprite's images, which I will help plugin developers fix).
  • Runtime performance is not affected.

This was a pretty challenging list to satisfy. However it will help ensure Construct 2 is doing the maximum it can to optimise your projects on all fronts, without making negative tradeoffs.

Subscribe

Get emailed when there are new posts!