Clipping a tilesheet

Whether you're a newbie or an experienced programmer, any questions, help, or just talk of any language will be welcomed here.

Moderator: Coders of Rage

Post Reply
PaperDuckyFTW
Chaos Rift Cool Newbie
Chaos Rift Cool Newbie
Posts: 76
Joined: Sat Apr 03, 2010 5:08 am
Programming Language of Choice: C++

Clipping a tilesheet

Post by PaperDuckyFTW »

Hey people, I've been stumped on this issue for around a week or two now X'( So I thought maybe someone here can show my what I'm doing wrong.
I have a 2D array based tile map, and I'm having trouble splitting the tilesheet. I know the logic behind it, but I'm most likely writing it the wrong way.

My understanding of it is this: the numbers in the array represent a tile on the tilesheet.
(dw, ill just post code cause idk how to put it into words)

here is my drawing function:

Code: Select all

void DrawSubImage(int x, int y, int clipX, int clipY, SDL_Surface* source)
{
   SDL_Rect pos = {x, y};
   SDL_Rect clip = {clipX * 32, clipY*32, 32, 32}; //the x and y position ON the image
   SDL_BlitSurface(source, &clip, screen, &pos);
}
This works perfectly fine, for example I can write this:
DrawSubImage(30, 30, 2, 3, tiles);
And it will draw the second tile on the third row if the image.

Here is the map drawing function, with the logic I understand is correct.

Code: Select all

RenderMap()
{
   //for this example, the tile dimensions are 32x32
   int sheetW =tiles->GetTexWidth()/tileWidth; //the width of the sheet divided by the height of the tile. Same as surface->h
   int sheetH = tiles->GetTexHeight()/tileHeight;
   int maxTiles = sheetW*sheetH; //max number of tiles on the tilesheet
   int clipX, clipY=0; //position on the tilesheet

   for(int x=0; x<mapW; x++)
   {
      for(int y=0; y<mapH; y++)
      {
         for(int clipX=0; clipX<maxTiles; clipX++)
         {
           //if a number in the array is the same one as the index
            if(map[x][y] == clipX)
            {
               //if we reach the end of the tilesheet
               if(clipX >= sheetW)
               {
                  clipX=0; //go back to the start of the sheet
                  clipY++; //go down a row
               }
               tiles->DrawSubImage(x*tileWidth, y*tileHeight, clipX, clipY, tiles); //draw all the tiles
            }
          }
       }
}
Firstly I would like to apologize for all the code and I hope it makes sense. My understanding is- draw the tiles accross the tilesheet, and if we reach the max number of tiles accross the sheet, jump down a row. Hopefully I am right about this, and if I am, I presume the error is in how I wrote it.
When I test the application, it drops down to the next row of tiles, but it stops there and doesn't continue to draw anymore tiles.

I've tried many different ways, this is just an example of how I'm implimenting it. If anything looks confusing, please ask and thankyou very much for taking your time to help me :) Btw, Im aware I have explained it wrong/confusingly so if you have any doubts about any of it, its most likely my inability to explain shit easily
Thanks for yo time peeps, take it easy.
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: Clipping a tilesheet

Post by dandymcgee »

PaperDuckyFTW wrote: Here is the map drawing function, with the logic I understand is correct.

Code: Select all

RenderMap()
{
   //for this example, the tile dimensions are 32x32
   int sheetW =tiles->GetTexWidth()/tileWidth; //the width of the sheet divided by the height of the tile. Same as surface->h
   int sheetH = tiles->GetTexHeight()/tileHeight;
   int maxTiles = sheetW*sheetH; //max number of tiles on the tilesheet
   int clipX, clipY=0; //position on the tilesheet

   for(int x=0; x<mapW; x++)
   {
      for(int y=0; y<mapH; y++)
      {
         for(int clipX=0; clipX<maxTiles; clipX++)
         {
           //if a number in the array is the same one as the index
            if(map[x][y] == clipX)
            {
               //if we reach the end of the tilesheet
               if(clipX >= sheetW)
               {
                  clipX=0; //go back to the start of the sheet
                  clipY++; //go down a row
               }
               tiles->DrawSubImage(x*tileWidth, y*tileHeight, clipX, clipY, tiles); //draw all the tiles
            }
          }
       }
}
That should not be your map drawing function, that should be your map loading function. Why would you bother looping through every existing tile every time you draw a tile when you could just store the tiles in an array and directly access the required index. That's the whole point of storing numbers in your map file.

This is completely untested code, but it should be close to what you're trying to do:

Code: Select all

//Get tile set
SDL_Surface *tileSet = LoadTileSet();

//Rect struct (you can use SDL_Rect in SDL)
struct Rect {
	int x;
	int y;
	int w;
	int h;
};

//Collection of each possible tile (assuming 10 tiles, can be any number)
Rect tiles[10];

//640 x 480 map of 32 x 32 tiles
int map[15][20];

//Load tile coords into array of Rects
void LoadMap()
{
	for(int y = 0; y < tileSetHeight; y += tileHeight)
	{
		for(int x = 0; x < tilSetWidth; x += tileWidth)
		{
			int curTileIndex = y*(tilSetWidth / tileWidth) + x;
			tiles[curTileIndex].x = x;
			tiles[curTileIndex].y = y;
			tiles[curTileIndex].w = tileWidth;
			tiles[curTileIndex].h = tileHeight;
		}
	}
}

//Render the map
void RenderMap()
{
	for(int y = 0; y < 15; y++)
	{
		for(int x = 0; x < 20; x++)
		{
			Rect curTile = tiles[map[y][x]];
			tiles->DrawSubImage(curTile.x*tileWidth, curTile.y*tileHeight, clipX, clipY, tileSet); 
		}
	}
}
Later on you can replace Rect tiles[10]; with Tile* tiles[10] (Tile being a custom struct containing a rect and stuff like collision and animation data).

Let me know if you notice any serious flaws or don't quite understand something I've written.
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
PaperDuckyFTW
Chaos Rift Cool Newbie
Chaos Rift Cool Newbie
Posts: 76
Joined: Sat Apr 03, 2010 5:08 am
Programming Language of Choice: C++

Re: Clipping a tilesheet

Post by PaperDuckyFTW »

Thanks for your quick reply
That should not be your map drawing function, that should be your map loading function.
To clear it up, I do have a separate loading function, and as bad as it is, i dont have any tile struct/class :oops: here is an example of my loading function:

Code: Select all

int map[10][10]; //basic array

void LoadMap()
{
   fstream file(filename, ios::in);
   while(file.is_open())
   {
      for(int x=0; x<10; x++)
      {
         for(int y=0; y<10; x++)
         {
            file >> map[x][y];
         }
      }
   }
}
The reason i had all that shit in the draw function is becuase I usually just have one big, long horizontal tilesheet, so I can just have a loop like this:

Code: Select all

for(int clipx=0, clipx<maxTiles; clipx++)
{
   if(map[y][x] == clipx) //if a tile in the array is one of the maxtiles
   {
      DrawTile(x*tileW, y*tileH, clipx)
   }
}
And it will just draw the horizontal tiles as all im doing is increasing the x position of the tilesheet Im drawing. I thought it might be a simple fix to just change the clipx and clipy before I draw it, as the logic seemed to be simple. (when the clipX reaches the last tile in the row, go down a row and repeat)

I can load and draw a map with tiles in a horizontal tilesheet (with only one row of tiles), but Im having trouble with drawing a map with more then one row of tiles

Thanks for your suggestion, ill probably start on a tile class. I have one question though, and I was wondering if you can explain how you got or use this:
int curTileIndex = y*(tilSetWidth / tileWidth) + x;
Will this initialize each of the tiles?

And there is also this part (ive never seen this before)
Rect curTile = tiles[map[y][x]];
I didnt know you can set a 2D array inside a 1D array.
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: Clipping a tilesheet

Post by dandymcgee »

PaperDuckyFTW wrote: Thanks for your suggestion, ill probably start on a tile class. I have one question though, and I was wondering if you can explain how you got or use this:
int curTileIndex = y*(tilSetWidth / tileWidth) + x;
Will this initialize each of the tiles?
I'm just giving each tile in the sheet a number based on its position in the sheet and using that as it's index in the tiles array. If the sheet was 3 x 3 the indices would look like this:

Code: Select all

1 2 3
4 5 6
7 8 9
So if the middle tile in the 3x3 sheet was the grass tile I'd reference it as tiles[5]
PaperDuckyFTW wrote: And there is also this part (ive never seen this before)
Rect curTile = tiles[map[y][x]];
I didnt know you can set a 2D array inside a 1D array.
I'm not setting an array, i'm simply using the integer stored at map[y][x] as the index to access the tile in the tiles array. It's the same as:

Code: Select all

int tileAtCurrentPositionID = map[y][x];  //We're looping through each tile in the map, what is the current tile's ID?
Rect curTile = tiles[tileAtCurrentPositionID];  //Use the ID from the map to locate the actual tile from the tiles array
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
PaperDuckyFTW
Chaos Rift Cool Newbie
Chaos Rift Cool Newbie
Posts: 76
Joined: Sat Apr 03, 2010 5:08 am
Programming Language of Choice: C++

Re: Clipping a tilesheet

Post by PaperDuckyFTW »

I tried what you suggested, however it seems to be drawing the whole tileset, and not clipping it

I'll finish up this tile class and then see if Im still stuck. (I know this isn't much info, it was more or less an update)
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: Clipping a tilesheet

Post by dandymcgee »

PaperDuckyFTW wrote:I tried what you suggested, however it seems to be drawing the whole tileset, and not clipping it

I'll finish up this tile class and then see if Im still stuck. (I know this isn't much info, it was more or less an update)
Alright, if you're still stuck post back. I might be able to grab a copy of your code and debug it myself, it's a thousand times easier to help when I can see it running in front of me.
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
PaperDuckyFTW
Chaos Rift Cool Newbie
Chaos Rift Cool Newbie
Posts: 76
Joined: Sat Apr 03, 2010 5:08 am
Programming Language of Choice: C++

Re: Clipping a tilesheet

Post by PaperDuckyFTW »

After some brainstorming I came up with an idea on how to solve it (with an array of SDL_Rects for all the possible tiles, like you suggested)
In that brainstorm, I was trying to figure out how to set the values of each array element without setting them manually. However it seems to stop

So I went of and wrote a small program to this affect, which set the values and displayed them in the console:

Code: Select all

int tileWidth=32;
int tileHeight=32;
int tileSetWidth=96; //three tiles accross
int tileSetHeight=96; //three tiles or rows down
int maxTiles = (tileSetWidth/tileWidth) * (tileSetHeight*tileHeight) //nine tiles in total in the tilesheet

struct Rect
{
   int x;
   int y;
   int w;
   int h;
}
Rect tiles[10]; //10 different tiles

void SetTiles()
{
   for(int ID = 0; ID<maxTiles; ID++) //There are 10 ID's
   {
      int ty=0;
      int tx = ID; //x position on the tileset

      /*It seems to stop the ID loop here, when I set tx to 0.
         Therefore it keeps 'tx' as 0, not increasing it


      if(ID >= tileSetWidth) //if we reach the last tile in the row
      {
         ty+=1; //go down a column
         ty=0; //reset the x position on the tileset
      }
      
      tiles[ID].x = ty*32
      tiles[ID].y = ty*32;
   }
}


//then I loop through each tile and display their variables
for(int i=0; i<maxTiles; i++)
{
   cout<<"Tile "<< i << ": " << tiles[i].x <<endl;
   cout<<"Tile "<< i << ": " << tiles[i].y << endl;
} 

This is the output I wanted:
Tile 0: 0 0
Tile 1: 32 0
Tile 2: 64 0

Tile 3: 0 32   <-Reset the x position and increase the y position on the sheet
Tile 4: 32 32 <-Continue increasing the x position
Tile 5: 64 32

However, after it reached tile 3, the x position stayed at 0;
So I was wondering, how do I continue the loop after I set it to 0?
My idea was to use the array of SDL_Rects as the clipx and clipy variables. I can set each array member manually but that will take a long time, and it will restrict me to the dimension of the tileset
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: Clipping a tilesheet

Post by dandymcgee »

Hey Ducky, no offense but I'm not going to try to list every change I made to your code. It doesn't even come slightly close to compiling, so I'll assume you just typed it into the post reply text field.
Nonetheless the comments were enough to let me know what you intended, thanks for that. ;)

This is based on what you posted and gives you the desired output. Let me know if you have any questions:

Code: Select all

#include <iostream>

using namespace std;

struct Rect {
	int x;
	int y;
	int w;
	int h;
};

int tileWidth;
int tileHeight;
int tileSetWidth;
int tileSetHeight;
int maxTiles;

Rect tiles[9];

void SetTiles()
{
	int tilesPerRow = (tileSetWidth/tileWidth);

	for(int ID = 0; ID < maxTiles; ID++)
	{
		tiles[ID].x = (ID % tilesPerRow) * tileWidth;
		tiles[ID].y = (ID / tilesPerRow) * tileHeight;
	}
}

int main(int argc, char* argv[])
{
	tileWidth = 32;
	tileHeight = 32;
	tileSetWidth = 96; //three tiles accross
	tileSetHeight = 96; //three tiles or rows down
	maxTiles = (tileSetWidth/tileWidth) * (tileSetHeight/tileHeight);

	SetTiles();

	for(int i = 1; i <= maxTiles; i++)
	{
		cout<<"Tile "<< i << ": " << tiles[i].x << ", " << tiles[i].y <<endl;
	} 

	cin.get();
	return 0;
}
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
PaperDuckyFTW
Chaos Rift Cool Newbie
Chaos Rift Cool Newbie
Posts: 76
Joined: Sat Apr 03, 2010 5:08 am
Programming Language of Choice: C++

Re: Clipping a tilesheet

Post by PaperDuckyFTW »

Thankyou very much dandymcgee for your help, I did have some issues with SDL_Rect (it's members are uint8 or something, and for some reason it wasn't initializing correctly)

Haha, you are right, I did post that in the text field. I wrote that program on another computer, so I just re wrote what I thought was the most important parts to illustrate what I needed help with.
And I wasn't offended, cause how can I be offended by someone, when they are helping me? :)

Thanks for your time and patience
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: Clipping a tilesheet

Post by dandymcgee »

PaperDuckyFTW wrote:Thankyou very much dandymcgee for your help, I did have some issues with SDL_Rect (it's members are uint8 or something, and for some reason it wasn't initializing correctly)

Haha, you are right, I did post that in the text field. I wrote that program on another computer, so I just re wrote what I thought was the most important parts to illustrate what I needed help with.
And I wasn't offended, cause how can I be offended by someone, when they are helping me? :)

Thanks for your time and patience
Cool, as long as you've got it working then all is well. ;)
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
Post Reply