Tutorials

Make a Top-Down Adventure

This is a top-down adventure game, the kind where you look down at the world from above. The warrior explores rooms, finds keys for locked doors, fights slimes with her sword, and goes looking for treasure.

It's the biggest tutorial, and it borrows from all the others (kickboxes, animations, particles, screens), so do the platformer tutorial first if you haven't. Same as always: play the demo, mess with the code, hit Run.

MDog Engine is already imported in these demos, and the screen is 256 by 192. For your own game, grab the import line from Getting Started. The tiles come from tiles.png in the Asset Packs.

1. Walking Around

The room is a TilemapInteractable from the UI Module, a grid of 16 by 16 tiles made from rows of tile numbers. In tiles.png, 1 is dirt, 2 is stone, and 3 is water. Tile 0 is grass with dirt under it, which is meant for side-on games like the platformer, so we skip it. Grass here is just an empty tile, -1, with the green background showing through.

Rows of numbers are a pain to read, so we write the room as letters instead, one per tile, so it kind of looks like the room: . is grass, = is dirt, # is stone, and ~ is water. makeRoom() turns the letters into tile numbers and builds the tilemap. The room is 16 by 12 tiles, which fills the 256 by 192 screen exactly.

From above, you can walk up, down, left, right, and diagonally. Each tick, the held arrow keys make a direction, like (1, -1) for right and up. The catch is that a diagonal is longer than a straight line, so you'd walk faster diagonally. setLength() fixes that by making it the same length either way.

Go ahead and change some letters in the room and hit Run. It's a weirdly satisfying way to make levels.

2. Walls

Now stone and water actually block you. hitsWall() checks the tile under each corner of the player's kickbox. Divide a position by 16, round down, and you've got which tile it's in. get() gives you that tile's number.

Moving works like the platformer: move across, and if that put you in a wall, move back. Then the same for up and down. Because they're separate, you slide along walls when you walk into them diagonally instead of getting stuck.

Only checking corners works as long as the kickbox isn't bigger than a tile. If you want dirt to be a wall too, add || tile === 1 in hitsWall().

3. The Warrior

Now the square becomes the warrior from the Asset Packs. She plays her idle animation standing still and her run animation while walking, flipped with flipX when she faces left. Walking straight up or down keeps her facing whatever way she was.

This part is easy to miss: from above, things are standing on the ground, so only their feet should hit walls. Her kickbox is now just her feet, 12 by 8, and her sprite is drawn above that. That way her head can overlap a wall behind her, and the room suddenly looks like it has depth.

If you want to see her kickbox, draw a 12 by 8 rectangle at playerBox.getLeftX(), playerBox.getTopY() after she's drawn.

4. Keys and Doors

Now the right side of the room is locked. The door is a kickbox that counts as a wall in blocked(), which checks the walls and every door. There's a key on the left side, and you pick it up by walking over it.

To open the door, you need to be right up against it holding a key. That needs a second kickbox, reachBox. It's stuck to the same Vector as her feet but 2 pixels bigger on every side, so it touches whatever she's standing against. If it touches a door and you have a key, the key gets used and the door disappears.

Behind the door is a treasure chest. Walk into it and you win.

5. Slimes and a Sword

Now there are slimes. They wander around, and if the warrior gets close, they come after her. A slime's direction toward her is her position minus its own, set to its speed with setLength(). Slimes use the same move() she does, so walls stop them too. move() now takes the position and kickbox to move.

Touching a slime costs one of her three hearts, knocks her back, and makes her blink for a moment while she can't be hit. Lose all three and it's game over.

Space swings her sword. She stands still while the attack animation plays, and on the frames where the sword is out, a kickbox in front of her takes out any slime it touches.

Now that things can overlap, we draw them from the top of the screen to the bottom. Lower on the screen means closer, so those get drawn last, on top.

The 70 is how close she has to get before slimes notice her. Make it bigger if you want them to be meaner.

6. Rooms to Explore

Last, the world gets bigger. There are three rooms now, and walking through a gap in the edge takes you into the next one. Rooms are named by their spot in a grid, like "1,0" for one room to the right of the start. When her middle goes past the right edge, the game switches to the room on the right and moves her back by a screen width, so she shows up on its left edge.

Each room has its own tilemap, keys, doors, slimes, and chest, all built from roomData by makeRooms(). Slimes you've beaten stay gone if you leave and come back. You'll need both keys to get to the treasure.

There's also a title screen, a game over screen, and a win screen, which work like the ones in the other tutorials. startGame() rebuilds the rooms, so everything comes back for a new game.

Next

Some ideas if you want to keep going:

  • Add more rooms to roomData. Just make sure the gaps in the edges line up with the room next door.
  • Put hearts in some rooms that give back a heart when she walks over them.
  • Make the rooms in a spreadsheet and load them from .csv files (see Levels From a File).

That's every tutorial. From here, the Playground is a good place to start your own game, and the Engine docs in the sidebar cover everything else MDog Engine can do.