Tutorial: Using sprite sheets in MelonJS

Andreas Löw
Tutorial: Using sprite sheets in MelonJS
Last update: More than 3 months ago

In this tutorial you are going to learn:

  • Why you should use a sprite sheet in your MelonJS game
  • Using TexturePacker to create a sprite sheet
  • Optimizing the sprite sheet for size faster downloads
  • Adding a static background
  • Creating an animation

Your goals for this tutorial:

  • Learn why you should use sprite sheets in your MelonJS game
  • Use TexturePacker to create a sprite sheet
  • Optimize the sprite sheet to improve startup time and performance of your game
  • Add a static background
  • Create and play an animation

Why you should use sprite sheets

Using a sprite sheet instead of a bunch of single images comes with some advantages:

  • Saving memory — the sheet uses less memory
  • Faster startup time — only one file has to be loaded from the server
  • Increased frame rate — MelonJS optimizes the GPU usage and makes your game faster

Creating a sprite sheet

The easiest way to create an optimized sprite sheet is using TexturePacker — available for Windows, MacOS and Linux (Ubuntu):

Selecting your data format

For this tutorial start a trial of the pro features of TexturePacker.

TexturePacker as a free mode which is automatically activated after the trial. It works with MelonJS if you use the generic JSON Array data format but does not contain the optimizations and presets for MelonJS.

You should see a screen similar to the one below after you've installed TexturePacker:

Selecting your game development framework

TexturePacker supports a bunch of different game and web development frameworks. Start by selecting MelonJS as your Data format .

Adding sprites

Next add your sprites by dropping the cityscene folder onto the center of TexturePacker. All known image formats (such as psd, png, jpg, even swf) inside the folder are added to the sprite sheet automatically. New or changed files update the sprite sheet as soon as you re-enter the application.

Optimizing your sprite sheet for speed and size

TexturePacker has already optimized your sprite sheet using Trim . You see this below the texture: 85% overdraw . The missing 15% are transparent pixels that have been removed. They are not needed in the sprite sheet — keeping them would waste memory and require more rendering. MelonJS gets the information about the removed pixels through the data file and keeps your animations aligned.

TexturePacker can rotate sprites to pack into a texture size. This causes some calculation overhead in MelonJS. If you prefer smaller memory over performance, click Show advanced in Layout and check Allow rotation .

Reduce the file size to improve the download speed of your game by setting Pixel format to INDEXED . This enables the pngquant — storing the image as 8 bit indexed png. This decreases the file size from 383kb to 102kb while keeping the image quality almost identical. If you see color bands in gradiens set Dithering to PngQuant High .

Original scene with 32 bit colors, 383kb: Original game scene with 32 bit colors

Optimized scene with indexed colors, 102kb: Optimized game scene with indexed colors (pngquant)

Creating the sprite sheet

You can now press Publish sprite sheet from the top toolbar to create a data file and the sprite sheet image. Enter cityscene.json as file name and save it in the data/img folder. You can change the file name under Data file if you have to.

Importing and using the sprite sheet in MelonJS

Start by adding the sprite sheet and data file toto the list of assets to be loaded in resources.js :

game.resources = [
    { name : "texture", type : "json",  src : "data/img/cityscene.json" },
    { name : "texture", type : "image", src : "data/img/cityscene.png" }
];

Note that resources.js is automatically managed by the MelonJS boilerplate, as it builds the resources list and exposes it to your app as game.resources when using the grunt serve task.

Now create a global reference of your Texture under the game namespace in game.js :

"loaded" : function () {

    // load the texture atlas file
    game.texture = new me.video.renderer.Texture(
        me.loader.getJSON("texture"),
        me.loader.getImage("texture")
    );

    …
}

Add the static background

Adding a static background is pretty easy:

game.PlayScreen = me.ScreenObject.extend({
    /**
     *  action to perform on state change
     */
    onResetEvent: function() {

        // viewport width and height
        var w = me.game.viewport.width;
        var h = me.game.viewport.height;

        // add the Background
        var background = game.texture.createSpriteFromName("background");

        // set position to the middle of the viewport
        // (as the sprite anchorPoint is (0.5, 0.5)
        background.pos.set(w / 2, h / 2, 1);

        // add to the scene
        me.game.world.addChild(background, 1);
    }
});

The code uses the background texture from the sprite sheet and adds it to the screen. It also centers the background by setting it's coordinates to the screen's middle.

Note that as the default settings in TexturePacker is to use center as the anchorPoint, we also set the default position for the background to the center of the screen, but you can of course change it to top-left or whatever you need, and change the default position accordingly (that would be [0, 0] if top-left coordinates are used for the anchor point).

Also, note that melonJS 3.x provides an alternate and easier way of creating directly a me.Sprite using a texture atlas, as shown below :

// add the Background with default position to the middle of the viewport
var background = new me.Sprite(
    me.game.viewport.width / 2,
    me.game.viewport.height / 2,
    {
        image : game.texture,
        region : "background.png"
    }
);
// add to the scene
me.game.world.addChild(background, 1);

Add the cap guy animation

Let's now add an animation. To do this create a new Entity in entity.js :

/**
 * Cap Guy entiry
 */
game.CapGuyEntity = me.Entity.extend({
    /**
     * constructor
     */
    init: function (x, y) {

        // call the super constructor
        this._super(me.Entity, "init", [200, 140, {width : 100, height : 300}]);

        // create an animation using the cap guy sprites, and add as renderable
        this.renderable = game.texture.createAnimationFromName([
            "capguy/walk/0001", "capguy/walk/0002",
            "capguy/walk/0003", "capguy/walk/0004",
            "capguy/walk/0005", "capguy/walk/0006",
            "capguy/walk/0007", "capguy/walk/0008"
        ]);

        // enable this, since the entity starts off the viewport
        this.alwaysUpdate = true;
    },
});

This code initialises a simple entity at 200,140 and adds a renderable component. This component is initialised with the animation frames from the CapGuy walk cycle.

Add the animation to the game scene, add the following code at the end of onResetEvent in play.js :

// add the Cap Guy
var CapGuyEntity = new game.CapGuyEntity();
// add it to the scene
me.game.world.addChild(CapGuyEntity, 2);

If you start the demo now you'll see CapGuy walking... but he's not yet moving. Add the following code block to entity.js

/**
 * manage the enemy movement
 */
update : function (dt) {

    // just manually change the guy position
    this.pos.x += 0.3*dt;

    // repeat once leaving the viewport
    if (this.pos.x >= me.game.viewport.width) {
        this.pos.x = 0;
    }

    // call the parent function
    this._super(me.Entity, "update", [dt]);

    return true;
}

This block moves CapGuy a bit to the right. dt is the time difference between the last frame and the current frame. Using this as a base for the update keeps the animation speed constant. The if block resets CapGuy to the left border after he left to the right.

Using the command line / grunt

TexturePacker starts with the graphical user interface by default. It also comes with a command line client — allowing you to fully automate the process.

Use Install Command Line Tool from TexturePacker's menu to add the TexturePacker command.

The command line tool allows you to fully configure the creation process of your sprite sheets. E.g. to create a MelonJS sheet you can use:

TexturePacker --format melonjs \
    --data  data/img/cityscene.json \
    --sheet data/img/cityscene.png \
    data/assets/cityscene

You can also use it to update the .tps file. This makes your life easier: Configure everything in the graphical user interface — then re-build sheets on change:

TexturePacker *.tps

You can even add it to grunt using the grunt-shell plugin:

shell: {
    update_spritesheets: {
        command: 'TexturePacker *.tps'
    }
}

Summary

You've now learned how to create sprite sheets and use them in your MelonJS project. TexturePacker makes it easy to handle your assets and optimize your game for size and speed.