PVG
2. Implementing a Basic Tiled Level — Program Video Games

2. Implementing a Basic Tiled Level

[[programvideogames]]Let's utilise the Fractal Code Cycle to figure out how to solve this problem.

The result we want: tiles on the screen in the shape of a platformer level.

What is the simplest way to represent this output?

Probably something like an array of rl.Rectangle that we then draw.

Thinking about potential inputs, we may consider something like a JSON representation of level data as a tool like LDtk or Tiled produces.

Can we simplify?

Let's see what relevant inputs we have available right now:

  1. Rendering dimensions: 640x360 pixels
  2. Tile size 16x16 pixels

Dividing these, we get 40 columns and 22.5 rows.

For a simple level, we don't need to worry about different types of tiles, just whether a tile exists or not at a position...

You know what tool has columns and rows? Our code editor!

We can create a simple level using just some text!

data/simple_level.dat

########################################
#                                      #
#                                      #
#                                      #
#                                      #
#                                      #
#                                      #
#                                      #
#                                      #
#                                      #
#                                      #
#                                      #
#                                      #
#     ##########                       #
#                                      #
#                                      #
#                      ########        #
#                                      #
#                                      #
#                                ###   #
#                                ###   #
########################################
########################################

Great, we have our full set of inputs and our desired output.

Time to do the processing.

// in main, first we declare our array
// Dynamic arrays are created by default using context.allocator
// We can add any number of items to this array and it will expand
solid_tiles: [dynamic]rl.Rectangle

// I like to use a separate block for initialisation code
// It keeps variables scoped to just that block and allows
// name reuse without worrying about collisions
{
    data, ok := os.read_entire_file("data/simple_level.dat")
    assert(ok, "Failed to load level data")
    x, y: f32
    for v in level_data {
        // reached end of the row
        if v == '
' {
            y += TILE_SIZE
            x = 0
            continue
        }
        if v == '#' {
            append(&solid_tiles, rl.Rectangle{x, y, TILE_SIZE, TILE_SIZE})
        }
        x += TILE_SIZE
    }
}

The output of this code is the array we are looking for.

Let's zoom out to our rendering cycle.

We want to simply take the array as input and the output should be drawing squares to the screen.

// our drawing code:
for rect in solid_tiles {
    rl.DrawRectangleRect(rect, rl.WHITE)
    // make the tiles visible by outlining them
    rl.DrawRectangleLinesEx(rect, 1, rl.GRAY)
}