Using sprites and tile maps

In this section, you will learn how to use a tile map editor to create the visual assets that your app uses. Then we will modify the basic files you created earlier to use these assets. Later, we will manipulate these assets to make the game playable, but for now, we just want to populate your skeleton Cocos2d-html5 app with a background, a hero, and coins for the hero to collect.

Let's get started. First, we need to set up a source that you can use to construct the graphics, so within your main project folder, create a new folder and name it images. This app requires only simple graphics, so right-click and save the image below as tiles.png in your images folder.


Save this image as tiles.png in your images folder.

Create a tile map

Open Tiled, the tile map editor you downloaded at the beginning of this tutorial.

We will build the graphics for your app as tile maps and save them as TMX files. First we need to make sure that we create the TMX files in a format that Cocos2d-html5 supports.

  1. Click Edit > Preferences.
  2. On the General tab, in the Store tile layer data as drop-down list, click Base64 (gzip compressed).

The Preferences dialog box.

We're now ready to create a tile map. To open the New Map dialog box, click File > New. Make sure the orientation is set to orthagonal to create a square-grid system, and we'll adjust the map size and tile size to match the screen dimensions of a BlackBerry 10 device. In the Map size section, select a width of 40 tiles and a height of 24 tiles, and in the Tile size section, select a width and height of 32 px to match tileset.png.


The New Map dialog box.

This sample app is designed to fit the screen of a BlackBerry Z10 device. When you design apps for different device models, you can reference the screen size chart in the UI guidelines.

Click OK and your tile map is ready for editing.

Add tiles to your map

To paint to the tile map, first we need to import our image source.

  1. Click Map > New Tileset.
  2. In the New Tileset dialog box, click Browse.
  3. In the images folder, select tileset.png.
  4. Click OK.

You can now find the tile set in the Tilesets pane.


The Tilesets pane with tileset.png imported.

To make the placement of tiles and objects easier, you should make two quick changes to the settings: click View > Show Grid and View > Snap to Grid. The first change displays a grid over your tile map, and the second change makes sure that the dimensions of any objects you add will conform to that grid.


The grid view in Tiled.

We're now ready to start creating the game level.

Create a custom level

With your tiles in place, you can begin to paint them to your tile map. For this sample app, we will use:

  • Black to represent walls and platforms
  • Red to represent lava
  • Dark blue to represent water
  • Light blue to represent ice
  • White to represent empty space

The finished tile map for the BoxQuest sample app.

In your main project folder, create a new folder and name it tmx. You can save your tile maps in this folder.

If you're feeling creative, you can paint your own custom level. Otherwise, you can download 0-0.tmx, which is the finished tile map, from the BoxQuest repository on GitHub, and follow along to see how its layers were created.

Export your tile map

Object layers dictate where objects appear on the screen. We'll add these layers manually, but first you need to export your finished tile map as an image file, so that you can use it as a background for your app.

Note that in the BoxQuest sample app, Tile Layer 1 is named Layer0. If you are following along with the completed file, make sure you select only this layer in the Layers pane.

Click File > Save As Image, and save 0-0.png in your images folder.

Add a portals layer

We can now add object layers for portals, coins, and walls. First we need to place the portals, which are the points where our hero starts and completes the game level.

Click Layer > Add Object Layer and a new Object Layer 1 appears in the Layers pane. Rename the layer as portals, to keep track of where we are.


The new object layer in the Layers pane.

Click the Insert Object tool in the toolbar at the top.


The Insert Object tool in the top toolbar.

As you move the pointer you can see your current coordinates in the lower-left corner of the window. Click the map at coordinates 1,18 to place an object there. We need to name this object:

  1. Right-click the object.
  2. In the pop-up menu, click Object Properties.
  3. In the Object Properties dialog box, in the Name field, type start.
  4. Click OK.

This object acts as the start point for our hero and it's important to create it before we create the end point. Repeat the steps above to create an end point named finish, at the end of your custom level (if you downloaded the finished tile map, you can see the end point at coordinates 38,1).


The start and end points on the tile map.

Add a coins layer

We now need to place seven coin objects around the tile map for our hero to collect. Add a new object layer and name it coins.

Use the Insert Object tool to place each coin. We don't need to assign names to coins, because they are referenced in the code by their position on the map.


The coin objects placed in the tile map.

Add a walls layer

Before we move on, add a final object layer and name it walls. This layer becomes important later, when we add physics to your app.

Click and drag the Insert Object tool to place objects that overlap with every black tile wall and surface in the tile map. These objects don't need names.


The wall objects placed in the tile map.

The grid is not shown in this image, so you can see how the wall objects are placed more clearly.

Save your files

Your tile map is now complete with portals, coins, and walls, and can be saved as a TMX file.

  1. Click File > Save As.
  2. In the Save As dialog box, navigate to the tmx folder.
  3. In the File name field, type 0-0.tmx.
  4. Click Save.

For the Ripple emulator to process the file, we also need to save a copy of the tile map with the XML file extension. Repeat the steps above, this time typing the file name and extension as 0-0.xml.

Your image files are complete, so close Tiled and let's go back into the code.

Populate the scene layer

Your Cocos2d-html5 app should now have the following skeleton file and folder structure:


The skeleton file and folder structure for your app.

The visual assets are all in place, and you can now use them to populate the scene layer of your app with content. To do this, we modify two of the files you created earlier. First, we need to preload your new image assets in main.js, and then we can create sprites from those assets in SceneStart.js.

Preload your assets

When you created main.js, there were no assets to preload. Now that you have created some, we need to tell the Cocos2d-html5 framework which ones we want to use.

Open main.js and return to where we called the preload function with an empty array, as cc.Loader.getInstance().preload([]). Place the following assets in the array:

{type: 'tmx', src: './tmx/0-0.xml'},
{type: 'image', src: './images/0-0.png'},
{type: 'image', src: './images/tiles.png'}

Save the file.

Add a background sprite

Now that the preloading is configured, we can use our assets to populate the scene layer with content. You will make substantial changes to SceneStart.js here, so let's take a look at the finished file and then walk through each code sample you need to add.

  • View the finished SceneStart.js file

    /*global cc */
    
    var LayerStart = cc.Layer.extend({
        background: null,
        hero:       null,
        coins:      null,
    
        ctor: function () {
            var tmx, n;
    
            this._super();
    
            tmx = cc.TMXTiledMap.create('./tmx/0-0.xml');
    
            this.background = cc.Sprite.create('./images/0-0.png');
            this.background.setAnchorPoint(new cc.Point(0.0, 0.0));
            this.background.setPosition(new cc.Point(0.0, 0.0));
            this.addChild(this.background, 0);
    
            this.hero = cc.Sprite.create('./images/tiles.png', 
                        new cc.Rect(32.0, 32.0, 28.0, 28.0));
            this.hero.setAnchorPoint(new cc.Point(0.5, 0.5));
            this.hero.setPosition(new cc.Point(0.0, 0.0));
            this.hero.j = [];
            this.addChild(this.hero, 2);
    
            this.coins = tmx.getObjectGroup('coins').getObjects();
            this.coins.sprites = [];
            for (n = 0; n < this.coins.length; n = n + 1) {
                this.coins.sprites.push(cc.Sprite.create('
                    ./images/tiles.png', new cc.Rect(32.0, 64.0, 
                     32.0, 32.0)));
                this.coins.sprites[n].setPosition(
                    new cc.Point(
                        this.coins[n].x + this.coins[n].width / 2.0,
                        this.coins[n].y + this.coins[n].height / 2.0
                    )
                );
                this.addChild(this.coins.sprites[n], 3);
            }
            this.coins.sprites.count = this.coins.sprites.length;
    
            this.finish = cc.Sprite.create('./images/tiles.png', 
                          new cc.Rect(32.0, 0.0, 32.0, 32.0));
            this.finish.setPosition(new cc.Point(
                tmx.getObjectGroup('portals').getObjects()[1].x + 
                    tmx.getObjectGroup('portals').getObjects()[1].width
                    / 2.0,
                tmx.getObjectGroup('portals').getObjects()[1].y + 
                    tmx.getObjectGroup('portals').getObjects()[1]
                   .height / 2.0
            ));
            this.finish.setOpacity(0.0);
            this.addChild(this.finish, 1);
    
            return true;
        }
    });
    
    var SceneStart = cc.Scene.extend({
        onEnter: function () {
            this._super();
            this.addChild(new LayerStart());
        }
    });

First we add placeholder variables for the background, hero, and coins sprites.

     background: null,
     hero:       null,
     coins:      null,

Next, we’ll add the background sprite. To do this, create a new instance of the sprite class using the background image you created earlier, set its properties, and then add it to your layer.

Note that the path you use in the call to create must be the same path that you preloaded in main.js.

We set the anchor point for this sprite as (0, 0), which is the lower-left corner of the sprite. We then set the position of the anchor as (0, 0), which is the lower left-corner of the window. Finally, we add the sprite to your layer.

         this.background = cc.Sprite.create('./images/0-0.png');
         this.background.setAnchorPoint(new cc.Point(0.0, 0.0));
         this.background.setPosition(new cc.Point(0.0, 0.0));
         this.addChild(this.background, 0);

Add a hero sprite

We'll use the same approach to add your hero sprite as we did to add the background sprite, with a few key differences. Here, we reference tiles.png instead of 0-0.png, and because tiles.png is a sprite sheet, we need to supply x and y coordinates, along with the dimensions for the specific image we want to use. We also set the anchor point to the center of the sprite, not to its lower-left corner.

We also need to create the this.hero.j variable, to keep track of the impulse that acts on your hero.

        this.hero = cc.Sprite.create('./images/tiles.png', 
             new cc.Rect(32.0, 32.0, 28.0, 28.0));
        this.hero.setAnchorPoint(new cc.Point(0.5, 0.5));
        this.hero.setPosition(new cc.Point(0.0, 0.0));
        this.hero.j = [];
        this.addChild(this.hero, 2);

Add portals and coins sprites

We now want to add sprites for portals and coins, but the code draws their positions from the tile map you created earlier, so first you need to create a tmx variable and a counter variable, n.

        var tmx, n;

We then populate tmx by referencing the XML copy of your map.

        tmx = cc.TMXTiledMap.create('./tmx/0-0.xml');

We create sprites for the coins and the finish portal, but this time we need to accommodate any potential number of objects when we add the coins, because that number depends on how many objects you placed in the the XML file.

        this.coins = tmx.getObjectGroup('coins').getObjects();
        this.coins.sprites = [];
        for (n = 0; n < this.coins.length; n = n + 1) {
            this.coins.sprites.push(cc.Sprite.create('./images/tiles.png',
                 new cc.Rect(32.0, 64.0, 32.0, 32.0)));
            this.coins.sprites[n].setPosition(
                new cc.Point(
                    this.coins[n].x + this.coins[n].width / 2.0,
                    this.coins[n].y + this.coins[n].height / 2.0
                )
            );
            this.addChild(this.coins.sprites[n], 3);
        }
        this.coins.sprites.count = this.coins.sprites.length;

        this.finish = cc.Sprite.create('./images/tiles.png', 
             new cc.Rect(32.0, 0.0, 32.0, 32.0));
        this.finish.setPosition(new cc.Point(
            tmx.getObjectGroup('portals').getObjects()[1].x + 
                tmx.getObjectGroup('portals').getObjects()[1].width 
                / 2.0,
            tmx.getObjectGroup('portals').getObjects()[1].y + 
                tmx.getObjectGroup('portals').getObjects()[1].height 
                / 2.0
        ));
        this.finish.setOpacity(0.0);
        this.addChild(this.finish, 1);

Save the file.

Test your app in the Ripple emulator

Well done! You have set up all the visual assets for your Cocos2d-html5 app. If you return to the start page in the Ripple emulator, your app should now be rendered as a game level with sprites.


The app running in the Ripple emulator.

You can see that the screen displays your background and the yellow coins. You can't see the finish portal because we set its opacity to 0.

Note that we haven’t used the start portal yet in SceneStart.js, so your hero sprite appears in the default position (0, 0) and is only partially visible in the lower-left corner of the screen. Don't worry, you will correct this sprite's position when we come back to this file in the following section and add physics to the app.

Last modified: 2014-03-10



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus