Create Your Own Level Editor: Part 2/3

In this second part of the tutorial, you will implement a portion of the editing capabilities of your level editor. You’ll work through adding popup menus, dynamically positioning and sizing your objects on screen, and much more. By Barbara Reichart.

Leave a rating/review
Save for later
Share

This is the second part of a tutorial series that shows you how to make a level editor for the Cut the Verlet game that was previously covered on this site.

In the previous tutorial, you designed the XML storage mechanism of your level files, implemented some model classes to hold your rope and pineapple data, and finished off by creating the load file functionality of your level editor.

At this point, your project is still just a level “loader”, as opposed to a level “editor”. (No, editing XML files by hand does not count as a level editor — just in case you were wondering!)

Rest assured that you’ll not have to manually edit those level files any longer. In this second part of the tutorial, you will implement a portion of the editing capabilities of your level editor. You’ll work through adding popup menus, dynamically positioning and sizing your objects on screen, and much more.

If you don’t have it already, download a copy of the sample project where you left it off in the previous tutorial.

Getting Started

So what functionality should a level editor contain? At the very least it should be able to create, reposition and delete objects in the level. As well, it should make the best use of the screen real estate available. And as always, the functions in your app should be as intuitive as possible for the user.

With those basic requirements in place, it’s time to set out the design of the editor.

First, the user needs a way to launch the editor. The easiest way to do this is by using a menu. The bottom of the screen is probably the best place for the menu, as it does not contain any dynamic game elements, as seen below:

iOS-Simulator.png

Second, the user must be able to add ropes and pineapples to the level. This means you need a mechanism that will allow them to create those new objects. Again, the easiest way to expose this functionality would be through a menu.

However, you already have a menu to be added to the bottom of your screen, and you’re rapidly running out of real estate.

What to do?

Instead of having a menu that is permanently visible, it would be better to have a popup menu that appears by tapping the screen. From this popup menu, the user can choose between adding a pineapple or a rope. The object will then be created automatically at the current position of the popup menu.

Moving objects will also be important to the user — and the user interaction for this on a touch screen is almost a no-brainer. Drag and drop is the logical choice for this functionality.

Last but not least, the user needs to be able to delete objects. One very common way to do this in iOS is to use a long press action to indicate that the user wants to remove the item being pressed on.

Now that the design of the editor has been decided, you can get busy constructing it!

Creating The LevelEditor Class

Create an Objective-C class under the LevelEditor group. Name the file LevelEditor, make its super class CCLayer, and be sure to change the file extension of the file from .m to .mm.

The .mm file extension tells the compiler that the file uses Objective-C++.

Why do you need to use Objective-C++ in the LevelEditor class?

Simply put, Box2D uses C++. As well, LevelEditor references other game classes which rely on Box2D.

To start, replace the contents of LevelEditor.h with the following:

#import "cocos2d.h"
#import "LevelFileHandler.h"
#import "CoordinateHelper.h"

@interface LevelEditor : CCLayer<CCTouchOneByOneDelegate>

+(CCScene *)sceneWithFileHandler:(LevelFileHandler*)fileHandler;

@end

This adds the CCTouchOneByOneDelegate protocol to the declaration, which allows the new LevelEditor layer to receive touch events.

Next, switch to LevelEditor.mm and replace its contents with the following:

#import "LevelEditor.h"
#import "LevelFileHandler.h"
#import "PineappleModel.h"
#import "RopeModel.h"
#import "CutTheVerletGameLayer.h"

@interface LevelEditor () {
    LevelFileHandler* fileHandler;
    CCSprite* background;
    CCSpriteBatchNode* pineapplesSpriteSheet;
    CCSpriteBatchNode* ropeSpriteSheet;
}

@end

@implementation LevelEditor

@end

The above code adds all necessary imports to LevelEditor and then creates some instance variables. fileHandler will store the level you are currently editing, while background is simply a sprite that displays the jungle background. There are also two CCSpriteBatchNodes for all pineapples and ropes.

Now add the sceneWithFileHandler: implementation to LevelEditor.mm, as follows:

+(CCScene *)sceneWithFileHandler:(LevelFileHandler*)fileHandler {
    CCScene* scene = [CCScene node];
    LevelEditor *layer = [[LevelEditor alloc] initWithFileHandler:fileHandler];
	[scene addChild: layer];
    return scene;
}

This code is pretty similar to the scene creation code in the Cocos2D project template. It simply creates a CCScene that contains the LevelEditor scene.

At the moment this scene is empty. That won’t be terribly interesting to look at! :] Start by adding the editor menu to your scene.

Adding the Editor Menu

Add the following code anywhere in LevelEditor.mm:

-(void) createMenu {
    CGSize winSize = [CCDirector sharedDirector].winSize;
    
    // Place Buttons at bottom of game
    CCLabelTTF* saveLabel = [CCLabelTTF labelWithString:@"Save" fontName:@"Marker Felt" fontSize:24];
    CCMenuItem* saveItem = [CCMenuItemLabel itemWithLabel:saveLabel target:self selector:@selector(save)];
    
    CCLabelTTF* resetLabel = [CCLabelTTF labelWithString:@"Reset" fontName:@"Marker Felt" fontSize:24];
    CCMenuItem* resetItem = [CCMenuItemLabel itemWithLabel:resetLabel target:self selector:@selector(resetLevel)];
    
    CCLabelTTF* playLabel = [CCLabelTTF labelWithString:@"Play Level" fontName:@"Marker Felt" fontSize:24];
    CCMenuItem* playLevelItem = [CCMenuItemLabel itemWithLabel:playLabel target:self selector:@selector(playLevel)];
    
    // Create menu with buttons
    CCMenu* menu = [CCMenu menuWithItems:saveItem, resetItem, playLevelItem, nil];
    [menu alignItemsHorizontallyWithPadding:winSize.width*0.1f];
    menu.position = CGPointMake(winSize.width/2, saveItem.contentSize.height/2);
    [self addChild:menu z:100];
}

-(void) save {
    // TODO: save level
}

-(void) resetLevel {
    // TODO: reset to last saved version of currently opened file
}

-(void) playLevel {
	[[CCDirector sharedDirector] replaceScene: [HelloWorldLayer sceneWithFileHandler: fileHandler]];
}

In the code above, first you get the screen size. Next, you create three menu items with the labels “Save”, “Reset” and “Play Level”. You then create a CCMenu and add the menu items to it. Finally, you add the CCMenu to the scene so that it becomes visible.

When you look at the code that creates the menu items, you’ll note that each item has a selector attached to a method that is called when the item is tapped. For the moment only playLevel is implemented — tapping this menu item simply switches to the game scene. You’ll come back to implement the other two methods later.

Now you’ll need some code to call the menu and draw the background on the screen.

Add the following code to LevelEditor.mm:

-(id)initWithFileHandler:(LevelFileHandler*)levelFileHandler {
    self = [super init];
    if (self) {
        fileHandler = levelFileHandler;
        [self createMenu];
        
        background = [CCSprite spriteWithSpriteFrameName:@"bg.png"];
		   CGSize winSize = [CCDirector sharedDirector].winSize;
        background.position = CGPointMake(winSize.width/2, winSize.height/2);
        background.color = kDefaultBackgroundColor;

        [self addChild:background];
        
        // Load the sprite sheet into the sprite cache
        [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"CutTheVerlet.plist"];
        pineapplesSpriteSheet = [CCSpriteBatchNode batchNodeWithFile:@"CutTheVerlet.pvr.ccz"];
        [self addChild:pineapplesSpriteSheet];
        ropeSpriteSheet = [CCSpriteBatchNode batchNodeWithFile:@"rope_texture.png"];
        [self addChild:ropeSpriteSheet];
        
        [self drawLoadedLevel];
    }
    return self;
}

-(void) drawLoadedLevel {
    // TODO: draw pineapples
    // TODO: draw ropes
}

The above code initializes the screen of the level editor. It then stores the level layout by making a copy of LevelFileHandler. Next, it calls the method that draws the menu. After that, it loads the background and positions it in the middle of the screen.

Next, the code tints the background so that it is darker than the original background. This will help the user avoid any confusion as to whether they are in game mode or in edit mode.

This drawing code looks pretty nice, but right now there’s no way for the user to switch to the level editor!

As an exercise, try to create the menu that will allow the user to switch to the level editor. Protip: you can create the new menu by following the same menu creation steps as above.

So go ahead, give it a shot!

[spoiler]
Add the following code to CutTheVerletGameLayer.mm:

// Add at the top of the file
#import "LevelEditor.h"

// Modify initWithFileHandler: and add the following before the call to initPhysics
        // Level Editor menu
        CGSize winSize = [CCDirector sharedDirector].winSize;
        CCLabelTTF* editorLabel = [CCLabelTTF labelWithString:@"Editor" fontName:@"Marker Felt" fontSize:24];
        CCMenuItem* toEditorItem = [CCMenuItemLabel itemWithLabel:editorLabel target:self selector:@selector(switchToEditor)];
        CCLabelTTF* menuLabel = [CCLabelTTF labelWithString:@"Menu" fontName:@"Marker Felt" fontSize:24];
        CCMenuItem* toMenuItem = [CCMenuItemLabel itemWithLabel:menuLabel target:self selector:@selector(switchToMenu)];
        CCMenu* menu = [CCMenu menuWithItems:toMenuItem, toEditorItem, nil];
        menu.position = CGPointMake(winSize.width/2, toEditorItem.contentSize.height/2);
        [self addChild:menu z:100];
        [menu alignItemsHorizontallyWithPadding:winSize.width*0.1f];

// Add the following methods
-(void)switchToEditor {
    CCScene* editorScene = [LevelEditor sceneWithFileHandler:levelFileHandler];
    [[CCDirector sharedDirector] replaceScene:editorScene];
}

-(void)switchToMenu {
    // TODO: implement switching to main menu, where user can select the level she wants to edit
}

[/spoiler]

Build and run your project! Once the game is running, hit the menu to switch to the editor. You should see the jungle background and a menu at the bottom.

Selecting the “Play Level” option should bring you back to the game, as seen below:

Switching back and forth using your menus

Barbara Reichart

Contributors

Barbara Reichart

Author

Over 300 content creators. Join our team.