How to load level files in SDL2 from Tiled map editor


#21

The textfile should have numbers which are the IDs of the tile you want to draw.

You load in your textfile which looks like this
0 1
2 0

Your array will look like this after you load the textfile values
int myLevel[2][2] = [ [0,1], [2,0] ]

When you want to know what the ID of the tile you want, you query the array.
Example
array[0][0] = 0;
array[0][1] = 1;
array[1][0] = 2;
array[1][1] = 0;

Using the TileID we can figure out the source rectangle “A piece of the full texture you want to draw”


#22

did you mean int myLevel[2][2] = { {0, 1}, {2, 0} } ;

I sorta get what you mean. So this example is right?

int myLevel[2][2] = { {0, 1}, {1, 1} };
void draw() {
for(int i = 0; i < 2; ++i) {
for(int j = 0; j < 2; ++i) {
if (myLevel[i][j] == 0) {
// SDL_RenderCopy with the texture for ID 0
} else if (myLevel[i][j] == 1) {
// SDL_RenderCopy with the texture for ID 1
}
}
}
}


#23

Your example is correct.

One thing though: like I wrote a few posts back - you should only use 1 texture containing all the tiles (often called a tile sheet, tile atlas or texture atlas) and use that texture whenever the different tiles should be rendered.
By using a so-called clip rectangle, you can choose what part of a texture you want to render, which means that you can clip the texture based on the information gathered from the level data. You should generate the clip data when generating the level, so you don’t have to worry about that during the rendering part.

I’ve included some code below, showing how to parse a level file and generate tile data based on that file.
Below the code I’ve also included a level file example, which shows what type of file the parser can handle.
Click on the texts below to see the code and the level file example.

If you decide to use all- or part of the code example (which you’re free to do), don’t forget to delete the tile data at game shutdown, because of potential memory leak.

If you have any more questions, just ask.

Level generation
// Data for one tile
struct STile
{
	unsigned int XPosition;
	unsigned int YPosition;
	unsigned int Width;
	unsigned int Height;
	unsigned int XTexcoord;
	unsigned int YTexcoord;
};

typedef std::vector<STile*> LevelData;

// The level data
LevelData m_LevelData;

SDL_Texture* m_pTilesetTexture;

bool GenerateLevel(const std::string& rLevelFileName)
{
	std::ifstream LevelFile;

	LevelFile.open(rLevelFileName.c_str(), std::ifstream::in);

	if(!LevelFile.is_open() || !LevelFile.good())
	{
		printf("Error: failed to load level file '%s'", rLevelFileName.c_str());

		return false;
	}

	unsigned int	LevelWidth	= 0;
	unsigned int	LevelHeight	= 0;
	unsigned int	TileWidth	= 0;
	unsigned int	TileHeight	= 0;
	unsigned int	CurrentTile	= 0;
	std::string	Temp		= "";
	bool		EndOfFile	= false;

	LevelFile >> LevelWidth;	
	LevelFile >> LevelHeight;

	getline(LevelFile, Temp);

	LevelFile >> TileWidth;	
	LevelFile >> TileHeight;	

	getline(LevelFile, Temp);

	for(unsigned int i = 0; i < LevelHeight; ++i)
	{
		for(unsigned int j = 0; j < LevelWidth; ++j)
		{
			if(LevelFile.eof())
			{
				EndOfFile = true;

				break;
			}

			LevelFile >> CurrentTile;

			// Empty tile, no need to create a tile
			if(CurrentTile == 0)
				continue;

			STile* pTile = new STile;

			pTile->XPosition	= j * TileWidth;
			pTile->YPosition	= i * TileHeight;
			pTile->Width		= TileWidth;
			pTile->Height		= TileHeight;
			pTile->XTexcoord	= TileWidth * (CurrentTile - 1);
			pTile->YTexcoord	= 0;

			m_LevelData.push_back(pTile);
		}

		if(EndOfFile)
			break;
	}

	LevelFile.close();

	return true;
}

bool Create(SDL_Renderer* pRenderer, const std::string& rTextureFileName, const std::string& rLevelFileName)
{
	// Load the image into an SDL_Texture
	m_pTilesetTexture = CreateTexture(pRenderer, rTextureFileName);

	if(!m_pTilesetTexture)
		return false;

	if(!GenerateLevel(rLevelFileName))
		return false;

	return true;
}

void Render(SDL_Renderer* pRenderer)
{
	LevelData::const_iterator ItBeg = m_LevelData.begin();
	LevelData::const_iterator ItEnd = m_LevelData.end();

	for(; ItBeg != ItEnd; ++ItBeg)
	{
		STile* pTile = (*ItBeg);

		// Create the clip- and destination rectangle based on the data in the tile
		const SDL_Rect ClipRect = {pTile->XTexcoord, pTile->YTexcoord, pTile->Width, pTile->Height};
		const SDL_Rect DestRect = {pTile->XPosition, pTile->YPosition, pTile->Width, pTile->Height};

		// Render the tile, clipped from the tileset texture
		SDL_RenderCopy(pRenderer, m_pTilesetTexture, ClipRect, DestRect);
	}
}
Level file example
8 4	// Number of horizontal and vertical tiles
32 32	// Tile size

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

#24

Here is another example,
Create a texture / .bmp file that contains 2x tiles that are 32 * 32 pixels each
Your .bmp file dimensions should be 64 X 32

#include <SDL2/SDL.h>

static SDL_Texture* loadTexture(SDL_Renderer *renderer, const char* filePath) {
    SDL_Surface* surface(SDL_LoadBMP(filePath));
    if (surface) {
        SDL_Texture* texture(SDL_CreateTextureFromSurface(renderer, surface));
        if (texture) {
            printf("Texture loaded successfully: %s\n", filePath);
            return texture;
        }
        printf("Failed to create texture: %s\n", filePath);
        return nullptr;
    }
    printf("Cannot find texture file: %s\n", filePath);
    return nullptr;
}


int main(int argc, char* args[] ) {

    bool quit = false;

    const int32_t kRow = 5;
    const int32_t kCol = 10;
    const int32_t tileSize = 32;
    SDL_Texture* texture = nullptr;

    int32_t level[kRow][kCol] = {
            {1,0,1,0,1,0,1,0,1,0},
            {0,1,0,1,0,1,0,1,0,1},
            {1,0,1,0,1,0,1,0,1,0},
            {0,1,0,1,0,1,0,1,0,1},
            {1,0,1,0,1,0,1,0,1,0}
    };

    SDL_Init( SDL_INIT_EVERYTHING );
    SDL_Window* window = SDL_CreateWindow( "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 320, 160, 0 );
    SDL_Renderer* renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED );

    // You may need to change the filepath to point to where your texture is located
    texture = loadTexture(renderer, "../tex.bmp");

    SDL_Event e;
    while (!quit) {
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
        }

        SDL_RenderClear(renderer);

        for (int32_t r = 0; r < kRow; ++r) {
            for (int32_t c = 0; c < kCol; ++c) {
                // Gets the tile position
                const SDL_Rect dstRect = {c * tileSize, r * tileSize, tileSize, tileSize};
                // Gets tileID from the texture we need to draw
                const SDL_Rect srcRect = {level[r][c] * tileSize, 0, tileSize, tileSize};
                SDL_RenderCopy(renderer, texture, &srcRect, &dstRect);
            }
        }

        SDL_RenderPresent(renderer);
    }

    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

#25

So how would I implement that in C? that’s way different,
Also i’m making a platformer game:

sticking to your example it would be: 17 6 as the Number of horizontal and vertical tiles
32 32 obviously sticking to the Tile size

00000000000000000
00000000000000000
00000000000000000
00000000000000000
11111111111111111111
11111111111111111111


#26

I understand that but i’m using PNGs so i’d use IMG_Load from the <SDL2/SDL_image.h> header.

Uhhmm for the loadTexture could i use a txt file so that it could read data off of the text file through 0s and 1s to make the level like what Naith did?


#27

No its only for loading .bmp images.
Create a .png with with your texture loader and run the program to get an understanding of whats happening.

You need to take it one step at a time,
#1 Understand 2d array and how they work
#2 Understand how I can draw tiles from one texture and a 2d array
#3 Fill my 2d array with numbers loaded from a texture.


#28

that’s the same as saying unsigned int as well?


#29

I understand #1 but the rest I may not have understood or understand but uhm, I should be good. i get what you mean, but i’m using a spritesheet so wouldn’t I have to refer to it??

The sprite sheet of the tiles so i guess you could call it a TileSheet.


#30

This figures out the tile in the sprite sheet for you.

 // Gets tileID from the texture we need to draw
const SDL_Rect srcRect = {level[r][c] * tileSize, 0, tileSize, tileSize};

This is your texture with 4 tiles of size 32 * 32
[ . ][ . ][ . ][ . ]
  ^
If level[r][c] = 0 then (0 * 32  = 0)

[ . ][ . ][ . ][ . ]
       ^
If level[r][c] = 1 then (1 * 32  = 32)

#31

Oh wow that’s really useful! thanks a bunch!!
I still have a few more questions though, i made this file called level.txt

it contains binary
00000000000000000
00000000000000000
00000000000000000
11111111111111111111

(this is like a part from the file) how would I get the program to read off of that data and input the tiles I want?


#32

Create a textfile called level.txt and it should look like
0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1
1 0 1 0 1 0 1 0 1 0
0 1 0 1 0 1 0 1 0 1

#include <SDL2/SDL.h>
#include <fstream>

static SDL_Texture* loadTexture(SDL_Renderer *renderer, const char* filePath) {
    SDL_Surface* surface(SDL_LoadBMP(filePath));
    if (surface) {
        SDL_Texture* texture(SDL_CreateTextureFromSurface(renderer, surface));
        if (texture) {
            printf("Texture loaded successfully: %s\n", filePath);
            return texture;
        }
        printf("Failed to create texture: %s\n", filePath);
        return nullptr;
    }
    printf("Cannot find texture file: %s\n", filePath);
    return nullptr;
}


int main(int argc, char **args) {

    bool quit = false;

    const int kRow = 5;
    const int kCol = 10;
    const int tileSize = 32;
    SDL_Texture* texture = nullptr;

    int level[kRow][kCol];

    //Load from file - Doesn't have any guards against errors
    std::ifstream fs("level.txt", std::ifstream::in);
    for(int r = 0; r < kRow; r++){
        for(int c = 0; c < kCol; c++){
            fs >> level[r][c];
        }
    }

    SDL_Init( SDL_INIT_EVERYTHING );
    SDL_Window* window = SDL_CreateWindow( "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 320, 160, 0 );
    SDL_Renderer* renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED );

    texture = loadTexture(renderer, "../tex.bmp");

    SDL_Event e;
    while (!quit) {
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
        }

        SDL_RenderClear(renderer);

        for (int i = 0; i < kRow; ++i) {
            for (int j = 0; j < kCol; ++j) {
                // Gets the tile position
                const SDL_Rect dstRect = {j * tileSize, i * tileSize, tileSize, tileSize};
                // Gets tileID from the texture we need to draw
                const SDL_Rect srcRect = {level[i][j] * tileSize, 0, tileSize, tileSize};
                SDL_RenderCopy(renderer, texture, &srcRect, &dstRect);
            }
        }

        SDL_RenderPresent(renderer);
    }

    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

#33

i’m uh…using C not C++


#34

Well then you will have to use
fread - https://www.tutorialspoint.com/c_standard_library/c_function_fread.htm

Again I am asking why limit yourself to C , do you need to build it in C is that a condition?


#35

Ok thanks, I’ll see what I can do with it (obviously i’m going to change it up and put it in the C way). If i have any more questions, i’ll ask you.


#36

Oh and yes, I do need to build it in C