SDL 2D Tile Engine Question

Started by
13 comments, last by GameDev.net 19 years, 4 months ago
hey, I'm trying to make a 2d tile engine and I'm having a problem with my code. I'm trying to randomly make a map from a bunch of tiles on a map surface then blit that map surface to my screen surface to display but for some reason it won't display. Everything is written in SDL btw. Take a look at my code and let me know if you have any ideas why it won't work.


#include <sdl_image.h>
#include <sdl.h>
#include <stdio.h>
#include <stdlib.h>

// Screen resolution
const int ScreenX = 640;
const int ScreenY = 480;
const int ScreenBBP = 16;

// Tile dimensions
const int TileDimension = 32;

SDL_Surface* g_pMainSurface = NULL; // surface for screen
SDL_Surface* g_pTileSurface = NULL; // surface for tile graphic
SDL_Surface* g_pMapSurface = NULL; // surface for the actual map
SDL_Event g_Event;  
SDL_Rect g_SrcRect, g_DstRect, g_MapRect; // RECT info for all surfaces

int main(int argc, char* argv[])
{
	// calculate the number of tiles needed to fill the screen
	int MapSizeX = ScreenX / TileDimension;
	int MapSizeY = ScreenY / TileDimension;
	
	// integers for the loop to make the map
	int y = 0;
	int x = 0;
	int RandTile = 0;

	// Initialize SDL Video
	SDL_Init(SDL_INIT_VIDEO);
	
	// Set Video mode on MainSurface
	g_pMainSurface = SDL_SetVideoMode(ScreenX,ScreenY,ScreenBBP,SDL_ANYFORMAT);

	// Load graphic for tiles
	g_pTileSurface = IMG_Load("grasstiles.tga");

	// Loop to randomly draw a map
	for(y = 0; y < MapSizeY; y++)
	{
		for(x = 0; x < MapSizeX; x++)
		{
			// Randomly select a number for the tile
			RandTile = rand() % 14;

			// Get the position of the random tile
			g_SrcRect.x= (RandTile * TileDimension) + RandTile ;
			g_SrcRect.y=0;
			g_SrcRect.w= g_SrcRect.x + TileDimension;
			g_SrcRect.h= TileDimension;

			// set the position of the next tile
			g_DstRect.x = x * TileDimension;
			g_DstRect.y = y * TileDimension;
			g_DstRect.w = g_DstRect.x + TileDimension;
			g_DstRect.h = g_DstRect.y + TileDimension;
			
			// Draw the tile to the Map Surface
			SDL_BlitSurface(g_pTileSurface, &g_SrcRect, g_pMapSurface, &g_DstRect);
		}
	}

	// get the dimensions for the mao
	g_MapRect.x= 0;
	g_MapRect.y= 0;
	g_MapRect.w= ScreenX;
	g_MapRect.h= ScreenY;
	
	for(;;)
	{
		if(SDL_PollEvent(&g_Event)==0)
		{
			// blit map surface to main surface
			SDL_BlitSurface(g_pMapSurface, &g_MapRect, g_pMainSurface, &g_MapRect);
			
			// update screen
			SDL_UpdateRect(g_pMainSurface, 0, 0, 0, 0);
		}
		else
		{ if(g_Event.type==SDL_QUIT) break; }
	}
	
	return(0);
}
	


All that is necessary for the triumph of evil is that good men do nothing. -Edmund Burke
Advertisement
Hmmm, I don't see anything wrong in particular...so you say nothing displays at all? Why don't you take out the code where you display the map and take it outside of the loop just to be safe? Or maybe instead of blitting all your tile images to an intermediate image, blit them directly to the surface and see what happens?

Print statements are your friends. :) Make sure that every blit statement is being executed. You might have a problem with that blit surface being inside the SDL_Event loop because it might blit the map and then loop back and immediately blit again, but it might overwrite all your tiles and instead display a blank screen. That's the only thing I can think of, but I haven't touched the SDL video functions in a while. Hope that helps.

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

I don't think you called SDL_Flip at all. . .
Quote:Original post by ImperfectFreak
I don't think you called SDL_Flip at all. . .


But he's using SDL_UpdateRect() instead.
Ad: Ancamnia
ok I tried displaying it directly to the main surface and it displays but not when I try drawing the map on another surface first. So of course I ran the debugger and it seems that nothing gets actually on to the MapSurface. The pointer always stays at NULL. No idea why. Anyway, would it be best If I just displayed directly on the Main Surface? I forgot what was faster, haven't done this in a while.
All that is necessary for the triumph of evil is that good men do nothing. -Edmund Burke
Quote:Original post by Eric Poitras
ok I tried displaying it directly to the main surface and it displays but not when I try drawing the map on another surface first. So of course I ran the debugger and it seems that nothing gets actually on to the MapSurface. The pointer always stays at NULL. No idea why. Anyway, would it be best If I just displayed directly on the Main Surface? I forgot what was faster, haven't done this in a while.


Yes, I always blit to the screen instead of blitting to an intermediate surface and then blitting that to the screen. (I think it should be slightly faster going directly to the screen).

I also highly recommend you use a double buffer and call SDL_Flip instead of SDL_UpdateRect(screen, 0, 0, 0, 0); This eliminates a couple of common video display problems, I forget the names of them at the moment. If you enable this option, then you can freely draw to the screen without worry of anything being partially or incompletely displayed. When you are done with your drawing, just call SDL_Flip and it will flip the screen surface you just worked on with what's actually on the screen. Its a piece of cake and works great.

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

yup I already switched to SDL_Flip and Double Buffer. I just didn't bother changing it in the code above. I know I can do this stuff easily but last time I actually programmed any 2D was about 2 years ago and it was all in DirectDraw. Anyway, just another quick question.

How to I set up the fastest way to get the x and y values of my source tiles if I have a tileset of multiple rows and multiple columns. like for example this is what I have for just one row:

g_SrcRect.x = (RandTile * TileDimension) + RandTile;
g_SrcRect.y = 0;
g_SrcRect.w = (g_SrcRect.x + TileDimension);
g_SrcRect.h = TileDimension;

Just to clarify, the RandTile variable is just a random tile number and the + Randtile I have there is because I have a 1 pixel border around all the tiles. I know there's a quick way you can do this with modulus but I can't remember how.

All that is necessary for the triumph of evil is that good men do nothing. -Edmund Burke
Hmm, ok let me get this straight. So you have a big image that is made up of several rows and columns of tiles, and you want to know the fastest way to get a random X, Y coordiante? I think that's what you're asking...well anyway lets say you have 5 tile rows and 3 tile columns. To get the x,y coordinates of a random tile (x,y coordiante is the top left hand corner of a tile) using the modulus operator try something like this:


Src_rect.x = rand() % tile_rows;
Src_rect.y = rand() % tile_cols;


The modulus operator returns the remainder after division. So in the Src_rect.x case you are dividing a random number by 5, so you can have a remainder between 0 and 4 (integers only). Same idea with the cols. I think that's the answer you're looking for.



I did my own random map generator for some test code a while back, but instead of keeping all the tiles in a tileset like you did I kept each tile in an individual file. Here's the function.


void MapData::LoadData(string map_name) {
// First remove all the old data in our vectors
tile_vector.clear();
for (unsigned int r = 0; r < map_vector.size(); r++)
map_vector[r].clear();
map_vector.clear();

// The following is temporary code until we get the data configuration code working
if (map_name == "testing") {
num_tile_rows = TILE_ROWS * 2; // Set the # of tile rows in the new map
num_tile_cols = TILE_COLS * 4; // Set the # of tile columns in the new map
col_pos = 2 * TILE_COLS + 4; // Set the initial column position
row_pos = TILE_ROWS; // Set the initial row position
num_tiles = 15; // Set the number of unique tile images that compose the map

//tile_vector.resize(num_tiles); // Resize our tile vector. For some weird reason, this causes a seg fault

// Load the tile vector with all of the tile filename strings
tile_vector.push_back("tile/bones_01.png");
tile_vector.push_back("tile/clay_01.png");
tile_vector.push_back("tile/desert_01.png");
tile_vector.push_back("tile/desert_02.png");
tile_vector.push_back("tile/desert_03.png");
tile_vector.push_back("tile/dirt_01.png");
tile_vector.push_back("tile/forest_01.png");
tile_vector.push_back("tile/path_01.png");
tile_vector.push_back("tile/path_02.png");
tile_vector.push_back("tile/road_01.png");
tile_vector.push_back("tile/road_02.png");
tile_vector.push_back("tile/rock_01.png");
tile_vector.push_back("tile/rock_02.png");
tile_vector.push_back("tile/wall_01.png");
tile_vector.push_back("tile/water_01.png");

// Load all the new tile images into the image_cache
for (unsigned int i = 0; i < tile_vector.size(); i++)
VideoManager.LoadImage(tile_vector);

// Resize our map_vector
map_vector.resize(num_tile_rows);
for (int r = 0; r < num_tile_rows; r++)
map_vector[r].resize(num_tile_cols);

// Now we go thru and generate a random map with the tiles we loaded in. Cool huh?
for (int r = 0; r < num_tile_rows; r++) {
for (int c = 0; c < num_tile_cols; c++) {
if (r == 0 || r == num_tile_rows - 1) // Make the top and bottom one tile for distinction
map_vector[r][c] = num_tiles - 1; // Top & bottom are now "water"
else if (c == 0 || c == num_tile_cols - 1) // Make the sides one tile for distinction
map_vector[r][c] = num_tiles - 2; // Top & bottom are now "wall"
else
map_vector[r][c] = RandomNum(0, num_tiles - 3); // Assign a random tile in the map_vector
}
}
}
}


RandomNum is a function I wrote myself that returns a Guassian random variable. The vectors are a little confusing at first but it works like this: the 2 dimension map_vector stores a bunch of indeces. These indeces are used to access the tile_vector, which stores the actual images.


I got the code working and managed to get a sprite to walk around on a randomly generated map. If you want to see more code go here:

http://sourceforge.net/projects/allacrost/

Then click on "Browse CVS" and go to module_01 => src and view map.h and map.cpp. It's primitive but it works. :) Hope that helps.

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

hmmm ok that wasn't exactly what I wanted. I'll explain in greater detail. First let's start with my tileset, I have 190 tiles at 32x32 pixels, of course I couldn't put 190 tiles in one row because I don't even think that would fit on a surface so I divided it up in 10 rows with 19 tiles in each.

ok so now what I'm doing is instead of having variables for rows and columns, I only have one variable for the tile. meaning that each tiles are numbered like so:

[0] [1] [2] [3] [4]
[5] [6] [7] [8] [9]
[10][11][12][13][14]

ok so now let's say Randtile = 2. The rect would look like this:

Rect.x= RandTile * TileDimension;
Rect.y= 0;
Rect.w= Rect.x + TileDimension;
Rect.h= TileDimension;

ok that's all good if I only have one row but I have more than one row and that rect won't suffice the way it is. Let's say I want to display the 7th tile for instance, how would I make a rect that would handle drawing all the tiles. I hope this makes some sense.

btw Roots, how's your game comming along?
All that is necessary for the triumph of evil is that good men do nothing. -Edmund Burke
Well, if your tiles are arranged in a 19x10 grid (19 across, 10 down) you can get the row/col of any given tile, T, by:
Row = T / 19Col = T % 19


Multiply Row and Col by the size of a tile to get the actual pixel coords of that tile within the image.

This topic is closed to new replies.

Advertisement