Loading
Lesson 02
Courses / Build a Game with SDL - Make a Labyrinth with C++ from Scratch - Beginner Tutorial
Loading the Game Map from a File and Painting it on the Window Surface – SDL Tutorial

The goal of this lesson is to draw a game map with tiles that are loaded from a text file.

The screen can be segmented into small squares of length 32 pixels. If the width of the screen is 640px, then there will be 640px ÷ 32px = 20 tiles across. If the height of the screen is 480px, then there will be 480px ÷ 32px = 15 tiles down.

The game map can be stored in a text file using the following simple format: each line represents a row of tiles in the screen. To identify the type of tile for each cell, we use an integer number. The number for each cell is separated from the other by a space.

0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0
0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Each integer determines what kind of tile to be drawn. The number 0 represents the floor tile; the player can walk over it. The number 1 represents the wall tile; the player should not be able to walk on it.

As an example, consider this piece of the map, with 3 tiles across and 3 tiles down. The rendered game map image follows the data file:

0 1 1
0 0 0
1 0 0

After establishing the format for the game map, we create thefile GameMap.txt to store the tile identifiers.

In order for the program to draw the map, it needs to know what to draw. So we have to open the map file and read its contents.

We create the function loadGameMap to do that. It uses the fstream header to allow us to use the ifstream class. You can instantiate an object using the constructor, passing the name of the file as argument. After that, you check if the file is open, then proceed to iterate over the contents using the >> operator inside a loop:

while (gameMapFile >> tileId)

The variable on the left-hand side of the >> operator is the object bound to the file; the variable on the right holds the value of the next integer number read.

We store that value in an array of integers of size 20 × 15 = 300 elements. Each of those elements stores the id of the tile, so we know what kind to draw later, be it floor or wall.

After going through the data file, you should make sure to close it.

Once we load the identifiers for each tile into memory, we need to create a surface to paint them. We write the createGameMapSurface function to do that. It goes through each element in the array of tile identifiers and figures out where to paint each little square.

You can draw the tiles in many different ways. The lesson does it top to bottom, left to right. That is, it starts with the first row, then paints a tile in each column there. Next, it goes to the second row, then paints the tiles left to right. Repeat until there are no more rows to paint.

The function SDL_FillRect needs a pointer to a SDL_Rect to know what area of the surface to paint. We set the x and y members of the structure using the column and row position of the tile, respectively.

For a given row (of fixed y coordinate), the tiles are laid out along the x axis. So to get the coordinate for the tile at column index j, you multiply it by the width of the tile, 32 pixels:

rectangle.x = j * 32

To figure out the x coordinate, you do something similar, but involving the row index i, multiplying it by the height of the tile:

rectangle.y = i * 32

Note both i and j are counted starting from zero, not one. That is why I called them indices.

Once you have those two coordinates, you know exactly where to paint on the surface. But you do not yet know what kind of tile to paint. To figure that out, you look into the array of tile identifiers.

The lesson used a one-dimensional array to store the two-dimensional map. Because of that, we need a formula for the index of the element.

A two-dimensional array could have been used in this case. It could make development easier and there would not be a need for a formula here.

In any case, you can figure out the formula with this simple grid of 3x3:

To get the element that is in the second row, second column, you consider:

For the column, we can get there if we add the column index j to the tile index: tiles[j]. The second column has index 1, so we have tiles[1].

That would give us the second column for the first row. To get to the second row, we need to add the total number of tiles for a single row. In the 3x3 example, it is 3. So we have 1 + 3 = 21, making the retrieval statement be tiles[4].

In general, the formula is

i * TILES_PER_ROW + j

For the case where the screen has a width of 640px, the tiles per row would be 20, if each tile has a width of 32px. The formula now being:

i * 20 + j

After getting the identifier for the tile, use a conditional such as an if statement to define the parameters for the tile that will be drawn. If the tile id is 0, make it have black color. If the tile id is 1, make it have white color.

We do it in a very simple way, but in practice you substitute the color statement for whatever configuration is necessary to render that tile. For example, the tile could be an image rendered out of a tile sheet and the statement would pick the correct clip from that tile sheet.

Did you like the lesson? 😆👍
Consider a donation to support our work: