Advertisement

SDL TileEngine

Started by December 18, 2011 11:12 PM
0 comments, last by MichaelBarth 12 years, 11 months ago
Okay first things first I'm only writing in C right now, not C++, so LazyFoo's tutorial doesn't help me so much.

Anyway I want to be able to create a tile map with file like:


[MapLayer]
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00

[SpriteLayer]

00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00
00 01 00 00 00 00 00 10 10 00 01 02 03 04 01 00


Something like that.

The map that will be visible on the screen I want to be 640x480 with 16x16 tiles, which totals to 1200 tiles per screen. So what I ultimately want is to be able to draw these 1200 tiles onto an SDL_Surface that I can resize to fit the screen. Now the problem isn't necessarily that I can't resize it or any of the math involved, it's reading from a file that's giving me trouble. I'm finding it very difficult to figure out how to read from a file and find where the tag [MapLayer] or [SpriteLayer] might be then reading all the tile numbers into an array, which being that I'm used to C#'s XNA, I would normally use a multidimensional array.

I've looked at input\output tutorials, but it's hard to make sense of it for what I'm trying to do. This is along the lines of what I want to do in simple terms:


Open File "mapname.map"
Read and find the string "[MapLayer]"
If string doesn't exist, close and quit.
Go to next line and read the numbers and put them into an array and get the length of how many tiles were counted and put into the array.
Continue reading the next lines and putting them into an array until there is no more tiles, and if they are a different length than the first, cancel and close the file.
Find if there is a string "[SpriteLayer]" and repeat the process of the [MapLayer], only this time, the tiles represent sprites.
Close the file.

Use array(s) to draw tiles relative to a "Camera's" position.

If someone could give me some guidance or a C based tutorial on how to do this, it'd be very appreciated.

Just for some clarification, right now I'm running on Mac OS X 10.6 with XCode 3.2.6
(If you're making some kind of a WTF face right now, I made that same one when I had to switch back to this Mac after having some problems with my PC)
Okay, I eventually decided to forget it and move back to C++. I've got a tile engine going and I'm working on a small remake of the Legend of Zelda A Link to the Past game for SNES for learning purposes.

The first part you may notice about my code just so you're not asking "why" is that when I draw a level I'm using the original resolution of 256x224 (224 tiles per screen) to make it look like the original game. Also, there's not much error checking because I know where everything is right now and I'll rewrite it with more error checking later once I have the actual TIleEngine up and running.

Okay here's my problem, I have a 1440 x 900 screen. All my tiles (so far) are 16x16 (later I'll add 32x32). What I'm doing right now is drawing 224 tiles per screen 16 tiles per line, and 14 lines. The problem I'm facing is that it's drawing at a resolution of 256 x 224 on a 1440 x 900 screen. What I want to do is re-size what's being drawn to the screen to the SCREENWIDTH and SCREENHEIGHT integers. The best case scenario would be to re-size the individual tiles using a scale factor based on the screen width and height. I'm finding that to be extremely difficult to do with the way this is set up. (I'm now following the LazyFoo SDL tutorial for a TileEngine)

Right now I have it working, but the way it's setup collision detection would be impossible once I add in a character or an enemy, or whatever. What I do right now is take the tiles blit them to the screen once have an SDL_Surface copy the contents of the screen clipping the width and the height then re-sizing that surface and blit it to the screen. It also seems to take up quite a bit of the CPU as well. Alright now that I've done all the explaining here's the actual code:

Clipping and Drawing Code and variables:


SDL_Surface *tileSheet = NULL;

const int LEVEL_WIDTH = 256;
const int LEVEL_HEIGHT = 224;

const int TileWidth = 16;
const int TileHeight = 16;


const int TOTAL_TILES = 224;
const int TILE_SPRITES = 16;

//The different tile sprites
const int TILE_ROCKS = 0;
const int TILE_BLADEGRASS = 1;
const int TILE_GRASS = 2;
const int TILE_SIGN = 3;
const int TILE_TALLGRASS = 4;
const int TILE_GRASSSPECS = 5;
const int TILE_BROWNGRASS = 6;
const int TILE_DIRTROCKS = 7;
const int TILE_STONE = 8;
const int TILE_BHOUSETLEFT = 9;
const int TILE_BHOUSET = 10;
const int TILE_BHOUSETRIGHT = 11;
const int TILE_BHOUSEMID = 12;
const int TILE_BHOUSEBLEFT = 13;
const int TILE_BHOUSEB = 14;
const int TILE_BHOUSEBRIGHT = 15;

SDL_Rect clips[ TILE_SPRITES ];
SDL_Rect camera = { 0, 0, SCREENWIDTH, SCREENHEIGHT };
void Tile::Draw()
{
//If the tile is on screen
if( check_collision( camera, box ) == true )
{
//Show the tile
RenderSurface( box.x - camera.x, box.y - camera.y, tileSheet, screen, &clips[ type ] );
}
}

void clip_tiles()
{
//Clip the sprite sheet
clips[ TILE_ROCKS ].x = 0;
clips[ TILE_ROCKS ].y = 0;
clips[ TILE_ROCKS ].w = TileWidth;
clips[ TILE_ROCKS ].h = TileHeight;

clips[ TILE_BLADEGRASS ].x = 16;
clips[ TILE_BLADEGRASS ].y = 0;
clips[ TILE_BLADEGRASS ].w = TileWidth;
clips[ TILE_BLADEGRASS ].h = TileHeight;

clips[ TILE_GRASS ].x = 32;
clips[ TILE_GRASS ].y = 0;
clips[ TILE_GRASS ].w = TileWidth;
clips[ TILE_GRASS ].h = TileHeight;

clips[ TILE_SIGN ].x = 48;
clips[ TILE_SIGN ].y = 0;
clips[ TILE_SIGN ].w = TileWidth;
clips[ TILE_SIGN ].h = TileHeight;

clips[ TILE_TALLGRASS ].x = 0;
clips[ TILE_TALLGRASS ].y = 16;
clips[ TILE_TALLGRASS ].w = TileWidth;
clips[ TILE_TALLGRASS ].h = TileHeight;

clips[ TILE_GRASSSPECS ].x = 16;
clips[ TILE_GRASSSPECS ].y = 16;
clips[ TILE_GRASSSPECS ].w = TileWidth;
clips[ TILE_GRASSSPECS ].h = TileHeight;

clips[ TILE_BROWNGRASS ].x = 32;
clips[ TILE_BROWNGRASS ].y = 16;
clips[ TILE_BROWNGRASS ].w = TileWidth;
clips[ TILE_BROWNGRASS ].h = TileHeight;

clips[ TILE_DIRTROCKS ].x = 48;
clips[ TILE_DIRTROCKS ].y = 16;
clips[ TILE_DIRTROCKS ].w = TileWidth;
clips[ TILE_DIRTROCKS ].h = TileHeight;

clips[ TILE_STONE ].x = 0;
clips[ TILE_STONE ].y = 32;
clips[ TILE_STONE ].w = TileWidth;
clips[ TILE_STONE ].h = TileHeight;

clips[ TILE_BHOUSETLEFT ].x = 16;
clips[ TILE_BHOUSETLEFT ].y = 32;
clips[ TILE_BHOUSETLEFT ].w = TileWidth;
clips[ TILE_BHOUSETLEFT ].h = TileHeight;

clips[ TILE_BHOUSET ].x = 32;
clips[ TILE_BHOUSET ].y = 32;
clips[ TILE_BHOUSET ].w = TileWidth;
clips[ TILE_BHOUSET ].h = TileHeight;

clips[ TILE_BHOUSETRIGHT ].x = 48;
clips[ TILE_BHOUSETRIGHT ].y = 32;
clips[ TILE_BHOUSETRIGHT ].w = TileWidth;
clips[ TILE_BHOUSETRIGHT ].h = TileHeight;

clips[ TILE_BHOUSEMID ].x = 0;
clips[ TILE_BHOUSEMID ].y = 48;
clips[ TILE_BHOUSEMID ].w = TileWidth;
clips[ TILE_BHOUSEMID ].h = TileHeight;

clips[ TILE_BHOUSEBLEFT ].x = 16;
clips[ TILE_BHOUSEBLEFT ].y = 48;
clips[ TILE_BHOUSEBLEFT ].w = TileWidth;
clips[ TILE_BHOUSEBLEFT ].h = TileHeight;

clips[ TILE_BHOUSEB ].x = 32;
clips[ TILE_BHOUSEB ].y = 48;
clips[ TILE_BHOUSEB ].w = TileWidth;
clips[ TILE_BHOUSEB ].h = TileHeight;

clips[ TILE_BHOUSEBRIGHT ].x = 48;
clips[ TILE_BHOUSEBRIGHT ].y = 48;
clips[ TILE_BHOUSEBRIGHT ].w = TileWidth;
clips[ TILE_BHOUSEBRIGHT ].h = TileHeight;
}



The main():


int main( int argc, char* args[] )
{
if (!Init())
return 1;
tileSheet = Load_IMG("C:\\tiles.png");
clip_tiles();
if (set_tiles( tiles ) == false)
return 1;
while (!quit)
{
FRATE.start();
Update();
Draw();
while(SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
quit = true;
if (event.type == SDL_KEYDOWN)
{
switch(event.key.keysym.sym)
{
case SDLK_ESCAPE:
quit = true;
}
}
}
if( SDL_Flip( screen ) == -1 )
{
printf("\nError updating the screen!");
return 1;
}
if((FRATECAPPED == true ) && ((float)FRATE.get_ticks() < 1000.0f / FPS))
{
//Sleep the remaining frame time
SDL_Delay(( 1000.0f / FPS ) - (float)FRATE.get_ticks());
}
}
clean_up(tiles);
SDL_FreeSurface(newScreen);
SDL_Quit();
return 0;
}

The Draw() referenced in main: (Update() is not used yet)


void Draw()
{
if (!tiletoScreen)
{
for( int t = 0; t < TOTAL_TILES; t++ )
{
tiles[ t ]->Draw();
}
}
if (!tiletoScreen)
{
newScreen = SDL_ConvertSurface(screen, screen->format, SDL_SWSURFACE);
newScreen = ScaleSurface(newScreen, SCREENWIDTH, SCREENHEIGHT, 0, 0, 256, 224);
tiletoScreen = true;
}
if (tiletoScreen)
RenderSurface(0, 0, newScreen, screen, NULL);
}


My modified version of ScaleSurface that uses the getpixel and putpixel functions:


SDL_Surface *ScaleSurface(SDL_Surface *Surface, Uint16 Width, Uint16 Height, int clipx = NULL , int clipy = NULL, int clipw = NULL, int cliph = NULL)
{
if(!Surface || !Width || !Height)
return 0;
int usingclip = 0;
if (!clipw || !cliph)
usingclip = 0;
else
usingclip = 1;
SDL_Surface *_ret = NULL;
_ret = SDL_CreateRGBSurface(Surface->flags, Width, Height, Surface->format->BitsPerPixel,
Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask, Surface->format->Amask);
double _stretch_factor_x, _stretch_factor_y;
if (usingclip == 0)
{
_stretch_factor_x = (static_cast<double>(Width) / static_cast<double>(Surface->w));
_stretch_factor_y = (static_cast<double>(Height) / static_cast<double>(Surface->h));
}
if (usingclip == 1)
{
_stretch_factor_x = (static_cast<double>(Width) / static_cast<double>(clipw));
_stretch_factor_y = (static_cast<double>(Height) / static_cast<double>(cliph));
}

if (usingclip == 0)
{
for(Sint32 y = 0; y < Surface->h; y++)
for(Sint32 x = 0; x < Surface->w; x++)
for(Sint32 o_y = 0; o_y < _stretch_factor_y; ++o_y)
for(Sint32 o_x = 0; o_x < _stretch_factor_x; ++o_x)
putpixel(_ret, static_cast<Sint32>(_stretch_factor_x * x) + o_x,
static_cast<Sint32>(_stretch_factor_y * y) + o_y, getpixel(Surface, x, y));
}
if (usingclip == 1)
{
for(Sint32 y = static_cast<Sint32>(clipy); y < static_cast<Sint32>(cliph); y++)
for(Sint32 x = static_cast<Sint32>(clipx); x < static_cast<Sint32>(clipw); x++)
for(Sint32 o_y = 0; o_y < _stretch_factor_y; ++o_y)
for(Sint32 o_x = 0; o_x < _stretch_factor_x; ++o_x)
putpixel(_ret, static_cast<Sint32>(_stretch_factor_x * x) + o_x,
static_cast<Sint32>(_stretch_factor_y * y) + o_y, getpixel(Surface, x, y));
}

return _ret;
}


Right now I would say that the most important part is the drawing code and the clipping code.

What's the best way I can re-size the tiles based on a scale factor that is calculated based on a comparison of the projected screen width and height (256x224) to the actual screen (1440x900 or whatever the case may be).

This topic is closed to new replies.

Advertisement