Level editor design in Ninja Cat, part 1 – inner workings

I’d like to share some of the design concepts applied, and lessons that I learned, while creating and using in-game editor in Ninja Cat. I like reading about how other developers make games, their stories from the trenches – it inspires me, gives knowledge and allows learning on others errors :-P So here is my story for you – how the Ninja Cat editor works, what’s the reasoning behind some of its concepts, and what proved to be good, bad or ugly.

At start, I want to explain how levels look like, so that it will be easier to communicate. It will be rather complete list of all possibilities, because of what I’ve stated – I like reading gory, low level details and think it’s very informative.

How does it work?

When the game is built with editor support (which can be checked using CONFIG::editor directive in AS3), press TAB during game. Then the game gets paused and editor UI appears, as visible on screen.

The editor has 3 modes, changed by pressing 1-2-3 number keys (or clicking on the menu captions on left). I will refer to controls visible in top left corner as main menu.

1. Enemy Zone mode

Whenever player is inside Enemy Zone (later shortened to EZ), dinosaurs will spawn on position and in way specified in EZ. That allows to prepare appropriate kind of experience for player.

Selecting which EZ you’re working on is easy: just mouse over the top border. Selected EZ will have red outline. It can be moved either by dragging its thick top border, or by arrow keys on keyboard. Resizing is done by grabbing right bottom corner. In main menu, there are buttons LoadSaves asReload – which do what they say, for all Enemy Zones. That’s because there’s separate .xml file storing EZ and path nodes.

One can also create there New Zone, Clone existing one or Delete it. Ignore Toggle and Show Hide – those are deprecated. At the beginning I thought it will be useful to have multiple EZ controls visible at once, but in practice I didn’t use it.

As visible on screen, Enemy Zone has properties:

Dino type – visible in top left corner, by clicking shows list of all dinosaurs, and allows to select a different specie.

Total amount – how many dinosaurs will be spawned at this EZ, through all of its life.

Max at once – how many dinosaurs spawned by this EZ can live at one moment. The numbers visible on the right tell how many dinos have spawned so far, in this game instance. By clicking R one can reset it – useful for testing.

Total amount and max at once are supposed to be used in this way: let’s say there is a place where we want to have raptors attack player. We want to keep player engaged, but not endangered, so we want to limit number of raptors attacking at once, and keep the amount stable throughout the encounter, so there is always something to do. Thus, we might set 3 raptors attacking player at once, and total amount to 6.

Spawn delay – how many milliseconds have to pass between two dinosaur spawns. Thanks to this, they won’t appear clumped together.

Difficulty – how hard it will be to kill dinosaur spawned by this EZ, which equals to how long will be his word/sentence. It’s combined with global game difficulty to generate final value. Gives quite granular way of specifying difficulty. Values: banal, easy, medium, hard, hardcore, sentence short, sentence medium, sentence long.

Facing – left, right or both. Used to orient dinosaurs. For raptors it doesn’t matter, they are jumping and moving freely, but ie. triceratops can’t change his facing, so for him it’s ultra important. „Both” is a little hack, used ie. by pterodactyls – for them it means „appear randomly either on left or right side of screen”, whereas „left facing” would mean „always appear on right side of screen, facing left”.

Tag: a way to do some poor man’s scripting, more on that later.

2. Path mode

Selecting it hides Enemy Zones and brings ability to edit paths.

Path mode main menu

The player avatar moves on a path, which consists of nodes. There can be multiple paths on a level, and player can jump between them on nodes marked as junctions. A lot of concepts here, so let’s break them down.

Typical path node looks like this:

Path node menu

Actually it looks only like the small green or white square, but if you right click on it, menu appears. Big button with “Walk” – it’s selector, which allows to set speed taken to reach the node from the one on the left. It’s important, because some paths are bidirectional, and there needs to be the same speed on one segment, no matter from which direction player is coming.

The Stay input is seconds based. Tells how many seconds will Ninja Cat stay at this node. Useful is to give at “corners” small amounts like 0.25 so that he moves more naturally, not always speeding 100%. Makes him have some mass :) Other than that, the obvious use is to make him stop for few seconds, ie. before places with lots of enemies.

If the Junction is checked, node menu looks like this:

Path node junction menu

Slider with value 85 is radius (shown graphically by circle). There’s difficulty selector for choosing this junction (this one is medium). Auto is for junctions, which are automatically chosen (duh). It’s used when you have two paths which naturally follow, with no real player decision taking place. In this way player won’t even know there is junction node (balloon won’t appear).

How are junctions meant to be used? The radius tells it all. Upon starting the level, every junction node will be scanned in its vicinity of up to radius pixels, and connected to all other junction nodes. Then, during the game, if player is nearing a junction node, game will show balloons for all connected junctions. If player types on of them, he changes the path.

Mind you, this connection is not necessarily two way, like the example from screen above shows – the radius on top junction node is small, so it doesn’t have any junctions to connect to – but other can connect to it. Definitely, just one way junctions are often used. Also, there is no limit on how many junctions can be connected to, so one can devise pretty intricate scenarios.

Back to editing and main menu – there are obvious buttons for loading, saving and reloading. Editor also supports lasso – hold shift and you can select multiple nodes. Then you can move all at once (easy) but also edit their speed setting – which is less obvious (in simple level editors) but nevertheless powerful and useful. Nodes can be moved by mouse, and there’s a tolerance zone, so you don’t have to point exactly at node.

3. UI

I was planning to put every strange thing that might be helpful in this section.

UI menu

It wasn’t used very much. Only later I added displaying of mouse position in 2 coordinate systems, and hacky ability to draw scripting zones positions. It works like this: hold shift, and by holding left mouse button you can define rectangle on the screen. Release mouse, and then you can press on keyboard certain keys, which will emit to FlashDevelop console some output.

I can then copy that and put into Ninja Cat in the level section which creates script zones. That’s how it looks, bare metal:

if (FlxG.keys.justPressed("H")) // health powerup
{
	trace("s = new ScriptZone(" + point + ", " + size + ", \"spawn health powerup\");");
	trace("s.func = Game.powerup_mgr.Spawn;");
	trace("s.parameter = HealthPowerup;");
	trace("Game.script_zones.push(s);");
}

if (FlxG.keys.justPressed("S")) // star
{
	trace("star = Game.stars.recycle(Star) as Star;");
	trace("star.reset( " + point + ");");
}

if (FlxG.keys.justPressed("L") ) // camera offset
{
	trace("s = new ScriptZone( " + point + ", " + size + ", \"Camera_\", 9999, 1);");
	trace("s.func = CameraOffset;");
	trace("s.parameter = new FlxPoint( , );");
	trace("Game.script_zones.push(s);\n");
}

Spawn health powerup should be self explaining, star also, but what about camera offset? It’s a scripting node, which offsets the camera to some degree. By default camera centers on player (with slightly more shown in front and top of him), but there are situations when something’s happening and we want to have camera show that thing. Then CameraOffset is used :) and you can observe its work in many places throughtout the game.
In practice, that’s how this pseudo scripting looks in Level 4:

// ScriptZone parameters: pos x, pos y, width, height, function name, max number of calls total,
//   max number of calls per frame
s = new ScriptZone( 6560, 1181, 370, 156, "Camera_ShowRoof", 9999, 1);
s.func = CameraOffset;
s.parameter = new FlxPoint( 50, -90 );
Game.script_zones.push(s);

var s : ScriptZone = new ScriptZone(300, 100, 100, 110, "Finish Level");
s.func = FinishedLevel;
Game.script_zones.push(s);

// top of pyramid
var star : Star = Game.stars.recycle(Star) as Star;
star.reset(1225, 1367);
star.max_offscreen_time = 7;

As you see, script zones are simply calling functions, as long as player is inside them. That we call poor man’s scripting ;) altough in practice it was very simple and quick to code, debug, maintain, expand, and I really didn’t feel need for more sophisticated system.

What about the rest?

That’s about it for EZ and path nodes. However, there was another editor used for putting the tiles – DAME. Tiles are almost everything you see in level – background, buildings, ground, grass and flowers.

Level DAME layers

There are actually three layers of tiles:

  1. Foreground – brown tiles on which Ninja Cat and dinosaurs are moving, black ones (collisions disabled) for tunnel.
  2. Background – green lawn – it doesn’t collide at all, it’s used for giving depth through parallax effect.
  3. Sky – blue, part of lawn – no collisions.

Obviously (for anyone who made games), layers are defined only as far as it’s useful to hide black background :) What is great about DAME is that it easily exports maps in a format that Flixel understands – .csv. Not only does it save the tiles, it can even generate ActionScript3 class for loading them, loading sprites, paths (there is a path editor in it but I didn’t use it) and set all level settings as were in DAME. Very cool, although I’ve settled for writing code by myself.

Number of tiles used in this game is rather small – about 100 for ground, grass and flowers, concrete, water, steel wires, Eiffel tower blocks, decoratables…

Mona Lisa and Nike figure don’t count, they made their way into tiles because I didn’t want to code sprite system.

DAME editor proved to be quite flexible, it has useful tools (like paint bucket or tile matrix). In certain areas it lacks, especially in regards to efficient unobtrusive editing, and has few irritating bugs. Three that I’ve noted:

  • impossible to scroll during painting, which makes it harder to paint long horizontal lines
  • clicking on white in layer panel deselects current layer, then even if you right click on a layer and choose “Edit layer”, editor will show blank panel… and you’re wandering what’s wrong.
  • impossible to scroll tile panel on the right through pressing middle mouse button (it’s possible to scroll like that in main editor pane, which is life savior).

Fortunately DAME is open source, and I’m planning to add my 2 cents to its development. Most likely I will be using it for making Virgin Delivery, so it makes sense to invest in it a little.

Conclusion

And that’s it -  that’s pretty much everything major involved and some nitty-gritty details in making levels. In the next part I’ll explain what I’ve learned from all of this.

, ,