Balancing Hardware Limitations and Sprite Sheets

A decision we made as a company early on was that Project One would have to run on an iPhone 4, as there are still tens of millions, if not more, of them still in use even though the model is over three years old. (This decision may change depending on Apple’s release announcement next week, but for now we’re proceeding as planned.) Three great features of the iPhone 4 were that Apple increased it’s processor speed with the A4 chip, doubled the amount of RAM over the 3GS to 512MB, and introduced us to the hi-res Retina screen.

Unfortunately, what Apple didn’t do to support the Retina screen was upgrade the GPU (Graphics Processing Unit). For most users this is not a big deal, but for us, as game developers, it has presented a challenging limitation. The iPhone 4 has twice the processing speed and display density of the 3GS, but the exact same amount of graphics memory. This means, unlike the 4S or 5, the iPhone 4 can only handle sprite sheets with a maximum physical size of 2048 x 2048 pixels, instead of 4096 x 4096 pixels.

Project One, as you will discover shortly, is a game with a lot, A LOT, of sprites. This sprite sheet size limitation has caused us to need more sprite sheets than we initially anticipated. More sprite sheets has two main issues associated with it. The first is that more memory is used in loading the sprite sheets, making the game take longer to load as a whole.

The second issue, relevant to this post, is that since each sheet is its own batch node, we must carefully keep track of the z-index of each sheet’s node in relation to every other sheet’s z-index node. What’s important to remember is that when sprites are kept on their batch nodes, any number of sprites used from a batch node only count as one graphics draw call, ie eight sprite sheets with say 100 sprites used only counts as eight draw calls. This batching helps us immensely in GPU usage.

A sheet’s z-index determines which sheet’s sprites can be on top of another. Sprites on the same sheet can be placed above or below any other sprite on the same sheet, but a sprite on a lower z-indexed sheet cannot be displayed above a sprite on higher z-indexed sheet. (This is not strictly true, because you can get around it by making the sprite “self render”, but placing a large number of these self-rendered sprites increases the graphics draw calls exponentially, eating up the precious graphics pipeline on your mobile device.)

Keeping track of memory, draw calls, and sprite sheet z-indexing has greatly affected in how we’re designing our demo level. We don’t have the amount of layout flexibility we thought we had and has caused us to really think about what each section of the level will look like. We’ve had to plan out what sprites go together on the same sheets, and then decide on z-index of those sheets, based on what elements we using most in the fore-, mid-, and backgrounds. We’ve had to trim and delete sprites that we thought we were going to use, but ended up not. We even had to duplicate a few sprites at various sizes on different sheets, specifically for parallaxing backgrounds and foregrounds. I’d like to say that it was an easy process, but like a lot of development there was a lot of trial and error, but in the end we’re certain we’ve created a level that’s visually exciting and fun to play.