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.
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.
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 = {}
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
.
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()
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.
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.
3.12.108.236