Make Your Own Level Editor – Part II

On part 1 of this series, I outlined the basic principles of handling the content pipeline, and how the process should work. To continue, we move on to building the level. As mentioned on part 1, I use the Flash IDE (CS4 or CS5) because of its support for automation and custom commands. Basic familiarity with Flash is required.

This article is cross posted on #AltDevBlogADay. See my other articles published on #AltDevBlogADay here.

Mapping Objects and Assets

Before actually making the first level, we need to create a manifest of all game objects. These can be anything from background elements, pick-ups or characters. Most are represented by static bitmaps or sprites-sheets where the game object is displayed using a single asset.

The idea is to map a logical game object (class) to a physical asset (bitmap or simple shape). For simplicity, we assume the size of the asset defines the bounding box of the logical game object. Each entry in the manifest will pair the class name with the asset file name, and a class ID. Its recommended to use some kind of naming convention with prefixes etc.

Building the Basic Level

Next we create a level template. This is a project with an empty level that would serve as a base for all future levels. Create a new Flash project (fla) and import all your assets into the library and save the file.

Flash keeps library objects either as the source assets or movie-clips, with the latter used as a container for a display object that can be manipulated.
For each of the source assets, create a movieclip for it (using F8), and name it to match the class id in the manifest. Save the file and start dragging objects to the stage. You can define a layer for graphic elements that are not exported and place there references, guides and bitmaps from the mock up. Make sure to have a rectangle that represents the screen bounds.Use the first level as template for the rest, reusing the same library objects.

Exporting the Level

The flash project file (fla) is how a levels is saved. This would be an intermediate file that will only be used to modify the level on design time. To actually use it in the game, we need to export the level data to a format that can be loaded at runtime.

Export the level data means we serialize it to XML, which will contain entries for all instances of game objects, each with is metadata (like class name, position, rotation and size). For that, we make use of JSFL – the JavaScript Flash API that’s included with the Flash IDE.

The following JSFL script creates an XML file and saves it in the current folder. To run, use Commands -> ‘Run Command’ and then point to the script file (should be saved with jsfl extension). After the first run, the script name will appear under the ‘Commands’ menu for quick access.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//==========================================
//
// Export all stage instances to xml
 
	var doc = fl.getDocumentDOM();
	doc.selectAll();
 
	var sel = fl.getDocumentDOM().selection;
	var myXml = "\r\n"
 
	var iLen = sel.length;
	for (var i = 0; i < iLen; i++) {
		var s = sel[i];
		if (s.instanceType != "symbol") continue;
		myXml += "    <child type='" + s.libraryItem.name + "'";
		myXml += " x='" + s.x + "' y='" + s.y + "'";
		myXml += " rotation='" + s.rotation+ "'";
		myXml += "/>\n";
	}
 
	doc.selectNone();
        myXml += "</level>";
 
	flash.outputPanel.clear();
	flash.trace(myXml);
	var exportFileName = doc.pathURI.replace(/%20/g, " ").replace(doc.name,
                                doc.name.replace(/\.fla$/, "") + ".xml"); 
 
	flash.outputPanel.save(exportFileName);
	flash.trace("File saved to " + exportFileName);

Here is a sample of the resulting file:


A major advantage of this export is the ability to easily customize the XML data by changing the script to add different properties for each entry (lines 15-18).

 

Loading the Level

Loading the level into the game is a matter of de-serializing the XML data into game objects. This is truly platform agnostic: you could read the XML from any language or platform and parse the results however you like. The most common are for Flash, iOS and XNA.
The process for loading should be something like:
  1. Read list of game objects from XML into an array.
  2. Iterate on the array. For each item:
    1. Pass the class name to a factory method to create a new instance of the object.
    2. Locate the asset (resource) associated with the object, by looking it up on the object manifest map.
    3. Call the init function on the newly created instance, to populate it with the asset and metadata.
    4. Place the object on stage
  3. End

Your mileage may vary, but that should do the trick. Here I assume all game entity classes are based on one base class, and make use of polymorphism to construct and populate them.

 

Round Trip and Edit Cycle

As mentioned before, the level is also saved as a native ‘fla’ file, as intermediate, so it can be loaded and exported at any later time. The most effective way to edit a level is by instant round tripping between the game (preview) and the editor. Round tripping is the process of moving from runtime back to design in order to edit or modify the level data.

Round Tripping

With a build of the game ready to run and the export script in place, this is made possible.Typically, the designer will have the level editor still open after exporting the data. Running the game will load the data and serve as a preview. If the game runs on a browser its only a matter of refreshing it to reload new data. Now all that is left to do is follow the sequence:  Edit –  Export –  Preview, rinse and repeat.

Extending the Loader

The described method works well for simple game objects that are based on a single asset. However, not all objects are simple. Some object are logical only with no assets, but we still want to be able to place them and get visual indication. Such are trigger boxes, waypoints and spawn points. For these we can create simple shapes (like a semi transparent box or some icon) and use proxy assets.
There is also the option of putting more data into each object. As long as there is a convenient way of entering meta-data in the editor, it can be exported with the object. An example of that would be a ‘fail zone’ – an invisible trigger box that triggers a level fail when touched by the player object. So a generic trigger box can be extended with a message code (integer or string) that will be specified in the editor – opening the possibility for the designer to control simple game logic in different scenarios.
These type of ‘programmable’ object not only save time, but also frees the designer to experiment and quickly try out different layouts and configurations without requiring further coding.

Conclusion

Sophisticated level editing should not be an exclusive perk of heavy platforms the likes of Unreal3. With little effort, even small indie developers can come up with decent tools to help take control over content pipeline and speed up game content creation.