engo


Tutorial 5
Tilemaps

In this tutorial, we will use a tmx file to render our background tiles. We’re also going to set our CameraBounds to the size of the map so we can pan the whole map.

Recap

Remember what we did in the last tutorial?
We added a solid colored box to the bottom left of the screen, and set the shader so that it acted as an HUD element that doesn’t move as the camera pans.

Final Code

The final code for tutorial 5 is available here at GitHub.

Why Use Tilemaps?

Tilemaps are essentially just files that tell the game where the sprites are located on the disk and where to place them on the screen. A useful tool for building tilemaps is the Tiled Map Editor, which produces .tmx files. The common library has the ability to read and load .tmx files, and placement is as easy as reading it and looping through all the tiles.

The .tmx files

The .tmx file used for this tutorial, as well as the accompanying images can be downloaded here. Extract it into your game’s assets folder.

Loading the .tmx Resource

First we’re going to load the tile map during our scene’s Preload

Note: engo.Files.Load() can take any number of strings as an argument, so rather than calling it multiple times, we can just append the new file to the call we’re already making

engo.Files.Load("textures/city.png", "tilemap/TrafficMap.tmx")

Then we’ll retrieve it in the scene’s Setup. We have to cast it as a common.TMXResource so we can access the fields inside.

resource, err := engo.Files.Resource("tilemap/TrafficMap.tmx")
if err != nil {
  panic(err)
}
tmxResource := resource.(common.TMXResource)
levelData := tmxResource.Level

Unpacking the Tilemap into Entities

Now that we have the level data, we need to unpack it into entities we can use for our RenderSystem. To do this we’ll make a struct to contain the data required

type Tile struct {
  ecs.BasicEntity
  common.RenderComponent
  common.SpaceComponent
}

Then, after you’ve unpacked the level data you’ll want to loop through it and fill the tiles.

Note: Our .tmx file only has TileLayers, however you can also loop through ImageLayers, etc. if you need to in the same way.

tiles := make([]*Tile, 0)
for _, tileLayer := range levelData.TileLayers {
  for _, tileElement := range tileLayer.Tiles {
    if tileElement.Image != nil {
      tile := &Tile{BasicEntity: ecs.NewBasic()}
      tile.RenderComponent = common.RenderComponent{
        Drawable: tileElement.Image,
        Scale:    engo.Point{1, 1},
      }
      tile.SpaceComponent = common.SpaceComponent{
        Position: tileElement.Point,
        Width:    0,
        Height:   0,
      }
      tiles = append(tiles, tile)
    }
  }
}
// add the tiles to the RenderSystem
for _, system := range world.Systems() {
  switch sys := system.(type) {
  case *common.RenderSystem:
    for _, v := range tiles {
      sys.Add(&v.BasicEntity, &v.RenderComponent, &v.SpaceComponent)
    }
  }
}

Now, if you run the game the tiles for your map should render to the screen.

Setting the CameraBounds to the Tilemap Bounds

All that’s left is to set your camera’s bounds to the tilemap. We can do that with levelData.Bounds(), which gives the boundaries of your tilemap.

common.CameraBounds = levelData.Bounds()

Congratulations! You’ve added a tilemap to your game! Next time, we’re going to learn how to use Spritesheets with engo, as well as update our citybuilding System so that cities add automatically!