How to Make Your First Roguelike


This Cyber Monday Envato Tuts+ courses will be reduced to just $3. Don't miss out.

Roguelikes have been in the spotlight recently, with games like Dungeons of Dredmor, Spelunky, The Binding of Isaac, and FTL reaching wide audiences and receiving critical acclaim. Long enjoyed by hardcore players in a tiny niche, roguelike elements in various combinations now help bring more depth and replayability to many existing genres.

Wayfarer a 3D roguelike currently in development
Wayfarer, a 3D roguelike currently in development.

In this tutorial, you will learn how to make a traditional roguelike using JavaScript and the HTML 5 game engine Phaser. By the end, you will have a fully-functional simple roguelike game, playable in your browser! (For our purposes a traditional roguelike is defined as a single-player, randomized, turn-based dungeon-crawler with permadeath.)

Click to play the game
Click to play the game.

Note: Although the code in this tutorial uses JavaScript, HTML, and Phaser, you should be able to use the same technique and concepts in almost any other coding language and game engine.

Getting Ready

For this tutorial, you will need a text editor and a browser. I use Notepad++, and I prefer Google Chrome for its extensive developer tools, but the workflow will be pretty much the same with any text editor and browser you choose.

You should then download the source files and start with the init folder; this contains Phaser and the basic HTML and JS files for our game. We will write our game code in the currently empty rl.js file.

The index.html file simply loads Phaser and our aforementioned game code file:

Initialization and Definitions

For the time being, we'll use ASCII graphics for our roguelike—in the future, we could replace these with bitmap graphics, but for now, using simple ASCII makes our lives easier.

Let's define some constants for the font size, the dimensions of our map (that is, the level), and how many actors spawn in it:

Let's also initialize Phaser and listen for keyboard key-up events, as we will be creating a turn based game and will want to act once for every key stroke:

Since default monospace fonts tend to be about 60% as wide as they are high, we've initialized the canvas size to be 0.6 * the font size * the number of columns. We're also telling Phaser that it should call our create() function immediately after it's finished initialising, at which point we initialize the keyboard controls.

You can view the game so far here—not that there's much to see!

The Map

The tile map represents our play area: a discrete (as opposed to continuous) 2D array of tiles, or cells, each represented by an ASCII character that can signify either a wall (#: blocks movement) or floor (.: doesn't block movement):

Let's use the simplest form of procedural generation to create our maps: randomly deciding which cell should contain a wall and which a floor:

This should give us a map where 80% of the cells are walls and the rest are floors.

We initialize the new map for our game in the create() function, immediately after setting up the keyboard event listeners:

You can view the demo here—although, again, there's nothing to see, as we haven't rendered the map yet.

The Screen

It's time to draw our map! Our screen will be a 2D array of text elements, each containing a single character:

Drawing the map will fill in the screen's content with the map's values, since both are simple ASCII characters:

Finally, before we draw the map we have to initialize the screen. We go back to our create() function:

You should now see a random map displayed when you run the project.

Click to view the game so far
Click to view the game so far.


Next in line are the actors: our player character, and the enemies they must defeat. Each actor will be an object with three fields: x and y for its location in the map, and hp for its hit points.

We keep all actors in the actorList array (the first element of which is the player). We also keep an associative array with the actors' locations as keys for quick searching, so that we don't have to iterate over the entire actor list to find which actor occupies a certain location; this will help us when we code the movement and combat.

We create all our actors and assign a random free position in the map to each:

It's time to show the actors! We're going to draw all the enemies as e and the player character as its number of hitpoints:

We make use of the functions we just wrote to initialize and draw all actors in our create() function:

We can now see our player character and enemies spread out in the level!

Click to view the game so far
Click to view the game so far.

Blocking and Walkable Tiles

We need to make sure that our actors aren't running off the screen and through walls, so let's add this simple check to see in which directions a given actor can walk:

Movement and Combat

We've finally arrived at some interaction: movement and combat! Since, in classic roguelikes, the basic attack is triggered by moving into another actor, we handle both of these at the same spot, our moveTo() function, which takes an actor and a direction (the direction is the desired difference in x and y to the position the actor steps in):


  1. We make sure the actor is trying to move into a valid position.
  2. If there is another actor in that position, we attack it (and kill it if its HP count reaches 0).
  3. If there isn't another actor in the new position, we move there.

Notice that we also show a simple victory message once the last enemy has been killed, and return false or true depending on whether or not we managed to perform a valid move.

Now, let's go back to our onKeyUp() function and alter it so that, every time the user presses a key, we erase the previous actor's positions from the screen (by drawing the map on top), move the player character to the new location, and then redraw the actors:

We will soon use the acted variable to know if the enemies should act after each player input.

Click to view the game so far
Click to view the game so far.

Basic Artificial Intelligence

Now that our player character is moving and attacking, let's even the odds by making the enemies act according to very simple path finding as long as the player is six steps or fewer from them. (If the player is further away, the enemy walks randomly.)

Notice that our attack code doesn't care who the actor is attacking; this means that, if you align them just right, the enemies will attack each other while trying to pursue the player character, Doom-style!

We've also added a game over message, which is shown if one of the enemies kills the player.

Now all that's left to do is make the enemies act every time the player moves, which requires adding the following to the end of our onKeyUp() functions, right before drawing the actors in their new position:

Click to view the game so far
Click to view the game so far.

Bonus: Haxe Version

I originally wrote this tutorial in a Haxe, a great multi-platform language that compiles to JavaScript (among other languages). Although I translated the version above by hand as to make sure we get idiosyncratic JavaScript, if, like me, you prefer Haxe to JavaScript, you can find the Haxe version in the haxe folder of the source download.

You need to first install the haxe compiler and can use whatever text editor you wish and compile the haxe code by calling haxe build.hxml or double-clicking the build.hxml file. I also included a FlashDevelop project if you prefer a nice IDE to a text editor and command line; just open rl.hxproj and press F5 to run.


That's it! We now have a complete simple roguelike, with random map generation, movement, combat, AI and both win and lose conditions.

Here are some ideas for new features you can add to your game:

  • multiple levels
  • power ups
  • inventory
  • consumables
  • equipment