Creating an efficient background

While objects and characters will typically be scattered around a level, covering only a small fraction of its total area, the ground generally takes up the whole area of the level. This isn't a problem on small levels that are mostly or completely contained within the screen, but in large, scrolling levels, the number of objects off-screen can begin to have a significant effect on performance.

Getting on with it

To display the background terrain of a level, first we're going to need to convert the one-character tiles from the level map file into sequences appropriate for the terrain type as the level loads. The core of the conversion will be handled by the terrain type itself.

Loading background data from a level file

Save any open files and reopen map.lua. When each line is tested to see if it is a terrain specifier, add code to save that choice with the map:

    local terrain = map_line:match('^(.+):$')
    if terrain then
      self.Terrain = require("terrain."..terrain)
    else

However, if a terrain specifier tries to replace another one, or if terrain content is loaded before any specifier is found, an error is thrown:

    if terrain then
      assert(not self.Terrain, "Error in map "..filename..": multiple terrain types specified")
      self.Terrain = require("terrain."..terrain)
    else
      assert(self.Terrain, "Error in map "..filename..": No terrain type specified for map content")
      local row = {}

Note

Obviously, you don't want your game to ship with faulty maps in it. But one of the reasons for supporting a file format like this, as in Project 5, Atmosfall – Managing Game Progress with Coroutines, is to facilitate other designers or scripters working on the game who may not be full-time programmers, and who will usually appreciate error messages that tell them why their file is broken. For that matter, you may make a mistake yourself, and be glad for the clue in tracking it down.

In the body of the loop that expands each line of terrain data, expand the fields it stores with each space:

        local space = {
          map = self;
          x = #row + 1,
          y = #self;
          Features = {};
        }

For each character in the file, we will also request that the terrain expand it to the appropriate animation sequence. So, if maps typically use the # character to represent a wall or obstacle, a desert terrain might expand it to rock while a forest terrain might expand it to bush.

          y = #self;
          Ground = self.Terrain:Expand(tile);
          Features = {};

Finally, after assembling the individual tiles, we ask the terrain type to clean up the map. For instance, it might adjust border tiles so that they use the correct edge or corner based on which side of a pool or hole or other feature they are located on.

  end
  if self.Terrain then
    self.Terrain:Polish(self)
  end
  local family_group
  for object in objects() do

Save map.lua.

Assembling the tile field

Open world.lua. At the beginning of the module function, before creating the features layer, add a new layer that will contain the terrain tiles. Since every tile in this layer will be a sprite from the same sheet, we can improve performance by using Corona's newImageGroup function:

  local self = display.newGroup()
  self.Ground = display.newImageGroup(terrain.Atlas)
  self:insert(self.Ground)
  self.Features = display.newGroup()

Request the size of a single tile from the terrain type, so that tiles can be fit together properly:

  self:insert(self.Ground)
  self.HSize, self.VSize = terrain:TileDimensions()
  self.Features = display.newGroup()

Create an array (which will eventually be two-dimensional) to store the tiles we create:

  self.HSize, self.VSize = terrain:TileDimensions()
  self.Tiles = {}
  self.Features = display.newGroup()

Next, add tiles to the layer to cover the screen. Note that we add an extra layer of tiles around the bottom and right. This means that we don't have gaps showing during the times the background is being aligned with the screen as it moves, in the next section.

  self.Tiles = {}
  for v = 1, rows + 1 do
    local row = {}
    self.Tiles[v] = row
    for h = 1, columns + 1 do
      local tile = terrain:Tile()
      self.Ground:insert(tile)
      tile:setReferencePoint(display.BottomRightReferencePoint)
      tile.x, tile.y = h * self.HSize, v * self.VSize
      row[h] = tile
    end
  end
  self.Features = display.newGroup() 

What did we do?

We've created a layer of terrain segments large enough to cover the screen. These segments can be configured to display any section of the background that fits within the confines of the screen.

What else do I need to know?

Performance concerns aren't quite as large as they used to be in earlier versions of Corona. Around the same time that image sheets and image groups were introduced, Corona instituted a feature called offscreen culling , wherein it doesn't spend time drawing objects that are not currently visible. However, while this makes the processing time for offscreen tiles very small, it doesn't reduce it to zero, so a performance impact might still be noticeable for very large worlds if you generated enough tiles to hold the whole thing.

Moreover, loading times are still a challenge for mobile games, since phones and tablets have comparatively limited memory. Using this window onto the terrain means that noticeably less time needs to be used to construct the world object during this easily overloaded program phase.

Regarding the terrain:Polish() function, it constitutes a large portion of the work done to make a level look good. However, it's also not very interesting; it consists mostly of a long chain of if...then...else...if statements. For this reason it's not being discussed here in detail; you can review the individual biome files in terrain.lua if you're curious about what it does.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.108.236