Advanced Tutorial: Building composite maps in Phaser using Tiled

Target Audience:

This is a very high level tutorial that will focus on the design of using many Tiled maps to build a game world. I won’t cover how to use Phaser or Tiled, but how to use Tiled with Phaser.
You can get these wonderful tools here:
Phaser (Note: This tutorial uses version 2.3.0 dev)
Tiled

Intro:

How about we build a simple RPG in Tiled? It will have one house on a map, and a black box to represent the player. It’s not as simple as it sounds. Today I’ll introduce how I chose to dynamically load content created using Tiled.

You can download the completed project at http://www.kieve.ca/demos/RPG.zip
And you can play the game at http://www.kieve.ca/demos/RPG/

You can find all the game art I used on OpenGameArt, or created by me:
PathAndObjects_0.png
hyptosis_tile-art-batch-3.png (Modified, added a tiled-aligned wall.)
city_outside.png (Door sprite comes from here.)
forest_tiles.png
interior.png (Modified, changed some black into transparency, added my own walls.)
Audio for the door comes from the Kenney Donation Pack

Tiled Maps:

We will be creating two maps. The first is a traditional “map” of the world. For now it will be a grassy region with some dirt where a house will be placed. The second is what I refer to as a “sub-map”. This sub-map will be placed on top of dirt area.

Both maps are created with the following settings:

Orientation:       Orthogonal
Tile layer format: CSV
Tile render order: Right Down
Tile Size:         32x32

We’ll start with building the house. Create a map with the above settings and make it 13 tiles wide, 9 tiles high. Save it as house0_f0.tmx. I chose this name with intention of building more houses of different shapes with many floors. So this tutorial will cover building “house0”, or the first house and “_f0”, the first floor.

You should now have a single layer called “Tile Layer 1”, or something similar. Delete that layer and create the following 7 layers, 5 tiles layers and 2 collision object layers in the order shown here:
Tiled Tutorial Layers

The house will use the following tile sets:

city_outside.png
interior.png
hyptosis_tile-art-batch-3.png
colors.png

Going to Map -> New Tileset… create each of these. They are all 32×32 with no spacing. Leave the transparent colour unchecked. Make sure they are named like their images. Example: the interior.png tileset should be named interior. This is very important for loading the maps later on in code. Your Tiled should look something like this (my UI is not default and it won’t be exact):
Screen Shot 2015-03-15 at 7.46.10 PM

Each of these layers have a specific purpose. The floor is just that, a floor. This layer will appear as the first above the “ground”, a layer we will create in the world map. indoors0 and indoors1 are used to create the walls and objects that will appear overtop of the walls. outdoors0 and outdoors1 make up the appearance when viewed from outside. Here is what my house looks like:

floor:
Tiled Tutorial Floor

indoors0Tiled Tutorial Indoors0

indoors1Tiled Tutorial Indoors1

All 3 together: Our indoorsTiled Tutorial Indoors

 

You may notice that the floor extends up into a wall. This is intentional, but isn’t used in this tutorial. It is there for a set of stairs to be located in a future tutorial. Feel free to extend that wall into the floor area. It will effect the collision layers later on though.

Now for the outdoors:

outdoors0Tiled Tutorial Outdoors0

outdoors1Tiled Tutorial Outdoors1

 

Both together:Tiled Tutorial Outdoors

 

Collision

That concludes creating the look of the house. Now we need to set up the collision layers. The outdoors and indoors have separate collision layers because the “walls” on the inside actually extend outside. If we only have the indoor collisions, you can’t get beside the house.

Starting with indoors, with the outdoor layers hidden. I’ll be using the red tile from the “colors” tileset. This is not displayed in the game, but used only as a visual guide in Tiled. Make sure that Snap to Grid is selected under “view”. This will make placing the tiles much easier. At the end, it will look something like this:Tiled Tutorial Collision In

You may be wondering why some of the tiles aren’t snapped to the grid, or why they look unusually large. Unfortunately, at the time of writing this tutorial, Tiled does not support resizing the objects. With that in mind, place all your tiles around the edges of the house and the walls indoors, but keep the following in mind while doing so:

All the tiles require two custom properties to be added: width and height. Phaser doesn’t make use of the existing size parameters, so they need to be added manually. The two properties that are already there, size -> width, height will not work. Additionally, some of the tiles will need to have their x and y manually updated to position of the wall. This will move them if updated in the properties file. Dragging them will force them to snap to the grid again, so be careful. This is why the tiles on the left and top of the building are positioned weirdly and the corners are even more odd. The end result is that all of the tiles have x, y, custom width, custom height that describe their collision box. I use many tiles to make up otherwise straight walls so I have a visual indicator, but you could represent an entire wall with one tile and larger widths and heights. If you use many tiles like I did, make use of the duplicate function to duplicate the tiles. This will save a lot of time adding and updating width and height parameters.

There are a few exceptions on the width and height to achieve some effects. We don’t want the player to have their head stop at the base of a wall. We want it to look like you can stand pretty close to a wall. Because of this, the collision box on walls facing “forwards”, walls that we can see the face of will have the bottom cut off. Here is an example:
Tiled Tutorial Collision half

 

The yellow circled areas represent what I mentioned. Walls where the player needs to get close to look like they are standing next to it. Since the player will me more than 32px height, this is important for getting through the hallway into the kitchen area.

The outdoors collision looks very similar:
Tiled Tutorial Collision out

Once the collision maps are all made export this map as JSON to RPG/res/maps/house0_f0.json

We will now build the game maps where this house will be placed. Create a new Tiled map with the settings from before, but make it 150 tiles wide, 150 tiles high. Or, some other relatively large size that can fit a city. Save it as home_village_f0.tmx. This map is the home village for our player, and it’s the first floor. Again, delete “Tile Layer 1” and create the following layers:
Tiled Tutorial Outside layers

The town will use the following tile sets:

forest_tiles.png
PathAndObjects_0.png
colors.png

Select a grass tile from forest_tiles.png and bucket fill the whole “ground” layer. Then use the minimap to move to the center of the map. Using some dirt from PathAndObjects_0.png, create an 11×5 dirt area. This will be where the house goes. If you remember, I had some broken boards in the house that were transparent. This dirt will appear under the house when inside. You should have something that looks like this:
Tiled Tutorial outside

Switch to the “module_view” layer. Turn down the opacity to about 50%. This layer will be used as a guide in Tiled so we know where the house is. Select the blue colour from colors.png and draw the 13×9 house area over the dirt. This layer is never drawn to the player, so don’t worry. It’s just to help us plan in Tiled for building the game. It will look like so:
Tiled Tutorial outside2

Now we can see where the house will be! Great. So we’re going to need to place 3 things into this map. We’re going to need the house, a door, and the player spawn.

So, we’re going to place the house. Select the objects layer and a colour tile, I chose pink. Place this object in the upper right corner of the house area. Give it the name home0 and type sub_map. We’ll need two custom properties: id with value 0 and sub_map with value house0_f0. That’s the house we just made! Your object and properties should look like this:
Tiled Tutorial house0

As you can see, we now need to make two more objects. To create the door, I used pink again. Place it where the door of our house would be. In this image it’s 7 across and 7 down from the top left corner of the house. Name it home0_door with type door. It will require custom properties: id with value 0parent with value home0 and sprite with value door. We want to be able to know which house it belongs to, and what to draw it with. We’ll be creating that sprite later in the code. You can then create a an object called spawn with type player_spawn. I made this one green. It doesn’t have any custom properties and you can place it anywhere, but I recommend you not put the spawn inside the house. There is some cool door logic, but it will depend on the player starting outside the house. Your finished “town” should look like this:
Tiled Tutorial house0 done

That’s it! We’re done making our map. It doesn’t require a collision map because the house will cover that. If you want to have walls and fences in the town, you will have to make a collision layer similar to how we did in the house.

Export this map as JSON to RPG/res/maps/home_village_f0.json

Coding Time!

Wow, that’s a lot just to make some maps. You’ll be able to make tons of houses, caves, and cities using this process, but it doesn’t do you any good if you can’t actually USE it. So, let’s get the game coding started.

To start, make sure you download a copy of the project and grab the resources. RPG/res/ contains the audio, sprites and other images. It also contains the maps I made here, but you can use your own if you want.

You’ll also need the Phaser library. Put it at RPG/lib/phaser.js (at least version 2.3.0). I don’t recommend using a minified version while developing.

Create your RPG/index.html like this. As you can see, there are a lot of files we haven’t event start writing yet, but I will get to them in order.

<!doctype html>
<html>
 <head>
 <meta charset="UTF-8" />
 <title>Title change!</title>
 <!-- Load libraries first -->
 <script src="lib/phaser.js"></script>

 <!-- Load the startup state -->
 <script src="js/state/startup.js"></script>

 <!-- Load game logic -->
 <script src="js/map/module.js"></script>
 <script src="js/map/submap.js"></script>

 <script src="js/map/object/door.js"></script>

 <!-- Load remaining states -->
 <script src="js/state/preloader.js"></script>
 <script src="js/state/game.js"></script>
 <script src="js/state/hi"></script>

 <!-- Load this after all the states are created -->
 <script src="js/main.js"></script>
 </head>
 <body>
 </body>
</html>

Let’s start with RPG/js/main.js

RPG.main = function() {
    var game = new Phaser.Game(1280, 720, Phaser.AUTO);

    game.state.add('startup', RPG.State.Startup);
    game.state.add('preloader', RPG.State.Preloader);
    game.state.add('game', RPG.State.Game);
    game.state.start('startup');
};

window.onload = function() {
    RPG.main();
};

We define RPG.main which will run when the page finishes loading. Then we add our states. We’ll use startup to setup our namespaces, hold our map names and load our JSON maps so we can load all their tilesets. The preloader state will load all of the images and sounds. Finally the game state will run the game. Starting off in the startup state:

RPG/js/state/startup.js

// Setup our namespaces and resource lists.
RPG = {};
RPG.State = {};
RPG.Map = {};
RPG.Map.Object = {};

RPG.Map.MAPS = [
    // Overworld maps
    'home_village_f0',

    // Modules used in maps
    'house0_f0'
];

RPG.State.Startup = function(game) {};
RPG.State.Startup.prototype = {
    preload: function() {
        // Load our "loading" images
        this.load.image('loading_text', 'res/img/loading_text.png');
        this.load.image('loading_bar_bg', 'res/img/loading_bar_bg.png');
        this.load.image('loading_bar', 'res/img/loading_bar.png');
        this.load.image('loading_bar_fg', 'res/img/loading_bar_fg.png');

        // Load the json maps, so we can load the images in the next steps.
        var map;
        for (var i = 0; i < RPG.Map.MAPS.length; i++) {
            map = RPG.Map.MAPS[i];
            this.load.tilemap(map, 'res/maps/' + map + '.json', null, Phaser.Tilemap.TILED_JSON);
        }
    },

    create: function() {
        this.scale.pageAlignHorizontally = true;
        this.scale.pageAlignVertically = true;

        //physics system
        this.game.physics.startSystem(Phaser.Physics.ARCADE);

        this.game.state.start('preloader');
    }
};

We will start with creating our namespaces. We load this file in HTML early on so that all the other states can make use of the namespace without worrying if they have been created yet. We then create an array holding the names of our JSON map files we created at the beginning of the tutorial.

In the preload of this state we will load the images that make up the loading bar. What is may seem unusual here is that we also load all of the json maps so we can extract the tilesets and load them in the preload phase.

When creating this state, we set the alignment of the game and start the physics system. Now we need to load all of our assets.

RPG/js/state/preloader.js

RPG.State.Preloader = function(game) {};
RPG.State.Preloader.prototype = {
    preload: function() {
        // Black loading screen
        this.game.stage.backgroundColor = '#000000';

        this.add.sprite(492, 175, 'loading_text');
        this.add.sprite(120, 580, 'loading_bar_bg');
        this.preloadBar = this.add.sprite(140, 600, 'loading_bar');
        this.add.sprite(140, 600, 'loading_bar_fg');
        this.load.setPreloadSprite(this.preloadBar);

        // Load our sprites
        this.load.image('player', 'res/img/sprites/player.png');
        this.load.image('door', 'res/img/sprites/door.png');
        this.load.image('stairs', 'res/img/sprites/stairs.png');

        // Get some audio up in this shit.
        this.load.audio('doorOpen_1', 'res/audio/doorOpen_1.ogg');
        this.load.audio('doorClose_4', 'res/audio/doorClose_4.ogg');

        // Load all of our maps and their components.
        this.loadTileMaps();
    },

    // Many of the maps use the same tile images. We create a map of all these images
    // so that we only load them once.
    loadTileMaps: function() {
        var tileSetMap = {};
        for (var i = 0; i < RPG.Map.MAPS.length; i++) {
            this.addTileSets(RPG.Map.MAPS[i], tileSetMap);
        }

        for (var key in tileSetMap) {
            if (tileSetMap.hasOwnProperty(key)) {
                this.load.image(key, tileSetMap[key]);
            }
        }
    },

    addTileSets: function(mapKey, tileSetMap) {
        var tileSets = this.cache.getTilemapData(mapKey).data.tilesets;
        var key, value;
        for (var i = 0; i < tileSets.length; i++) {
            key = tileSets[i].name;
            value = 'res/maps/' + tileSets[i].image;
            if (key in tileSetMap) continue;
            tileSetMap[key] = value;
        }
    },

    create: function() {
        this.game.state.start('game');
    }
};

That’s quite a lot, but for the most part it’s pretty standard. Loading some images and audio files. The interesting part comes with this:

    // Load all of our maps and their components.
    this.loadTileMaps();

Let’s look at what this does, but to understand what it does we need to look at “addTileSets” first.

    addTileSets: function(mapKey, tileSetMap) {
        var tileSets = this.cache.getTilemapData(mapKey).data.tilesets;
        var key, value;
        for (var i = 0; i < tileSets.length; i++) {
            key = tileSets[i].name;
            value = 'res/maps/' + tileSets[i].image;
            if (key in tileSetMap) continue;
            tileSetMap[key] = value;
        }
    }

We pass in a mapKey, the name of a map. We created these in startup.js. They’re “house0_f0” and “home_village_f0”. The second parameter, “tileSetMap” is a JavaScript map of key-value pairs. To begin we get a list of tilesets for this map. We loaded these maps in startup, so Phaser has cached them. We use this to grab an array of tilesets. These tilesets contain the name we used when creating them in Tiled. This is why the name was very important.

We then loop through each of the tilesets, we grab the name and construct the path to the image from it. So the colors tileset will be at res/img/tiles/colors.png. So, why am I making the value ‘res/maps/’? Well, the tile set is also saved with a path to the image relative to the json location. This means that when using tiles in res/img/tiles/ and exporting the maps to res/maps/ the relative path to the img is ../img/tiles/. So, at the end of all this the result is a path that looks like this: res/maps/../img/tiles/colors.png. The .. essentially erases the maps/.

We then check if this tilset has already been added, incase another map already used it. If not, we add this key (the name) and the value (the path) to the tileSetMap that was given to us. Now we can see what loadTileMaps does.

    loadTileMaps: function() {
        var tileSetMap = {};
        for (var i = 0; i < RPG.Map.MAPS.length; i++) {
            this.addTileSets(RPG.Map.MAPS[i], tileSetMap);
        }

        for (var key in tileSetMap) {
            if (tileSetMap.hasOwnProperty(key)) {
                this.load.image(key, tileSetMap[key]);
            }
        }
    }

We create our key-value pair map, tileSetMap and go through each map name. We grab the list of tile sets that are used. We then loop through this resulting map and load all the images. Cool! Now we don’t have to manually load all of those images.

Before we can look at game.js, we’re going to have to take a look at module.js. What is module.js? It’s a “template” or “cookie cutter” to create our tilemaps. We’ve loaded all the content for a map, now we need to add them to the world. module.js will handle creating layers and finding the objects in a tile map, then adding an instance of them to the world.

Looking at RPG/js/map/module.js one piece at a time.

RPG.Map.Module = function(game, key) {
    this.game = game;
    this.tilemap = game.add.tilemap(key);
    // Add the tilesets
    for (var i = 0; i < this.tilemap.tilesets.length; i++) {
        this.tilemap.addTilesetImage(this.tilemap.tilesets[i].name);
    }
};

Starting with the constructor. We give it a reference to the Phaser game object and a key. This key is the name of the map object. Here it will be either house0_f0 or home_village_f0. We add the tilemap to the game and then add all the tileset images to the tilemap. Since we’re being consistent with our naming, we can easily grab this information from the tilemap, as we do here.

Let’s go through the prototype functions we get by using these modules.

RPG.Map.Module.prototype = {
    /* Other functions */

    // Pass through to the Phaser.TileMap createLayer function
    createLayer: function(args) {
        return this.tilemap.createLayer.apply(this.tilemap, arguments);
    }

    /* Other functions */
};

This is just a shortcut to be able to quickly access the tilemap createLayer functions. Calling createLayer on our module will be the same as calling createLayer on the tilemap.

RPG.Map.Module.prototype = {
    /* Other functions */

    // Expects strings for the layer names
    // Returns a map containing the layers
    createLayers: function(args) {
        var result = {};
        var key;
        for (var i = 0; i < arguments.length; i++) {
            key = arguments[i];
            result[key] = this.tilemap.createLayer(key);
        }
        return result;
    }

    /* Other functions */
};

This function lets us quickly create many layers. For example, calling module.createLayers(‘layer1’, ‘layer2’, ‘layer3’); will create all 3 layers. It then returns a map containing these layers. The key is the layer name and the value is a reference to the layer.

RPG.Map.Module.prototype = {
    /* Other functions */

    // Searches the object layer for a specific type
    findObjectsByType: function(type) {
        var self = this;
        var result = [];
        this.tilemap.objects['objects'].forEach(function(element) {
            if (element.type === type) {
                // Phaser uses top left, Tiled bottom left so we have to adjust the y position
                // also keep in mind that the cup images are a bit smaller than the tile which is 16x16
                // so they might not be placed in the exact pixel position as in Tiled
                element.y -= self.tilemap.tileHeight;
                result.push(element);
            }
        });
        return result;
    }

    /* Other functions */
};

Here we can search the “objects” layer for a specific type (like “player_spawn” or “door”) and get an array containing all the objects found.

RPG.Map.Module.prototype = {
    /* Other functions */

    getCollisionSprites: function(layer, group, tileX, tileY) {
        tileX = tileX || 0;
        tileY = tileY || 0;

        var self = this;
        var result = [];
        var sprite;
        this.tilemap.objects[layer].forEach(function(element) {
            element.y -= self.tilemap.tileHeight;
            sprite = group.create(element.x + tileX*32, element.y + tileY*32);
            self.game.physics.arcade.enable(sprite);
            sprite.body.setSize(element.properties.width, element.properties.height);
            sprite.body.immovable = true;
            result.push(sprite);
        });
        return result;
    }

    /* Other functions */
};

Here is a very important one. We added all those collision tiles to our maps, this let’s us load them. We pass in which layer we want to look through, like collisions_outside, a Phaser group to add them to, and tileX and tileY offsets. We include these offsets because the objects in house will have positions as if the world is 13×9 tiles big, but ones you place it in the map, it’s not at 0,0. We use the tile offsets to update the position of all the collision sprites to make sure they are in the right position in the much bigger world. We need to adjust the element.y since Phaser uses the top left as the origin, but Tiled uses the bottom left. We then enable physics on all the sprites and set the size from the custom properties we made in Tiled. Since these represent walls, we don’t want them to move, so we set them immovable and add them to the array.

RPG.Map.Module.prototype = {
    /* Other functions */

    spriteFromObject: function(element, group) {
        var sprite = group.create(element.x, element.y, element.properties.sprite);

        // Copy all properties to the sprite
        sprite.properties = {};
        Object.keys(element.properties).forEach(function(key){
            sprite.properties[key] = element.properties[key];
        });

        return sprite;
    }

    /* Other functions */
};

This function is similar to collision sprites, but it copies over the custom properties as well. This function will also grab the custom property “sprite” and use that as the image. This is why we added it to the door in Tiled.

That’s everything for Module. Now we need to take a look at RPG/js/map/submap.js

// x, y are tile coords
RPG.Map.SubMap = function(module, tileX, tileY) {
    this.module = module;
    this.tileX = tileX || 0;
    this.tileY = tileY || 0;

    this.tileLayers = this.module.createLayers(
        'floor', 'indoors0', 'indoors1', 'outdoors0', 'outdoors1'
    );

    // We need to be able to position these.
    var layer;
    for (var key in this.tileLayers) {
        if (this.tileLayers.hasOwnProperty(key)) {
            layer = this.tileLayers[key];
            layer.fixedToCamera = false;
            layer.scrollFactorX = 0;
            layer.scrollFactorY = 0;
            layer.position.set(this.tileX*32, this.tileY*32);
        }
    }
};

For the submaps, we give it the module (in this case, ‘house0_f0’ module) and a tileX and tileY for the position in the world. Since the house won’t be at 0,0 we need to be able to move it around. We then create all the layers for this house. We’ll assume that all submaps will have the layers listed. All new submaps will should be created with those layers.

Phaser doesn’t really support an easy way to move these layers around. So, we loop through them all and unfix it from the camera and remove the scrolling. The result is that we can now set the position! Yay.

Now we’ll take a look at the functions here.

RPG.Map.SubMap.prototype = {
    /* Other functions */

    setPosition: function(x, y) {
        var layer;
        for (var key in this.tileLayers) {
            if (this.tileLayers.hasOwnProperty(key)) {
                layer = this.tileLayers[key];
                layer.position.set(x*32, y*32);
            }
        }
    }

    /* Other functions */
};

This function will aid us in updating all the layers if we want to move this submap. If you want a moving house, you can have one! Unfortunately this doesn’t update the collision objects. Feel free to modify this to do that.

RPG.Map.SubMap.prototype = {
    /* Other functions */

    getAlpha: function(layer) {
        return this.tileLayers[layer].alpha;
    }

    /* Other functions */
};

When passed the name of a layer, it will return the current alpha of that layer.

RPG.Map.SubMap.prototype = {
    /* Other functions */

    setIndoorAlpha: function(alpha) {
        this.tileLayers['floor'].alpha = alpha;
        this.tileLayers['indoors0'].alpha = alpha;
        this.tileLayers['indoors1'].alpha = alpha;
    }

    /* Other functions */
};

This will set all the indoor layers to the given value. Useful for hiding the layers when you want to show the outside layers.

RPG.Map.SubMap.prototype = {
    /* Other functions */

    setOutdoorAlpha: function(alpha) {
        this.tileLayers['outdoors0'].alpha = alpha;
        this.tileLayers['outdoors1'].alpha = alpha;
    }

    /* Other functions */
};

Similarly, this will update the alpha of the outside layers.

That finishes up the submap.js file. Now we can load, create, and position our tiled maps! The last thing we need to look at before diving into the game is our door! So let’s look at RPG/js/map/submap.js

RPG.Map.Object.Door = function(game, sprite) {
    this.game = game;
    this.sprite = sprite;
    this.id = sprite.properties.id;
    // The percent this door is open, [0, 1]
    this.delta = 0;

    this.game.physics.arcade.enable(this.sprite);

    // Doors are 1.5 tiles high, so we need to move it down half a tile to align it.
    this.sprite.position.y += 16;

    // Adjust the hit box to have a "slider" for activation
    this.sprite.body.setSize(32, 48, 0, 16);

};

We’ll give our door object a reference to the game and the sprite it represents. We copy over the id so it’s easier to access. (We set this ID in Tiled. When we create the sprite from an object it’s copied over, as we defined in module.js). We’ll enable the physics. Our tile isn’t aligned to the 32×32 squares because it’s 48 pixels tall. This makes it 16 pixels above our doorframe, so we need to move it down.

I adjust the hitbox so as you run over the door, the outside fades out and the inside fades in. The hitbox needs to be extended a little for this to be smooth.

RPG.Map.Object.Door.init = function(game) {
    RPG.Map.Object.Door.sound_open = game.add.audio('doorOpen_1');
    RPG.Map.Object.Door.sound_close = game.add.audio('doorClose_4');
};

We’ll call this once to load the sounds the door will use. It’s attached to the Door object so all the doors can make use of the same sound files.

RPG.Map.Object.Door.prototype = {
    /* Other functions */

    isOpen: function() {
        return this.delta !== 0;
    }

    /* Other functions */
};

This simply checks if the door is open. We define this to be “delta !== 0” which means when the outside layer is fading or invisible, the door is open. This can be expanded and adjusted later as needed.

RPG.Map.Object.Door.prototype = {
    /* Other functions */

    overlapTrigger: function(player) {
        var wasOpen = this.isOpen();

        // Calculate the position.
        var cX = this.sprite.x + this.sprite.width / 2;
        var cY = this.sprite.y + 40;
        var pX = player.x + player.width / 2;
        var pY = player.y + (player.height * 3)/4;

        var signum = cY < pY ? -1 : 1;
        var dist = Math.sqrt((cX - pX)*(cX - pX) + (cY - pY)*(cY - pY));

        var alpha = 0.5;
        if (signum < 0) {
            alpha = Math.max(0, alpha - dist / 24)
        } else {
            alpha = Math.min(1, alpha + dist / 24);
        }

        this.delta = alpha;
        this.sprite.renderable = !this.isOpen();

        var isOpen = this.isOpen();
        if (wasOpen !== isOpen) {
            if (wasOpen) {
                RPG.Map.Object.Door.sound_close.play();
            } else {
                RPG.Map.Object.Door.sound_open.play();
            }
        }
    }

    /* Other functions */
};

This is what makes the door open / close. As the player runs over the door, we calculate the distance to the centre of the door. As you walk over the door the “delta” value will update. We will use this in the game to change how the house is rendered. We also play the “open” or “close” door sounds if the door opened or closed because of the player.

That’s everything! Now we can get the the best part and make use of everything we’ve built.

RPG/js/state/game.js

/**
 * Created by knash on 15-03-12.
 */

RPG.State.Game = function(game) {};
RPG.State.Game.prototype = {
    create: function() {
        // Load the current over world map
        this.home_village_f0 = new RPG.Map.Module(this.game, 'home_village_f0');
        // Load the modules that will be used in the map.
        this.modules = {};
        this.modules['house0_f0'] = new RPG.Map.Module(this.game, 'house0_f0');

        // We manually add the over world ground for now...
        this.ground = this.home_village_f0.createLayer('ground');

        // Resize the game world to match the layer dimensions
        this.ground.resizeWorld();

        // Place the subMaps
        this.subMaps = {};
        var subMapLocations = this.home_village_f0.findObjectsByType('sub_map');
        var location, tileX, tileY;
        for (var i = 0; i < subMapLocations.length; i++) {
            location = subMapLocations[i];
            tileX = location.x / 32;
            tileY = location.y / 32;
            this.subMaps[location.name] = new RPG.Map.SubMap(
                this.modules[location.properties.sub_map], tileX, tileY);
        }
        this.subMaps['home0'].setIndoorAlpha(0);

        // Place some objects
        this.doorGroup = this.game.add.group();
        var tiledDoors = this.home_village_f0.findObjectsByType('door');
        this.doors = {};
        var doorSprite = this.home_village_f0.spriteFromObject(tiledDoors[0], this.doorGroup);
        RPG.Map.Object.Door.init(this.game);
        this.doors[doorSprite.properties.id] = new RPG.Map.Object.Door(this.game, doorSprite);

        // Collision map!
        this.isOutdoors = true;
        this.indoor_walls = this.game.add.group();
        this.outdoor_walls = this.game.add.group();
        var home0 = this.subMaps['home0'];
        this.inCollisionObjects = this.modules['house0_f0'].getCollisionSprites(
            'collision_indoors', this.indoor_walls, home0.tileX, home0.tileY);
        this.outCollisionObjects = this.modules['house0_f0'].getCollisionSprites(
            'collision_outdoors', this.outdoor_walls, home0.tileX, home0.tileY);

        // Make our player
        var spawn = this.home_village_f0.findObjectsByType('player_spawn');
        this.player = this.game.add.sprite(spawn[0].x + 4, spawn[0].y - 16, 'player');
        this.game.physics.arcade.enable(this.player);
        this.player.body.setSize(20, 20, 0, 20);
        this.game.camera.follow(this.player);

        // Input
        this.cursors = this.game.input.keyboard.createCursorKeys();
    },

    // This is called by the physics overlap function during the update
    doorHandler: function(player, doorSprite) {
        var door = this.doors[doorSprite.properties.id];

        // This will update the doors "delta", telling us how far over the player is.
        door.overlapTrigger(player);

        var alpha = door.delta;
        this.subMaps[doorSprite.properties.parent].setIndoorAlpha(alpha);
        this.subMaps[doorSprite.properties.parent].setOutdoorAlpha(1 - alpha);
        this.isOutdoors = !door.isOpen();
    },

    update: function() {
        // Collision

        // Check if we're overlapping the door.
        this.game.physics.arcade.overlap(this.player, this.doorGroup, this.doorHandler, null, this);
        if (this.isOutdoors) {
            this.game.physics.arcade.collide(this.player, this.outdoor_walls);
        } else {
            this.game.physics.arcade.collide(this.player, this.indoor_walls);
        }

        // Player movement
        this.player.body.velocity.y = 0;
        this.player.body.velocity.x = 0;

        if (this.cursors.up.isDown) {
            this.player.body.velocity.y -= 200;
        } else if (this.cursors.down.isDown) {
            this.player.body.velocity.y += 200;
        }

        if (this.cursors.left.isDown) {
            this.player.body.velocity.x -= 200;
        } else if (this.cursors.right.isDown) {
            this.player.body.velocity.x += 200;
        }
    }
};

Don’t have to say much here. The comments explain it all and it’s built using the other other stuff.

See all this live at http://www.kieve.ca/demos/RPG/

A Drip Fell

Finally going to put one of my domains to use. Let’s see how this blogging thing goes.

My plan is to use this as a place to document various things I learn while working on my various projects and games. If I run into a problem that I found difficult or time consuming to solve, I’ll throw a tutorial or discussion up here.