Layers and sorting

Set the draw order of your sprites and more

To make our simple game look a bit better, we want to add a background image to it. We are going to use this image here (right-click to save it):

 

Since we don't need transparency, I'm using a jpg file here, which tends to compress much better for this type of images. Now let's add a line of code to the load function:

    wade.loadImage('data/background.jpg');

 

Now let's add this image to the scene, in the usual way, at the end of our init function:

    // add a background image
    var backgroundSprite = new Sprite('data/background.jpg');
    this.background = new SceneObject(backgroundSprite);
    wade.addSceneObject(this.background);

Now save and run your app. You'll see that the background image has been loaded and added to the scene correctly but, unfortunately, ended up on top of everything else, basically hiding the rest of our game. This happened because it was added to the scene after all the other objects. To fix the problem, you could add it to the scene before the other objects, however this may be impractical in some situations and there is actually a better way of doing it.

You can change the line that creates your sprite with this:

    // add a background image
    var backgroundSprite = new Sprite('data/background.jpg', 5);

Note the extra argument that we're passing to the Sprite constructor. That is a layer id: it means that our sprite will be created on layer number 5.

When you don't specify a layer id, sprites are created on layer number 1. Layers are drawn in order, and layers with lower id's get drawn on top of layers with higher id's. So layer 5 will be below layer 1 (which is where all our other sprites are at the moment). If you save and refresh now, you'll see how things appear in the correct order.

Technically each layer is a separate HTML5 canvas. This has some serious performance implications too, and to understand that we need to know how WADE works internally.

The important thing to know about WADE's draw mechanism, is that from one frame to the next, it tries to re-draw only the objects that actually need re-drawing. If scene objects don't move, don't animate, and don't change, WADE tries to avoid redrawing them. This is a big performance boost, because on most devices the most important bottleneck is the number of pixels to draw per frame.

Now consider this: if our background was on the same layer as our match-3 items, every time we move an item, WADE would need to redraw the item itself, but also the background object behind it; and since the background object overlaps everything, WADE would need to redraw all the other items too.

With our background object on a separate layer, WADE can redraw only the moving item itself, and any other item that the moving item is overlapping. That is, it has to do at most 2 draw calls per frame, as opposed to 65 draw calls per frame (8x8 items + 1 background).

Of course using more layers requires more memory, and it's up to you to find the optimal balance between memory usage and performance for your particular app. As a general guideline though, background images should be on their own layers and, in general, if there are static objects behind moving objects, they should be on a separate layer.

It is also possible to sort objects within a layer, without having to rely on the order in which objects are added to the scene. WADE provides a couple of highly-optimised sorting methods for the most common scenarios, but also allows you to create your own sorting methods.

Each layer has a sorting mode: the default sorting mode is none, which means that objects don't get sorted and are drawn in the same order as they were added to the scene. The other built-in modes are bottomToTop and topToBottom, that are common in a variety of game types (isometric, top-down RPG, shoot-em-up, etc).

You can set a particular mode by calling wade.setLayerSorting(layerId, sortingType), passing the name of the drawing mode as the sorting type. Alternatively, you can pass your own function as the sortingType argument: this would be a function that look like this:

    function mySortFunction(spriteA, spriteB)
    {
        // return a value >0 if spriteA needs to be drawn before spriteB
        // return a value <0 if spriteB needs to be drawn before spriteA
    }

That function would then be called every frame by WADE for every pair of sprites in your layer that need re-drawing, to determine their draw order.

You don't need to do that here, for this simple game we're happy with the default sorting mode of none, but it's good to know that you have the option to do whatever you like with the sorting of each layer.

There is another cool thing that you can do with separate layers: you can control their translation and scale separately. Let's see how.

First of all, you need to know that in a WADE app we are looking at objects through a camera. That is, when we set the positions of our objects, we don't use screen coordinates, but world coordinates: then there is a camera that moves around the world, and with that WADE determines where objects end up on the screen.

Initially, the camera is looking at the origin (0, 0) of the world: so the center of the world is in the middle of the screen. Also, the camera is set at a distance such that moving an object by 1 world unit, results in the object moving by 1 pixel on the screen. To get the camera position, use your browser's console and type:

    wade.getCameraPosition();

You'll see that this returns an object with x=0, y=0, z=1.

Now let's move the camera to the right by typing (still in the browser's console):

    wade.setCameraPosition({x:40, y:0, z:1});

As you do this, you'll notice that everything in the scene appears to move to the left, which is what we expected but maybe is not exactly what we wanted. In most cases, we want the background image to not move at all when we move the camera. We can do this by changing the background layer's transform:

    wade.setLayerTransform(5, 1, 0);

The first argument is the layer id (5 is our background layer id), the second argument is a scale factor (I'll get to that in a minute), and the third argument is a translation factor. By setting its translation factor to 0 we're saying that the layer should not move, no matter how we translate the camera. So now the background doesn't move, while the other objects do.

In fact, you can use any value that you like for the translation factor: 0 means no movement, 1 means default movement, 0.5 means something in between, negative numbers indicate a movement in the opposite direction, and so on.

However, if we now move the camera along its z axis, which basically means that we zoom in or out, we'll still have the same problem of the background getting bigger or smaller. As you might have guessed, you can change that behavior by setting the scale factor (the second argument) when you call setLayerTransform.

This is especially useful in side-scrolling games, or top-down shooters for examples: by setting different values for the transforms of several layers you can easily achieve some quite interesting parallax effects, giving your game a nice sense of depth.

But this is a match-3 game. Speaking of which, there is no item-matching going on just yet. That's because that is done with some plain javascript code, and since this is a tutorial about WADE and not javascript, I didn't want to go through all that.

Nevertheless, you may be curious to know how you would implement something like the match-3 game mechanics in a WADE framework. In this case feel free to have a look at the source code here, which should be fairly easy to understand - it's not a fully featured match-3 game obviously, but enough to get an idea: test.js and behaviors/match3Item.js.

 

Try it right here

 

And this is what it's like: