Link to exercise files

Like you saw from chapter “Map format”, we will have our map in two-dimensional array. Now we will make the tiles appear on the screen, place them in correct position and make them show correct frame. Movie will look like this:

Getting the stage ready

In order to get Starling up and running, you have to link to the Starling framework, and set op a Starling object. If you cant remember how to do that, please do one of the following:

  1. Take a look at http://blog.hjaelpmignu.dk/2012/03/getting-hooked-up-with-starling/
  2. Just download the workfiles and import “Map 1 Begin”
For easy access, I will just enter the starter code her (to put in the default package):

Main.as

package
{
  import flash.display.Sprite;
  import starling.core.Starling;

  [SWF(frameRate="60", width="448", height="320", backgroundColor="0xFFFFFF")]

  public class Main extends Sprite
  {
    private var starlingStage:Starling;

    public function Main()
    {
      starlingStage = new Starling(GameStage, stage);
      starlingStage.start();
    }
  }
}

GameStage.as

package
{
  import starling.display.Sprite;
  import starling.events.Event;

  public class GameStage extends Sprite
  {
    public function GameStage()
    {
      addEventListener(Event.ADDED_TO_STAGE, onAdded);
    }
    private function onAdded(event:Event):void
    {
      //Stage is ready
    }
  }
}

Creating the Tile class

Showing the map is done by two classes. One that takes care of the map, and one that represents the individual tile. The game itself, will the bring a layout to the map, and a movie clip, containing the individual tiles.

Making the movie clip is done via a sprite sheet. A sprite sheet is a large image, containing all graphic in the game, or individual character. Along with the image, is a data file (XML or JSON) that tells, what position in the large image, the individual parts is located.

Create a Sprite class called “Tile” that extends starling.display.Sprite, and place it next to the other files (default package). Enter the following in Tile.as:

package
{
  import flash.display.Bitmap;

  import starling.display.MovieClip;
  import starling.display.Sprite;
  import starling.events.Event;
  import starling.textures.Texture;
  import starling.textures.TextureAtlas;

  public class Tile extends Sprite
  {

    [Embed(source="assets/tiles.xml", mimeType="application/octet-stream")]
    public const SpriteSheetXML:Class;

    [Embed(source="assets/tiles.png", mimeType="image/png")]
    public const TileSheet:Class;

    private var tileMovie:MovieClip;
    private var frame:uint;

    public function Tile(frame:uint=0)
    {
      this.frame = frame;
      addEventListener(Event.ADDED_TO_STAGE, onAdded);
    }

    private function onAdded(event:Event):void
    {
      tileMovie = createClip();
      addChild(tileMovie);
      tileMovie.currentFrame = this.frame;
    }

    private function createClip():MovieClip
    {
      var bitmap:Bitmap = new TileSheet();
      var texture:Texture = Texture.fromBitmap(bitmap);
      var xml:XML = XML(new SpriteSheetXML());
      var sTextureAtlas:TextureAtlas = new TextureAtlas(texture, xml);
      var frames:Vector.<Texture> = sTextureAtlas.getTextures("tile_");

      return new MovieClip(frames);
    }
  }
}

Lets take it one at the time:

[Embed(source="assets/tiles.xml", mimeType="application/octet-stream")]
public const SpriteSheetXML:Class;

[Embed(source="assets/tiles.png", mimeType="image/png")]
public const TileSheet:Class;
private var tileMovie:MovieClip;
private var frame:uint;

First you use the embed meta-tag to embed the tile image, and tile data-file. I used Texturepacker. It has a free basic version, there is sufficient for our purpose. I have placed them in a subfolder called “assets” inside the source folder. Feel free to make your own tiles in 32 x 32 pixels, or use the one found in the working files.  Theses embeds are captured in constants with the name SpreteSheetXML and TileSheet.

Then a movie clip it declared, to hold the tiles in frames and finally, a private variable called frame is declared. This variable is supposed to contain the current frame to show in this tile.

public function Tile(frame:uint=0)
{
  this.frame = frame;
  addEventListener(Event.ADDED_TO_STAGE, onAdded);
}

private function onAdded(event:Event):void
{
  tileMovie = createClip();
  addChild(tileMovie);
  tileMovie.currentFrame = this.frame;
}

The constructor is taking one argument, that is the frame to show. It is optional, and will show the first frame as default, but it is often convenient to pass the frame along, when creating the tile. Besides, passing the argument to the local property, it is also listening for the ADDED_TO_STAGE event, so it can start flashing it’s tiles.

In the onAdded() method a movie clip is instantiated, by calling the createClip() method, that you will define in a moment. The movie clip is placed in the Tiles display list with addChild(), and the movie clips current frame is set to the position given in the constructor.

Movie Clip inside a sprite

I have placed a movie clip inside a sprite here. This is because a Starling movie clip needs a set  of texures when created. You may be able to get around that, and create the afterwards, but I find this nesting easer to manage.
private function createClip():MovieClip
{
  var bitmap:Bitmap = new TileSheet();
  var texture:Texture = Texture.fromBitmap(bitmap);
  var xml:XML = XML(new SpriteSheetXML());
  var sTextureAtlas:TextureAtlas = new TextureAtlas(texture, xml);
  var frames:Vector.<Texture> = sTextureAtlas.getTextures("tile_");

  return new MovieClip(frames);
}

This is the tricky one. The proces when creating a movie clip with Starling is:

  1. Create a texture
  2. Convert that texture into an atlas via the provided data file, mapping each frames position and size in the large image
  3. Store the information in an vector containing individual textures
  4. Provide that vector object to the movie clip during construction.
The previous method (createClip) returns exactly that type of clip. First a bitmap i created from the embedded tile sheet. This bitmap is used to create the texture. You could save yourself a line, by just entering something like:
var texture:Texture = Texture.fromBitmap(new TileSheet());

but I tend to like it a bit more verbose, when looking over my code, later on. The same way, the atlas created in the next two lines could be written like this:

var sTextureAtlas:TextureAtlas = new TextureAtlas(texture, XML(new SpriteSheetXML()));

but again, making the xml object first, and pass it to TextureAtlas() is a bit more readable, in my opinion.

A texture vector is then created via the getTextures() method. If you have several tile sets in the same sprite sheet – good guy, bad guy and backgrounds –  you can enter an optional prefix, that distinguish the tiles you are about to retrieve from other in the sheet.

Finally the method returns a shiny new movie clip, based on the textures.

See, if it works

That is almost too good to be true, right? Give it a go. Open up GameStage.as, and enter the following in the onAdded() method:

var myTile:Tile = new Tile(0);
addChild(myTile);

Using my sprite sheet, it shows a little grey tile in the top left corner. Give it a go, and enter new Tile(1) instead, to see the other til in the sheet. This way, you can actually create all the tiles, just by entering the tile number. Sadly this sheet has only two tiles – so much for the fun in that – but think big here, you could have 60 tiles in the clip … this little Tile object, is about to become a powerful ally in the quest for creating tile based games.

On to the Map

With the tile object in place, it is time to create a complete map. The way you are going to do it is with a two-dimensional array containing the frame numbers to be shown in each map position. Open GameState.as and enter the following:

package
{
  import starling.display.Sprite;
  import starling.events.Event;

  public class GameStage extends Sprite
  {
    private var level1:Array;
    private var map:Map;

    public function GameStage()
    {
      level1 = [
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
        [1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1],
        [1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1],
        [1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1],
        [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
      ];
      addEventListener(Event.ADDED_TO_STAGE, onAdded);
    }

    private function onAdded(event:Event):void
    {
      map = new Map();
      map.buildMap(level1, new Tile());
      addChild(map);
    }
  }
}

Two variables are created called level1 and map. Level1 is an array containing each row of information about frame number to show in the tile placed at that position in the grid … you can almost sense the grid and layout in the array. The map is a new object, that you will create in a moment. After creation, you call its method buildMap, that takes the layout of level 1, and the tile clip that the numbers refer to. Finally the map is placed in the Starling display list. That shouldn’t cause to much trouble, so let’s look at the last piece of the puzzle – the Map object.

Create a Sprite class called “Map” that extends starling.display.Sprite, and place it next to the other files (default package). Enter the following in Map.as:

package
{
  import starling.display.Sprite;

  public class Map extends Sprite
  {
    private var tiles:Tile;
    private var layout:Array;
    private var tileW:int;
    private var tileH:int;

    public function Map()
    {
      tileW = 32;
      tileH = 32;
    }

    public function buildMap(layout:Array, tiles:Tile):void
    {
      this.tiles = tiles;
      this.layout = layout;
      drawMap();
    }

    private function drawMap():void
    {
      var mapWidth:uint = this.layout[0].length;
      var mapHeight:uint = this.layout.length;
      var tmpTile:Tile;
      for (var i:uint = 0; i<mapHeight; ++i)
      {
        for (var j:uint = 0; j<mapWidth; ++j)
        {
          tmpTile = new Tile(layout[i][j]);
          tmpTile.x = j*tileW;
          tmpTile.y = i*tileH;
          addChild(tmpTile);
        }
      }
    }
  }
}

The first part isn’t that interesting in this context. The information about the tiles are stored in local variables, and a few variables is declared to hold information about width and height of the tiles. This is just hacked, in and improved on later on, but change 32 to whatever size the tiles you created are … remember to update the documents dimensions as well ;-)

The interesting here is drawmap() which get called from the buildmap() method. Let’s bite it over in two chunks:

var mapWidth:uint = this.layout[0].length;
var mapHeight:uint = this.layout.length;
var tmpTile:Tile;

The width is found by looking in the first row of the layout array, and count the elements in them. It looks like this:

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

so mapWidth will hold the value 14. In the same way mapHeight is storing the number of elements (rows) in the array which, in this example is 10. Then a temporary tile is created for use in the next part of the code.

for (var i:uint = 0; i<mapHeight; ++i)
{
  for (var j:uint = 0; j<mapWidth; ++j)
  {
    tmpTile = new Tile(layout[i][j]);
    tmpTile.x = j*tileW;
    tmpTile.y = i*tileH;
    addChild(tmpTile);
  }
}

Two for-loops are nested inside each other. The outer loop is iterating through all the rows, one at the time. Every time i comes to a row, it starts the inner loop, that iterates through all the cells in the current row. When there is no more cells in a given row, the outer loop continues to the next row, and so forth.

So there you have it. The variable i is holding the row, and j is holding the column (or cell if you like). When the inner loop is running. It first creates a new tile, and pass the frame ID to it. The ID is found, by looking at row number “i” and the cell number “j”. Then it’s positioned by multiplying the row number, or column number width the width of the tile. If you are looking at tile[3][5] it would be positioned at x = 160 (5 x 32) and y = 96 (3 x 32).

At last it is placed into the display list of the map.

Now, you try?

Save all the files, and test. You should see a map, that reflects the layout in the array. Now you can try and alter the array to a different layout, or draw a few tiles yourself, and pack them with texturepacker or similar. You could also change the width and height of the map, if you enter more numbers in the two-dimensional array. Remember, that it looks best, if all the rows, have the same amount of cells :-)

Link to original

This tutorial is inspired by Tony Pas work. The original version of this page can be found at http://www.tonypa.pri.ee/tbw/tut02.html 

Leave a Comment