Building a Typing Game with MelonJS

Learn how to build a JS game using MelonJS and Tiled

Fernando Doglio
Bits and Pieces
Published in
15 min readOct 19, 2019

--

Game development is not something that needs to be restricted to people using Unity or Unreal Engine4. JavaScript game development has been a thing for quite some time. In fact, the latest versions of the most popular browsers (i.e Chrome, Firefox and Edge) provide support for advance graphic rendering (such as WebGL) which lend to very interesting game development opportunities.

Mind you, learning to utilize WebGL for game development is not something you can cover in a single article (there are full books written solely for this), and as a personal preference, I tend to lean towards using the help of frameworks before wanting to dig deep into a particular technology.

This is why after doing some research, I’ve decided to write this quick tutorial using MelonJS.

What is MelonJS?

As you’ve probably guessed by now, MelonJS is a JavaScript Game Engine that is fully compatible with all major browsers (all the way from Chrome to Opera, going through Chrome for mobile as well as iOS Safari).

It has a set of features that made it very compelling during my research:

  • For starters, it’s completely independent, there are no external dependencies needed to make it work.
  • It has, however, integration with several 3rd party tools that will make your life a lot easier, such as Tiled (which will help you create maps and stages for your games), TexturePacker (which will also help you create the required texture atlas to simplify and optimize sprite management).
  • 2D physics engine integrated. This means you can have access to out-of-the-box 2D realistic movement and collision detection. This is crucial, since having to work out all of that would require a lot of work (not to mention a very interesting amount of math, which is not really my cup of tea).
  • Support for the sound API, allowing you to add sound effects and background music with outstanding simplicity.

There are other amazing features for this engine, and you can check them out on their website, but the above ones are the ones we’ll care for the most for this article.

Tip: Use Bit (Github) to easily share and reuse JS modules and UI components across your projects, suggest updates, sync changes and build faster as a team.

Components with Bit: Easily share across projects as a team

Planning our game

The aim of a typing game is to provide the player with the ability to move or perform some kind of action, through typing words (or just hitting random keys).

I remember learning how to type back when I was a kid (yeah, that was quite some time ago), using “Mario Teaches Typing”, a platformer where you had to type single letters to move ahead, either by jumping onto a turtle or by hitting a block from beneath. The image below gives you an idea of how the game looked and what kind of interaction you could have with it.

Although it was a fun little game, it wasn’t really a platfomer, you had to say over the actions Mario would perform, there was always a single option available on every key and it would never fail.

For this article though, I wanted to make things a bit more interesting, so instead of creating a simple typing game such as the one above, we’ll do the following:

Instead of having a single letter determine your next move, the game will provide five alternatives, and for each one, you’ll have to write a full word:

  1. move forward
  2. jump forward
  3. jump up
  4. jump backwards
  5. move backwards

In other words, you’ll be able to move your character using the words you type, instead of the classic arrow-based controls.

Other than that, the game will be your classical platformer, with the player moving around collecting coins. For the sake of brevity, we’ll keep enemies and other type of entities out of this tutorial (although you should be able to extrapolate the code used and create your own entities based off of it).

In order to keep this article within a reasonable length, I will focus only on a single stage, full range of movement (in other words, you’ll be able to perform all 5 actions) a few enemies, one type of collectible and finally a healthy amount of platforms for you to jump around.

The tools you’ll need

Although melonJS is completely independent, there are a few tools that’ll help A LOT during this process and I recommend getting them:

  • Texture Packer: With this one, you’ll be able to auto-generate the texture atlas, which is another way of saying a JSON file where all images are packed so the engine can later retrieve them and use them as you see fit. If you don’t have this utility, maintaining that atlas manually can be too much.
  • Tiled: This will be our main level editor. Although you can download it for free (you’ll need to look for the link that says “No thanks, just take me to the downloads”), you can donate as little as 1 dollar to the author of this amazing tool. If you have a PayPal account or a debit card available, I’d suggest you do so, software like this needs to be maintained and it takes time and effort to do it.

With these tools, you’ll be able to follow along and complete the tutorial, so let’s get coding, shall we?!

A basic platformer

In order to get started with this project, the best thing we can do (especially if you haven’t had any contact with melonJS before) is to use some sample code. By default, when you download the engine, it’ll come with a set of sample projects you can check out (they’re inside the examples folder)

This example code is what we’ll use to jumpstart our project.inside of it, you’ll find:

  • A data folder, containing everything not-code related. Here you’ll find sounds, music,images, map definition and even fonts.
  • A js folder, where you’ll store all your game-related code.

An index.html and an index.css files. These will be your point of contacts needed by your app to interact with the outside world.

Understanding the existing code

Leaving aside resources inside the data folder for now, we need to understand what kind of head start this example is giving us. For that, you can either execute the game or keep reading.

Executing the game

To execute the game, you’ll need a few things:

  1. A copy of melonJS. If you’ve downloaded it, then make sure you get the content of the dist folder. Copy it inside any folder and make sure to add include it from the index.html file like any other JS file.
  2. Install (if you haven’t already) the http-server module available in npm, which can quickly serve-up all relevant files inside this folder. If you haven’t installed it yet, simply do:
$ npm install -g http-server

And once installed, from within the project’s folder, run:

$ http-server

With that, you should be able to visit http://localhost:8080 to test the game.

Reviewing the code

Playing around with the game, you’ll notice you have la platformer capable of basic (and very awkward) movement, a couple of different enemies and one collectible. Basically, we were aiming to achieve just this, but with a slightly different control scheme.

The key files to check here are:

  • game.js: This file contains all initialization code, it’s interesting to see how the game graphics get instantiated and the main controlling.
  • screens/play.js: This one has all the code required to setup a level. You’ll notice that it’s not that much. Since level definition is done using another tool (i.e Tiled) this code simply enables that.
  • entities/player.js: Clearly, your main objective. This file contains your character’s movement code, your collision reactions and the controlling key bindings. It’s not that big though, but it’s where you want to spend the most time.
  • entities/enemies.js: Second to the player’s code, this is relevant because you’ll see how to setup automated behavior based on pre-defined coordinates.

The rest of the files are useful, but they’re not that important, we’ll get to them when we need them.

Understanding where everything is coming from

If you’ve done your homework, you’ve probably noticed there is no line instantiating the player nor any of the enemies. And their coordinates are no-where to be found. How is the game, then, able to understand that?

Here is where the level editor comes into play. If you’ve downloaded Tiled, you can open the file called map1.tmx inside the folder data/map and you’ll see something like this:

The center section of the screen is showing you the actual level you’re designing. If you look closely you’ll see images, but also rectangular shapes, some of which have different colors and names. Those are objects that represent things inside the game, depending on their name and the layer they belong to.

Which takes me to the right section of the screen, where you’ll see the layers list (on the top right section). There are different types of layers:

  • Image layers: meant for background or foreground images
  • Object layers: meant for collision objects, entities and anything you’d like to instantiate from the map.
  • Tile layers: Where you’ll place the tiles to create the actual level.

And the lower right corner, containing the tileset of this map. The tileset can also be created by Tiled, and this one can be found inside the same folder, with a tsx extension.

Finally on the left side of the screen, you’ll notice the properties section, here you’ll see details about the object you’ve selected or the layer you’ve clicked on. You’ll be able to change generic properties (like the color of a layer to better understand where its objects are located) and to add custom properties (which will later be passed as arguments to the constructor of your entities in-game).

But now that this is out of the way, you have everything you need to start hacking away.

Changing the movement scheme

Now that we’re ready to code, let’s focus on the main aim of this article, we’ll grab the working version of the code that comes as an example, and we will try to modify it so it works as a typing game.

That means, the first thing we need to change is the movement scheme, or in other words: changing the controls.

So go over to entities/player.js and check out the init method. You’ll notice a lot of bindKey and bindGamepad calls. Those lines are essentially binding a particular key with a logical action. Put simply, it makes sure that whether you’re pressing the right arrow key, the D key or moving the analog stick to the right, the same “right” action will be triggered in your code.

That all needs to go away, so remove away, we won’t have a use for it. And in parallel, let’s create a new file, call it wordServices.js and inside this file, we’ll create an object that will return the words for every turn and it’ll help us understand which action was selected by the player.

Essentially, the service contains a list of words which it then shuffles and every-time you request the list (using the getWords method), you grab a random set of them and assigned them to one of the actions I mentioned above. There are other attributes associated to each action that are relevant as well:

  • The coords property is used to place the text in the right coordinates, based off of the action HUD (more on this later)
  • The regionPostfix property is used to pick the right frame for the actions HUD (essentially highlighting the written action).

And now, let’s see how to request user input in the middle of the game.

Note: Before we move forward, remember that in order to make this new service available to the rest of your code, you’ll have to include it in index.html file, just like you would any other JS library:

<script type="text/javascript" src="js/wordServices.js"></script>

How to capture user input

Capturing user input with melonJS is actually quite easy, since we don’t need melonJS for it. This is, after all, a web page, and you’re more than welcomed (in fact, encouraged to) use a good ol’ input field for this.

You could potentially use a combination of key bindings to mimic the behavior of an input field using game elements, but consider all the possible combinations and behaviors an input field provides by default (i.e pasting text, selecting, moving without deleting characters, and so on), you would have to program all those in just to make it usable.

So instead of that, we can simply add a text field into the main HTML page, and using CSS we can style it to be over the Canvas element and thus, it will be part of the game.

All you need is this code inside your <body> element:

<input type="text" id="current-word" />

Then, although this is completely up to you, I would recommend using jQuery to simplify the code required to attach a callback onto the keypress event. This can of course, be done using vanilla JS, but I prefer the syntactic sugar provided by this library.

The following code living in the loaded method of the game.js file is what takes care of grabbing the user’s input:

It is essentially us capturing the input element and storing it in the me global object. This global variable contains anything and everything you need for the game.

With that, we can setup the event handler for any key pressed. And as you can see, I’m checking for key code 13 (which stands for the ENTER key) to recognize when the player is done typing, otherwise I’m making sure they’re entering valid characters (I’m simply avoiding special characters to avoid having problems with the default font provided by melonJS).

Last but not least, I’m setting two different states on the StateManager object, lastWord to understand the last word entered by the player, and the partialWord to understand what they’re typing right now. Both these states will become important in a minute.

Sharing data between components

This is a common issue in many frameworks: how can you share data between components? In this case, knowing that we’re capturing input as part of the game component, how do we share that input with others?

My solution was to create a global component that acts as an event emitter:

The code is very straightforward, you can set several “observers” (which are callback functions) for a specific state, and once that state is set (i.e it changes), all those callbacks are called with the new value.

Adding the UI

The final step before creating an actual level, would be to display some basic UI. Why? Because we need to display the different directions the player can move to as well as the words they need to type.

For that, we’ll use two different UI elements:

  • One for the graphics, which will have several different frames, essentially one for the normal image and then one showing each direction as “selected” (this ties in to the “regionPostfix” attribute on the ActionWordsService )
  • One for the text that will be printed around the image from the previous point. This also, by the way, ties in with the “coords” attribute on the ActionWordsService.

For this, we can hitchhike the existing HUD.js file inside the js folder. We’ll add the two new components there.

The first one, the ActionControl component will look like this:

It seems like a lot, but it just does a few things:

  1. It pulls its coordinates from the settings attribute, which we’ll review once we setup the map on Tiled.
  2. It adds code to react to partial words entered. We use the postfix attribute for the currently written word to select the right frame.
  3. And it adds code to react to final words. If there is an action associated to that word (i.e it’s a correct word), then it’ll add points to the player’s score. Otherwise, it’ll shake the screen and play an error sound.

The second graphic component, the words to be written, looks like this:

This component’s heavy lifting is done on it’s draw method. The init method is simply initializing variables. During the call to draw we’re iterating over the selected words, and using the coordinates associated to them, plus a set of fixed numbers, we’re able to position the words around the coordinates of the ActionControl component.

Here is what a proposed design for the action control would look like (and how the coordinates relate to it):

Of course, it should have a transparent background, but you get the point.

Just make sure you save these images inside the /data/img/assets/UI folder, this is so that later, when you open TexturePacker, it will recognize the new images and you’ll be able to add them to the texture atlas.

The above image shows how the new images for the action wheel have been added. You can then click on “Publish sprite sheet” and accept all default options. It will overwrite the existing atlas, so as for your code, you don’t need to do anything. This step is crucial, because the texture atlas gets loaded as a resource (more on this in a minute) and several entities will use it for things like animations, so remember to always do this whenever you’re adding or updating graphics on your game.

Putting everything together with Tiled

Alright, now that we’ve covered the basics, let’s get the game together. First things first: the map.

Using tiled, and the default tileset included in melonJS, I’ve created this map (a 25x16 tiles map, with 32x32px tiles):

Essentially, these are the layers I’m using:

  • HUD: It only contains a single element called HUD.ActionControl (it’s important to keep the names the same, you’ll see why in a minute). The following image shows the properties of this element (notice the custom properties)
  • collision: By default, melonJS will assume any layer that starts with the word “collision” to be a collision layer, meaning any shape inside it will not be traversable. So in this shape you’ll define all shapes for the floor and platforms.
  • player: This layer simply contains the mainPlayer element (a shape that will let melonJS know where the player needs to be placed at the start of the game).
  • entities: In this layer I’ve added the coins, again, their names are important so keep it the same, since they need to match the name you register them with in your code (keep reading, it’ll make sense in a second).
  • The final three layers are just there to be able to add the images for the map and the background.

Once this is ready, we can go to the game.js file and inside the loaded method, we’ll add the following lines:

These lines are registering your entities (at least the ones you want to place directly on the map using Tiled). The name you provide as the first parameter is the name you need to match using Tiled.

Furthermore, in this file, the onLoad method should look like this:

You’re basically requesting a resolution of 965x512 (I’ve found that things work great when the height of your screen is the same as the height of your map, and in our case it is 16*32 = 512). After that, the ActionWordsService is being initialized with 5 words (these are the 5 directions you can move on).

The other interesting line from the onLoad method is this line:

me.loader.preload(game.resources, this.loaded.bind(this));

We’ve not covered resources so far, so this is a great moment to do so.

The resources file

Any type of resource your game needs (i.e images, sounds, background music, JSON configuration files, etc) will need to be added into this file (the resources.js file).

This is what your resource file should look like at this point:

In there you have things such as the tileset used, the map for our screen (notice how the names are always the name of the file without the extension, this is a mandatory requirement, otherwise the resources won’t be found).

Your coins

The coins in the game are very simple, yet when you collide with them, things need to happen, so the code for them would look like this:

Notice how our coin entity is actually extending a CollectibleEntity (this gives it a special collisionType to the entity, so melonJS knows to call the collision handler when the player moves over it), so all you need to do is to call its parent’s constructor and then, on the onCollision method we’re playing a quick sound when you pick it up, add points to the global score and finally , remove the object from the world.

The finished product

Putting everything together you should have a working game which lets you move in 5 different directions based on which word you type.

It should look something like this:

And because this tutorial is already way too long, you can check the full code-base for this game over at Github. Feel free to leave a comment down below if you have any questions or if you’d like to understand how to do anything I haven’t mentioned yet.

I will be creating more tutorials with more specific details about different aspects I haven’t mentioned, so stay-tuned!

Learn More

--

--

I write about technology, freelancing and more. Check out my FREE newsletter if you’re into Software Development: https://fernandodoglio.substack.com/