Create Your Own Level Editor: Part 3/3

In this last portion of the tutorial, you’ll add the ability to delete objects, move them around, complete the level save and reset mechanism, and implement multiple level handling to round out your level editor! By Barbara Reichart.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 5 of this article. Click here to view the first page.

Resetting Your Level Data

First, add the following method prototype to LevelFileHandler.h:

-(void)reset;

Next, add the following method implementation to LevelFileHandler.m:

-(void)reset {
    [self loadFile];
}

All that this method does is to load the level file again from the save directory, which overwrites the current changes you've made to the level.

Now you can call the reset method when the user taps the corresponding menu item in the level editor.

Replace the existing stub for resetLevel in LevelEditor.mm with the following code:

-(void)resetLevel {
    [pineapplesSpriteSheet removeAllChildrenWithCleanup:YES];
    [ropeSpriteSheet removeAllChildrenWithCleanup:YES];
    [ropes removeAllObjects];
    selectedObject = nil;
    [connectedRopes removeAllObjects];
    [fileHandler reset];
    [self drawLoadedLevel];
}

The above code first removes all onscreen elements, clears out the selected object, and resets all level data. It then calls reset on the file handler to reload the level and then draws the newly loaded level.

And that completes the saving and reloading functionality!

Build and run your project, start the editor, make some changes, then tap Save and restart the simulator. The level that loads should now contain your changes. Awesome!

As well, make a few changes to the level and tap Reset. Boom! A nice clean slate to work from!

Menu – Scrolling with CCMenuAdvanced

OK, so far your editor can edit and save a level. But you can only edit, load, and play a single level. That's going to get boring really quickly!

It's time to implement multiple file handling in your level editor.

Create an Objective-C class named MenuLayer with superclass CCLayer. Also change the file extension of the implementation file from .m to .mm.

Replace the contents of MenuLayer.h with the following:

#import "cocos2d.h"
#import "CCMenuAdvanced.h"
#import "FileHelper.h"
#import "LevelFileHandler.h"
#import "CutTheVerletGameLayer.h"

@interface MenuLayer : CCLayer

+(CCScene*) scene;

@end

In addition to the imports, the above adds a static method that will return a scene containing the menu.

Implement the following static method in MenuLayer.mm:

+(CCScene*)scene {
    CCScene* scene = [CCScene node];
    [scene addChild: [MenuLayer node]];
    return scene;
}

Nothing too complicated here; you're simply creating the MenuLayer and adding it to an otherwise empty scene.

Now switch to AppDelegate.mm and remove the following import from the top of the file:

#import "LevelFileHandler.h"

Next, add the following import to the top of the file:

#import "MenuLayer.h"

Now switch to AppDelegate.mm and replace the following two lines of application:didFinishLaunchingWithOptions::

    LevelFileHandler* fileHandler = [[LevelFileHandler alloc] initWithFileName:@"levels/level0"];
    [director_ pushScene:[HelloWorldLayer sceneWithFileHandler:fileHandler]];

With these two lines:

    // Create menu and pass it to scene
    [director_ pushScene: [MenuLayer scene]];

Build and run your project; you should see an amazing...black screen?

iOS-Simulator.png

Hmm — it looks like you still have some work to do! You'll start by adding a menu to your MenuLayer.

Add the following initialization method to MenuLayer.mm:

-(id)init {
    self = [super init];
    if (self) {
        NSMutableArray* items = [NSMutableArray array];

        int levelNumber = 0;        
        BOOL fileExists;
        do {
            fileExists = [FileHelper fileExistsInDocumentsDirectory:[NSString stringWithFormat:@"levels/level%i.xml", levelNumber]];
            CCMenuItem* item;
            if (fileExists) {
                NSString* itemLabel = [NSString stringWithFormat:@"Level %i", levelNumber];
                item = [CCMenuItemFont itemWithString:itemLabel target:self selector:@selector(selectLevel:)];
            } else {
                item = [CCMenuItemFont itemWithString:@"New Level" target:self selector:@selector(selectLevel:)];
            }
            item.tag = levelNumber;
            [items addObject:item];
            
            levelNumber++;
        } while (fileExists);
        CCMenuAdvanced* menu = [CCMenuAdvanced menuWithArray:items];
        [menu alignItemsVerticallyWithPadding:0.0f bottomToTop:NO];
        menu.position = CGPointMake(0, -menu.contentSize.height/2);
        
        CGSize winSize = [[CCDirector sharedDirector] winSize];
        menu.boundaryRect = CGRectMake(winSize.width/2-menu.contentSize.width/2, 0, menu.boundingBox.size.width, winSize.height);
        
        [self addChild:menu];
        [menu fixPosition];
    }
    return self;
}

The above method first creates an empty array to contain the menu items to be displayed on the screen. The menu should contain one item for each level file present in the documents directory.

As all files in that directory will have been created by your level editor, you can assume that the files will be named like level0.xml, level1.xml, level2.xml and so on.

You next create a loop to iterate over all those files using a counter. At each step, you use the FileHelper class to check for the existence of a file for the given counter. If the file exists, create a menu item with the file name. If there is no file with the given name, assume that there are no more level files to load, and instead add a menu item named "New Level".

You assign the current level number to each menu item as a tag so that you can identify each menu item uniquely. Then you add the menu item to the array of items.

At this point you have a nice array containing an item for each level — now you need to display them on the screen.

At this point you might see a class that is completely unfamiliar to you - the CCMenuAdvanced class, which is part of the Cocos2D extensions. Why should you use it here instead of the normal CCMenu?

The screenshot below does a pretty good job of showing what would happen if you used CCMenu:

It just won't fit :(

The screen on an iPhone or iPod touch is relatively small, and it's hard to fit more than a few items on the screen without crowding.

If you were to use a standard CCMenu, then you might have to do something drastic like limit the number of levels in your menu list. But CCMenuAdvanced supports scrolling and so does away with any screen size limitations.

To display the menu, you first create a CCMenuAdvanced instance with your array of menu items. Then you set the menu alignment so that it starts adding items from top.

Next you position it on the screen and give it a boundary rectangle which determines the scrolling area. Finally, you add the menu to the menu layer so that it is displayed on screen.

Build and run your app, and you should see your menu on the screen. Go ahead and select one of the menu items!

Now the menu scrolls when there are more items than fit the screen

Now the menu scrolls when there are more items than fit the screen

Whoops — you probably just crashed your app. Oh, right — you need to add the menu handler!

Add the following method to MenuLayer.mm:

-(void)selectLevel:(id)sender {
    if ([sender isKindOfClass:CCMenuItem.class]) {
        CCMenuItem* item = (CCMenuItem*) sender;
        int levelNumber = item.tag;
        NSString* fileName = [NSString stringWithFormat:@"levels/level%i", levelNumber];
        LevelFileHandler* fileHandler = [[LevelFileHandler alloc] initWithFileName:fileName];
        [[CCDirector sharedDirector] replaceScene: [HelloWorldLayer sceneWithFileHandler:fileHandler]];
    }
}

The above code simply gets the sender object, casts it back to a CCMenuItem and retrieves its tag. Next, the tag is used to assemble the level's file name. Finally, the method creates a new game scene with the filename as parameter.

Build and run your project — you should now be able to load any levels you've created or create a new level from the main menu. Have fun creating challenging new levels for your game!

Barbara Reichart

Contributors

Barbara Reichart

Author

Over 300 content creators. Join our team.