In this tutorial, we are going to load an isometric tilemap from a file and render it on the screen. We will be looking at several techniques commonly applied in 2D games.
The code for loading map files is really short. It scans the input file one character at a time while tracking each character's position in map coordinates. The x position increases by one for each consecutive character and the y position increases for each new line in the map file. We convert each scanned character to its ANSI code by calling the "string.byte" function. Finally, we subtract 32 from the ANSI code and we get a number representing the particular tile at that position.
-- load and parse the map file local file = io.open ( "Tutorials/isomap.txt" ) local filesz = file:read ( "*a" ) file:close ( ) -- track the position in our map local x, y = 0, 0 -- scan the file string local len = string.len ( filesz ) for i = 1, len do local b = string.byte ( filesz, i ) local c = string.char ( b ) if c == '\n' then -- new line x = 0 y = y + 1 else -- convert ANSI character to tile number local tile = b - 32 -- create new tile here x = x + 1 end end
-- diamond ordering local x = (tx + ty)*(tile_width/2) local y = -(ty - tx)*(tile_height/2)Staggered ordering is preferred in strategy games (like Civilization 2 & 3) where the map is usually wrapped horizontally. It should be noted that staggered maps add some additional complications in particular with finding the index of neighboring tiles. Here is how tiles are placed on the screen with staggered ordering:
-- staggered ordering local x = tx*tile_width + ty%2*(tile_width/2) local y = -ty*(tile_height/2)Notice that in both examples the y tile index is negated. This is because in AGen sprites with a greater y-value are drawn higher vertically in the scene.
-- diamond ordering local ty = my - mx/2 - tile_height local tx = mx + ty local y = math.ceil(-ty/tile_width) local x = math.ceil(tx/tile_width) + 1
-- staggered ordering local ty = my - mx/2 - tile_height local tx = mx + ty ty = math.ceil(-ty/(tile_width/2)) tx = math.ceil(tx/(tile_width/2)) + 1 local x = math.floor((tx + ty)/2) local y = ty - txSome modifications may be necessary to both examples after you paste them in your code. Firstly, mx and my are assumed to be coordinates on the map (not the screen). Another thing to note is that both scripts assume that the first tile (1, 1) is on the bottom left for staggered ordering or the left corner of the diamond.
local sprite = Sprite ( ( x + y ) * 32, ( y - x ) * -16 ) sprite.depth = sprite.y -- convert tile number to a sub-section of the tileset local cols = tileset.width / 128 local x = tile % cols * 128 local y = math.floor ( tile / cols ) * 128 sprite.canvas:clear ( ) sprite.canvas:set_source_subimage ( tileset, x, y, 128, 128 ) sprite.canvas:paint ( )
Notice that when positioning tiles on the screen we are multiplying each tile's column (x) and row (y) number by 32 and -16. Although the tiles are 128 pixels in size, they contain quite a bit of padding. Also, note that the y-position value has to be negated since AGen uses a projection system where the y-axis increases upward.