Continuing with my extensions to Apple's Texture2D class, this post is about extending the sprite sheet functionality in part 2 to render sprites of arbitrary size indexed by an atlas. This is more flexible than having all sprites of the same dimension, laid out uniformly in a large texture image. You can even have overlapping sprites and do other funky things as you're in full control over exactly which part of the texture to render.

For example, the following texture atlas is an extension of the sprite sheet used in part 2 and contains sprites of different sizes laid out in no particular way in the image:

Texture atlas

The first step is a method to render a specified part of a texture (specified as a rectangle) to the screen:

- (void) drawInRect:(CGRect)rect textureRect:(CGRect)textureRect  

This needs to render the rectangle specified by textureRect, which is relative to the original texture image, in the rectangle specified by rect, which is in screen co-ordinates. Most of the time (unless you want to squash or stretch the sprite), the width and height of textureRect and rect will be the same. Implementation of this function involves calculating texture co-ordinates as fractions of maxS and maxT that match textureRect as follows (see part 2 for an explanation of maxS and maxT):

x = maxS * (textureRect.origin.x / self.imageSize.width)  
y = maxT * (textureRect.origin.y / self.imageSize.height)  
width = maxS * (textureRect.size.width / self.imageSize.width)  
height = maxT * (textureRect.size.height / self.imageSize.height)

It's useful to have all the sprites that are contained within a texture and their bounding rectangles in a single place, keyed on something useful like a name, for more readable code and ease-of-use of the sprite atlas. The natural choice is to put it all in an NSDictionary, with NSString keys and NSValue values, each containing a CGRect.

I am using a simple comma-delimited text file to store all the sprite information, with each line containing name,x,y,width,height for each sprite and reading it into a NSMutableDictionary upon construction of the class (I chose not to use the built-in [NSDictionary dictionaryWithContentsOfFile: savePath] method as I wanted the file to be compact and simple to edit). This required the addition of a new initialiser:

- (id) initWithImage:(UIImage*)uiImage atlasFilename:(NSString*)atlasFilename  

a new member variable:

NSMutableDictionary* _textureAtlas  

and two new draw methods:

- (void) drawFromAtlasInRect:(CGRect)rect key:(NSString*)key  
- (void) drawFromAtlasAtPoint:(CGPoint)point key:(NSString*)key  

Using the example texture atlas above, which has 13 different sprites of the prince and 13 of a magic potion, I updated the example code on github to render an animated figure and an animated magic potion. Have a look at the additions to MyTexture2D class, the new textureatlas.txt file and the additional code in TextureExtAppDelegate.

Texture atlas layout

When a retina display is being used, it's useful to provide a texture of double the resolution for improved clarity. Please note that with this implementation, the texture atlas uses non-retina co-ordinates for everything, but double-size texture images are used if available (using Apple's @2x convention).


Posted Tue 22 Nov 2011 by Michael Patricios

Tags: Development, Objective-C, OpenGL


Post a comment