Async and Multipack Support for CCSpriteFrameCache
I've put together a few Cocos2d extensions (categories) while crafting our more recent titles, including our latest project, Poker Puzzle Tour. By far, one of the extensions I've used the most is this category on CCSpriteFrameCache that I'll be sharing in this post.
Multipack Support
The first extension allows for importing the multipack sprite sheets that TexturePacker generates in a single line. It also allows special handling between device types, which is important because of texture size limits. For instance, many times assets for the iPhone might all fit onto a single 2048px x 2048px sprite sheet, while the same assets might need to be split across multiple sprite sheets on an iPad due to the images' bigger sizes (iPad images are typically twice the size of their iPhone counterparts).
- (void)addMultipackSpriteFramesWithFormat:(NSString *)format ipadhd:(NSUInteger)ipadhdCount hd:(NSUInteger)hdCount standard:(NSUInteger)standardCount { NSUInteger count = [self countFromIpadhd:ipadhdCount hd:hdCount standard:standardCount]; for (NSUInteger i = 0; i < count; i++) { [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:format, i]]; } } - (void)addMultipackSpriteFramesWithFormat:(NSString *)format textureFormat:(NSString *)textureFormat ipadhd:(NSUInteger)ipadhdCount hd:(NSUInteger)hdCount standard:(NSUInteger)standardCount { NSUInteger count = [self countFromIpadhd:ipadhdCount hd:hdCount standard:standardCount]; for (NSUInteger i = 0; i < count; i++) { [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:format, i] textureFilename:[NSString stringWithFormat:textureFormat, i]]; } } #pragma mark - Helpers - (NSUInteger)countFromIpadhd:(NSUInteger)ipadhdCount hd:(NSUInteger)hdCount standard:(NSUInteger)standardCount { NSUInteger count = 0; if ([EFDeviceUtilities isIPad] && [EFDeviceUtilities isRetina]) { count = ipadhdCount; } else if ([EFDeviceUtilities isRetina]) { count = hdCount; } else { count = standardCount; } return count; }
#import "CCSpriteFrameCache+EF.h" // Your sprite sheets should be named to accept a [0..n) suffix // Pass in a format string with a single %d to be replaced [[CCSpriteFrameCache sharedSpriteFrameCache] addMultipackSpriteFramesWithFormat:@"MY_LARGE_SPRITE_SHEET_BASENAME%d.plist" ipadhd:2 hd:1 standard:1];
Async Support
The second extension builds on the first and loads your sprite sheets asynchronously instead of blocking the main thread. This has been really handy for the dynamic and predictive loading of textures on our very large maps. When the user scrolls, the framework will load the next few textures into memory. If the textures are loaded synchronously there's a visible jitter in the scrolling of the game. With this extension, that issue is solved.
One thing to note is that every subsequent call to addAsyncMultipack... before the first texture is loaded will yield a separate attempt to load the texture. This is because the internal state of CCSpriteFrameCache isn't modified till after the first texture is loaded in memory. You'll want to incorporate some mechanism for locking if you forsee this becoming an issue. Once the first texture is fully loaded into memory, CCSpriteFrameCache is updated and behaves normally - subsequent calls won't attempt to load the same texture into memory again since it will be detected as a hit in the cache.
- (void)addAsyncMultipackSpriteFramesWithFormat:(NSString *)format ipadhd:(NSUInteger)ipadhdCount hd:(NSUInteger)hdCount standard:(NSUInteger)standardCount withCompletion:(void (^)())completeCallback { NSString *imageFormat = [format stringByReplacingOccurrencesOfString:@".plist" withString:@".pvr.ccz"]; NSUInteger count = [self countFromIpadhd:ipadhdCount hd:hdCount standard:standardCount]; __block NSUInteger loaded = 0; for (NSUInteger i = 0; i < count; i++) { [[CCTextureCache sharedTextureCache] addImageAsync:[NSString stringWithFormat:imageFormat, i] withBlock:^(CCTexture *tex) { [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:format, i]]; if (++loaded == count && completeCallback != NULL) { completeCallback(); } }]; } } - (void)addAsyncMultipackSpriteFramesWithFormat:(NSString *)format textureFormat:(NSString *)textureFormat ipadhd:(NSUInteger)ipadhdCount hd:(NSUInteger)hdCount standard:(NSUInteger)standardCount withCompletion:(void (^)())completeCallback { NSUInteger count = [self countFromIpadhd:ipadhdCount hd:hdCount standard:standardCount]; __block NSUInteger loaded = 0; for (NSUInteger i = 0; i < count; i++) { [[CCTextureCache sharedTextureCache] addImageAsync:[NSString stringWithFormat:textureFormat, i] withBlock:^(CCTexture *tex) { [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:format, i] textureFilename:[NSString stringWithFormat:textureFormat, i]]; if (++loaded == count && completeCallback != NULL) { completeCallback(); } }]; } }
Alexander Wong http://alexanderwong.me













