Preparing your game for importing from iTileMaps using cocos2d-iphone

In this tutorial we will add ability to import tmx maps directly from iTileMaps. All the code in this tutorial is based on cocos2d-iphone.There is also prerequisite, tutorial is based on Ray Wenderlich’s 2 part series tutorials, you can just download the result from hes site or you can complete first part and second part of his tutorials.

You can get complete source code of this tutorial at github or download it here.

Preparing the code

I’m assuming you have opened completed tutorial of creating base tile based games so lets begin.

In HelloWorldScene.h just after +(id)scene add:

+(id) sceneWithMap:(NSString*)fileName;
-(id) initWithMap:(NSString*)fileName;

We are just defining our new functions to support loading maps from another sources not just hardcoded.

In HelloWorldScene.m

// Change
-(id) init
// to:
-(id) initWithMap:(NSString*)fileName

// Inside this function find and replace
self.tileMap = [CCTMXTiledMap tiledMapWithTMXFile:@"TileMap.tmx"];
// to
self.tileMap = [CCTMXTiledMap tiledMapWithTMXFile:fileName];

This code changes how map is loaded we are not using standard map but any map that we want.

Now the same with scene initialization:

// Change
+(id) scene
// to:
+(id) sceneWithMap:(NSString*)fileName

// inside this function find and replace
HelloWorld *layer = [HelloWorld node];
// to
HelloWorld *layer = [[[HelloWorld alloc] initWithMap:fileName] autorelease];

Just the same thing with scene loading which will load layer with the map we want.

To not break standard behavior add this somewhere near sceneWithMap:

+(id) scene
{
return [HelloWorld sceneWithMap:@"TileMap.tmx"];
}

This addition gives standard behavior of the tutorial just with the ability to load maps from any source. When you compile and run nothing should change but with this functionality we are ready to import some maps.

Importing some maps

Open your info.plist with some text editor and add this code at the end before </dict>

<key>UIFileSharingEnabled</key>
<true/>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSItemContentTypes</key>
<array>
<string>com.klemix.tmx</string>
</array>
<key>CFBundleTypeExtensions</key>
<array>
<string>tmx</string>
</array>
<key>CFBundleTypeName</key>
<string>TMX Map</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
</dict>
</array>

This is one of the key parts to be able to import maps. First of all we are saying that users are able to access applciation’s document directory from iTunes. Then we are telling iOS that we are capable to load some files with extension .tmx and content type com.klemix.tmx. This part of functionality isn’t well documented by Apple so it might be wrong or hard to understand. More info about this you will get at the end of tutorial.

Note: you will need to copy all tileset images you are using to your application’s documents directory via iTunes File Sharing before you can open map otherwise it will crash.

Now open TileGameAppDelegate.m and rename applicationDidFinishLaunching to

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

This is done to upgrade your code so it can load files from another sources, it isn’t well documented also, but if you are writing iOS 3.0+ code it is recommended to use this kind of function to start your application.

After add some code in this function

// at the very beginning of the function
if ([launchOptions valueForKey:@"UIApplicationLaunchOptionsURLKey"]) {
return [self application:application

openURL:[launchOptions valueForKey:@"UIApplicationLaunchOptionsURLKey"]
sourceApplication:[launchOptions valueForKey:@"UIApplicationLaunchOptionsSourceApplicationKey"]
annotation:nil];
}

if ([launchOptions valueForKey:@"UIApplicationLaunchOptionsSourceApplicationKey"]) {
return YES;
}

// at the end of the function replace
[[CCDirector sharedDirector] runWithScene: [HelloWorld scene]];

// with
NSString *mapName = @"TileMap.tmx";
if ([launchOptions valueForKey:@"OpenMap"]) {
mapName = [launchOptions valueForKey:@"OpenMap"];
}
[[CCDirector sharedDirector] runWithScene: [HelloWorld sceneWithMap:mapName]];
return YES;

At the very beggining we are telling application to load map calling function that we will add later, it is also due to some iOS version differnces. After that we are checking some newer functionality of iOS, it is asking if we want launch our application if it is called by supplied application bundle id, we are going to launch from any application so we are returning YES always.

In the end we are just searching if theres a key for supplied map filename, which we will create later. If it’s not we are just launching default map from our bundle.

After this one add new function

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *newFilePath = [documentsDirectory stringByAppendingPathComponent:[url lastPathComponent]];

[[NSFileManager defaultManager] copyItemAtURL:url toURL:[NSURL fileURLWithPath:newFilePath] error:nil];

if (!window) {
NSDictionary *dict = [NSDictionary dictionaryWithObject:newFilePath forKey:@"OpenMap"];
[self application:application didFinishLaunchingWithOptions:dict];
}
else
{
[[CCDirector sharedDirector] replaceScene: [HelloWorld sceneWithMap:newFilePath]];
}

return YES;
}

This function gets called if our application is running on some supported iOS version and our application accepted source application’s bundle id in previous function. The main idea is doing here we are copying supplied map to our application’s documents directory, which is bad to be honest but another way is to edit cocos2d itself, this is caused by that we can add our tileset images only to documents directory using iTunes File Sharing feature. Then we are just loading new map by initializing application or just replacing scene if application is already running.

But to support multi tasking we should add two new functions also:

-(void) applicationDidEnterBackground:(UIApplication*)application {
[[CCDirector sharedDirector] stopAnimation];
}

-(void) applicationWillEnterForeground:(UIApplication*)application {
[[CCDirector sharedDirector] startAnimation];
}

This will not generate crash under iPad or 4th gen devices because you can’t render something in background so we are just stopping all drawing made by cocos.

The results

That’s all. Now you are able to compile and run this code to load maps directly from iTileMaps. Ofcourse you can do much more with the map or functionality given just turn on your imagination and read some Apple documentation about working with files.

All the code we wrote is available at github or direct download.

Further reading

To better understand how you can interact with documents in your applications follow up some Apple documentation, though it’s really not documented so good.

Registering the File Types Your App Supports

Opening Supported File Types

If you know something more on these topics feel free to share information with me, it was really hard to get information on this and make it working.