I encountered a small puzzle while programming my map creation code. As explained yesterday, my maps are defined as an array of arrays. The bounds in the X and Y directions will be variable (though each sub array, or X bound, must be the same as the others), but you'll end up with a rectangle in the end. My texture map has one unique quality to it: each row is four tiles wide (4 x 32px). I can add more textures vertically if I need them, but the horizontal size of the texture map will remain at 4 tiles. So the question became, "How do you mathematically figure out which tile in the texture map to use based on the integer stored in the map definition?"
Glad I asked.
Say, for example, that the current position in the map definition tells us we need tile 5. If we look back at the texture map, you start counting from the top left at 0, and the first row ends at 3. Then you jump down a row and start over from the left. The #5 tile should be at position [1,1] if you think of the texture map as a multidimensional array which is exactly how we need to think about it in order to program it. So how the heck do we figure that 5 == [1,1]?
It's quite simple, really. The first step is to get the Y coordinate, or the row. You create a variable of type double (this will be important), and you divide your tile position (5 in our example) by the number of tiles in a row (4). From there, we can cast the double to an int to get the Y position. Note that sheetWidth here is calculated elsewhere based on the texture map width, but it equates to 4.
double tilePositionPrecision = (double)5 / (double)sheetWidth; int tileY = (int)tilePositionPrecision; // tilePositionPrevision == 1.25 // tileY == 1
Simple enough. Now to get the X position. This line is a little more complex looking, but it's still quite simple when you get down to it:
int tileX = (int)((tilePositionPrecision - (int)tilePositionPrecision) * sheetWidth); // tileX == 1
The first (int) says we want to cast the rest of the calculation as an integer. From there we take the double that we calculated earlier, subtract the same value casted as an integer, which will give us just the decimal remainder (0.25 in this example), and finally multiply that by sheetWidth which is 4. After all that, we have our X value.
So if you put that all together, you can draw the tile on screen easily with your X,Y pair:
spriteBatch.Draw( textureSheet, new Rectangle(x * TILESIZE, y * TILESIZE, TILESIZE, TILESIZE), new Rectangle(tileX * TILESIZE, tileY * TILESIZE, TILESIZE, TILESIZE), Color.White );
Now that I'm considering it, the logic above could probably accommodate different texture map widths as long as the textures themselves remain in the same order as they started out. I'll have to play around with that to see if my theory is correct.