Series: dev April 17, 2018

In my current project, I’m building tile-based levels and wanted a way to quickly build and edit them. I opted for a simple text based system. Each character is a different entity that will be loaded into the level. Each tile has a fixed width and height that I’ve defined in game.tile.width and game.tile.height In the example below, the player is X, wall tiles are 1, and lava tiles are 2. H/h, D/d, and W/w are powerups the character will pickup.

1       111                                              D             111     1111111111
1   X   111                                                              1     1111111111
1       111                                               d              1     1111111111
1       111                                111    11     11        11    1111  1111111111
1       111                     1111111                                     1  1111111111
1       111                                                                 1  1111111111
1                            111111111111                                   1  1111111111
1                         1                 1    H                          1       W   1
1    *     1      1      111               11     h                         1        w  1
11111111111111211111111111111111122111111111111111111111111111111111111111111111111111111


First, to load the file, we just use love.filesystem.lines, which allows us to iterate over a series of lines. We also set the starting y position to 0.

function build(filename)
local y=0
local lines = love.filesystem.lines(filename)
for line in love.filesystem.lines(filename) do
-- This is where we deal with each line individually.
end
end


I tend to store the level files in a levels directory. In my game, I calling the build function with build('levels/lvl1.lvl').

Next, we need to iterate over each character in the line. To do this, we just create a for loop between 1 and the length of our line using #line. At the end of the for loop, once we’ve completed our work on the line, we’ll increment the y position by 1. I’m mentioning this now, but remember, first we process each character on a line, and then we bump the y value up by one.

		for x = 1, #line do
-- Here, we'll process each character individually.
end
y = y + 1


Finally, we process each line individually. We get the character at position x on our line using line:sub(x,). Then we just use elesif statements to determine what the character we’re looking at is, and add the correct entity into the world at the correct location.

			local c = line:sub(x,x)
if c == 'X' then
game.start_position = component.position(t.x*x,t.y*y,1,1)
elseif c == 'S' then
elseif c == 'i' then
elseif c == 'c' then
elseif c == '1' then tiny.addEntity(world, entity.block(t.x*x,t.y*y))
elseif c == '2' then tiny.addEntity(world, entity.lava(t.x*x,t.y*y))
elseif c == '6' then tiny.addEntity(world, entity.wall_eye(t.x*x,t.y*y, -1))
elseif c == '7' then tiny.addEntity(world, entity.wall_eye(t.x*x,t.y*y, 1))
elseif c == '*' then tiny.addEntity(world, entity.bonfire(t.x*x,t.y*y,false))
-- Pickups
elseif c == 'd' then tiny.addEntity(world, entity.pickup(t.x*x,t.y*y,{double_jump = component.double_jump()}))
elseif c == 'h' then tiny.addEntity(world, entity.pickup(t.x*x,t.y*y,{high_jump = true}))
elseif c == 'w' then tiny.addEntity(world, entity.pickup(t.x*x,t.y*y,{wall_jump = true}))
elseif c == 'b' then tiny.addEntity(world, entity.pickup(t.x*x,t.y*y,{bomber = true}))
elseif c == 'D' then tiny.addEntity(world, entity.world_text("Double JumP",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'H' then tiny.addEntity(world, entity.world_text("High JumP (C/Square)",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'W' then tiny.addEntity(world, entity.world_text("Wall JumP",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'B' then tiny.addEntity(world, entity.world_text("Boom! (X/Circle)",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'E' then tiny.addEntity(world, entity.world_text("The end?",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'l' then tiny.addEntity(world, entity.world_text("...Left",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'k' then tiny.addEntity(world, entity.world_text("KeeP going (this area's boring...)",t.x*x,t.y*y,{255,255,255},false))
end


When we put it all together, you get something like this:

function build(filename)
local world = game.world
world:clearEntities()
local t = game.tile
game.level_file_modified = love.filesystem.getLastModified(filename)
local y=0
local lines = love.filesystem.lines(filename)
for line in love.filesystem.lines(filename) do
for x = 1, #line do
local c = line:sub(x,x)
if c == 'X' then
game.start_position = component.position(t.x*x,t.y*y,1,1)
elseif c == 'S' then
elseif c == 'i' then
elseif c == 'c' then
elseif c == '1' then tiny.addEntity(world, entity.block(t.x*x,t.y*y))
elseif c == '2' then tiny.addEntity(world, entity.lava(t.x*x,t.y*y))
elseif c == '6' then tiny.addEntity(world, entity.wall_eye(t.x*x,t.y*y, -1))
elseif c == '7' then tiny.addEntity(world, entity.wall_eye(t.x*x,t.y*y, 1))
elseif c == '*' then tiny.addEntity(world, entity.bonfire(t.x*x,t.y*y,false))
-- Pickups
elseif c == 'd' then tiny.addEntity(world, entity.pickup(t.x*x,t.y*y,{double_jump = component.double_jump()}))
elseif c == 'h' then tiny.addEntity(world, entity.pickup(t.x*x,t.y*y,{high_jump = true}))
elseif c == 'w' then tiny.addEntity(world, entity.pickup(t.x*x,t.y*y,{wall_jump = true}))
elseif c == 'b' then tiny.addEntity(world, entity.pickup(t.x*x,t.y*y,{bomber = true}))
elseif c == 'D' then tiny.addEntity(world, entity.world_text("Double JumP",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'H' then tiny.addEntity(world, entity.world_text("High JumP (C/Square)",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'W' then tiny.addEntity(world, entity.world_text("Wall JumP",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'B' then tiny.addEntity(world, entity.world_text("Boom! (X/Circle)",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'E' then tiny.addEntity(world, entity.world_text("The end?",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'l' then tiny.addEntity(world, entity.world_text("...Left",t.x*x,t.y*y,{255,255,255},false))
elseif c == 'k' then tiny.addEntity(world, entity.world_text("KeeP going (this area's boring...)",t.x*x,t.y*y,{255,255,255},false))
end
end
y = y + 1

	if game.level_file_modified < love.filesystem.getLastModified(level_file) then