Web-Enabled Resource Manager for cocos2d

Aug 19, 2009   //   by Derek van Vliet   //   Development  //  5 Comments

You might have noticed in our recent videos that our game features the ability to load assets from either the device or the web depending on which menu item you select from the main menu.

Early on in the development of our game, we realized how valuable it would be to be able to load image assets from the web instead of from the app bundle. This led us to creating a Resource Manager class that we use to load all of our image assets.

ResourceManager is a singleton class that can be toggled between web mode and local mode. Since it’s a singleton, it is intended to stick around in memory for the duration of the app’s runtime, so we added a cleanup method which can be called to dump everything it is responsible for allocating from time to time. We run the cleanup method after every level.

So far it supports getting images in 3 ways: as a UIImage, as a CGImageRef, or as a cocos2d Texture2D. It is by no means a finished product. We are thinking of adding support for sounds as well. And if you see anything glaringly obvious that can be added or improved, I would love to hear it.

Enough talk, let’s see the code

ResourceManager.h

#import <Foundation/Foundation.h>
#import "cocos2d.h"
 
@interface ResourceManager : NSObject {
	BOOL web;
	NSString * urlPrefix;
	NSMutableArray * images;
	NSMutableArray * textures;
}
 
@property (readwrite) BOOL web;
@property (readwrite,assign) NSString * urlPrefix;
 
+(ResourceManager*)sharedResourceManager;
 
-(void)cleanup;
 
-(CGImageRef)getCGImageRef:(NSString*)fileName;
-(UIImage*)getUIImage:(NSString*)fileName;
-(Texture2D*)getTexture2D:(NSString*)fileName;
 
-(CGImageRef)getCGImageRefFromWeb:(NSString*)url;
-(UIImage*)getUIImageFromWeb:(NSString*)url;
-(Texture2D*)getTexture2DFromWeb:(NSString*)url;
 
-(CGImageRef)getCGImageRefFromAppBundle:(NSString*)fileName;
-(UIImage*)getUIImageFromAppBundle:(NSString*)fileName;
-(Texture2D*)getTexture2DFromAppBundle:(NSString*)fileName;
@end

ResourceManager.m

#import "ResourceManager.h"
 
 
@implementation ResourceManager
 
@synthesize web;
@synthesize urlPrefix;
 
static ResourceManager* _sharedResourceManager = nil;
 
+(ResourceManager*)sharedResourceManager
{
	@synchronized([ResourceManager class])
	{
		if (!_sharedResourceManager)
			[[self alloc] init];
 
		return _sharedResourceManager;
	}
 
	return nil;
}
 
+(id)alloc
{
	@synchronized([ResourceManager class])
	{
		NSAssert(_sharedResourceManager == nil, @"Attempted to allocate a second instance of a singleton.");
		_sharedResourceManager = [super alloc];
		return _sharedResourceManager;
	}
 
	return nil;
}
 
-(id)autorelease {
    return self;
}
 
-(id)init {
	images = [[NSMutableArray alloc] init];
	textures = [[NSMutableArray alloc] init];
	web = NO;
 
	return [super init];
}
 
-(void)cleanup {
	[images removeAllObjects];	
	[textures removeAllObjects];
}
 
-(CGImageRef)getCGImageRef:(NSString*)fileName {
	UIImage * img = [self getUIImage:fileName];
	if (img)
		return [img CGImage];
 
	return nil;
}
 
-(UIImage*)getUIImage:(NSString*)fileName {
	if (web) {
		return [self getUIImageFromWeb:[NSString stringWithFormat:@"%@%@", urlPrefix, fileName]];
	}
	else {
		return [self getUIImageFromAppBundle:fileName];
	}
 
	return nil;
}
 
-(Texture2D*)getTexture2D:(NSString*)fileName {
	UIImage * img = [self getUIImage:fileName];
	if (img)
		return [[Texture2D alloc] initWithImage:img];
 
	return nil;
}
 
-(UIImage*)getUIImageFromWeb:(NSString*)url {
	UIImage * img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]]];
	[images addObject:img];
	return img;
}
 
-(CGImageRef)getCGImageRefFromWeb:(NSString*)url {
	UIImage * img = [self getUIImageFromWeb:url];
	if (img)
		return [img CGImage];
 
	return nil;
}
 
-(Texture2D*)getTexture2DFromWeb:(NSString*)url {
	UIImage * img = [self getUIImageFromWeb:url];
	if (img) {
		Texture2D * tex = [[Texture2D alloc] initWithImage:img];
		[textures addObject:tex];
		return tex;
	}
 
	return nil;
}
 
-(UIImage*)getUIImageFromAppBundle:(NSString*)fileName {
	UIImage * img = [UIImage imageNamed:fileName];
	[images addObject:img];
	return img;
}
 
-(CGImageRef)getCGImageRefFromAppBundle:(NSString*)fileName {
	UIImage * img = [self getUIImageFromAppBundle:fileName];
	if (img)
		return [img CGImage];
 
	return nil;
}
 
-(Texture2D*)getTexture2DFromAppBundle:(NSString*)fileName {
	UIImage * img = [self getUIImageFromAppBundle:fileName];
	if (img) {
		Texture2D * tex = [[Texture2D alloc] initWithImage:img];
		[textures addObject:tex];
		return tex;
	}
 
	return nil;
}
 
@end

Example Usage

Since it’s a singleton, there’s no need to instantiate it. Just include the header file and call its sharedResourceManager method. By default, it is set to load assets from the application bundle.

How to load a file into a UIImage:

UIImage * img = [[ResourceManager sharedResourceManager] getUIImage:@"menu.png"];

How to load a file into a cocos2d Sprite:

Sprite * bg = [Sprite spriteWithCGImage:[[ResourceManager sharedResourceManager] getCGImageRef:@"background.jpg"]];

How to load a file into a cocos2d AtlasSpriteManager:

AtlasSpriteManager * mgr = [AtlasSpriteManager spriteManagerWithTexture:[[ResourceManager sharedResourceManager] getTexture2D:@"character.png"] capacity:1];

As I mentioned above, we like to be able to switch between loading the same assets from the device and from the web at run-time. We do this by setting the ResourceManager to be in web mode and giving it the URL of a folder where our assets are stored online:

[ResourceManager sharedResourceManager].web = YES;
[ResourceManager sharedResourceManager].urlPrefix = @"http://example.com/assets/";

If you run those 2 lines before running any of the previous examples, the ResourceManager will look online for the asset you asked for instead of on the device.

As well, there are methods included that force the class to load from the web or device, regardless of which mode it is in.

How to force it to load a UIImage from the web:

UIImage * img = [[ResourceManager sharedResourceManager] getUIImageFromWeb:@"http://example.com/assets/menu.png"];

Finally, to clean up any resources already loaded:

[[ResourceManager sharedResourceManager] cleanup];

5 Comments

  • [...] They often take the form of manager or factory classes. A good example of a singleton is the web-based resource manager class that we posted about [...]

  • [...] eventually led to us writing a resource manager singleton class that handles all of our resource loading and can be toggled between local and web [...]

  •  Thx for sharing all these example classes and tutorials! Lots to learn here! thank you, Alex

  • [...] Web Resource Manager Allows you to put images on your webserver, and load them at runtime, to reduce the amount of time it takes to get your artist to test new graphics. [...]

  • Works with cocos2d-iphome 1.0.1 retina?

Leave a comment

Our Games

Latest Tweets