[SOLVED] Strange problem with extra-tiles...

Started by
2 comments, last by d h k 16 years, 9 months ago
Hey there, I'm having a problem with tiles in my 2D-RTS game. I know that this might go into "Isometric Land" as well, but my issue is not really related to isometric graphics in any way, so I figured I'd post it in here, where a lot more people seem to come by and answer. If that is not okay, please feel free to move the thread. With that out of the way, I'll give you some information on the game and then come to the reason why I'm posting - the problem. The game uses 2D graphics and simple isometric tiles. My tiles use the following index-system on the map: As you can see, tiles always have either both coordinates even or both odd. However, this way of numbering (while being kinda a pain to set up in the first place) feels much more natural to me than most other common ways to number. Anyways, I'm only drawing tiles on the screen and when I print the numbers out of how many tiles are drawn, it becomes obvious that there seem to be a lot of tiles "uninitialized" in the top-left corner with x:0 and y:0 over each other. When I move the camera around the map, it's at 360 tiles on the screen at the moment, but when I hover over that spot in the top-left corner, the number goes up to 1100. This is how tiles are initialized when the game starts:

void init_tiles ( const char *filename )
// inits the tiles
{
	int	row_index = 0;
	int _x = 0;
	int _y = 0;

	tiles_drawn = 0;

	// init tileset
	tileset.init ( filename, TILE_WIDTH, TILE_HEIGHT, 10 );

	for ( int y = 0; y < TILES_Y; y++ )
	// loop through all tiles vertically
	{
		for ( int x = 0; x < ( TILES_X / 2 ); x++ )
		// loop through all tiles horizontally
		{
			if ( row_index == 0 )
			// if we are in a normal row
			{
					if ( ( is_even ( x * 2 ) && is_even ( y ) ) || ( !is_even ( x * 2 ) && !is_even ( y ) ) )
						// init normal tile
						tile[x * 2][y].init ( _x - ( TILE_WIDTH / 2 ), _y - ( TILE_HEIGHT / 2 ) - 1, grass, x * 2, y );
			}

			else if ( row_index == 1 )
			// if we are in a tabbed row
			{
				if ( ( is_even ( ( x * 2 ) + 1 ) && is_even ( y ) ) || ( !is_even ( ( x * 2 ) + 1 ) && !is_even ( y ) ) )
					// init tabbed tile
					tile[( x * 2 ) + 1][y].init ( _x - ( TILE_WIDTH / 2 ), _y - ( TILE_HEIGHT / 2 ) - 1, grass, ( x * 2 ) + 1, y );
			}

			// move on to next tile to the right
			_x += TILE_WIDTH;
		}

		// move on to the next row downwards
		_y += ( TILE_HEIGHT / 2 ) + 1;

		if ( row_index == 0 )
		// if the row before was normal
		{
			// make the next row tabbed
			_x = TILE_WIDTH / 2;
			row_index = 1;
		}
		else
		// if the row before was tabbed
		{
			// make the next row normal
			_x = 0;
			row_index = 0;
		}
	}
}


And this is how I draw the tiles repeatedly:

if ( ( is_even ( tile[x][y].i ) && is_even ( tile[x][y].j ) ) || ( !is_even ( tile[x][y].i ) && !is_even ( tile[x][y].j ) ) )
{
	if ( tile[x][y].x + TILE_WIDTH >= camera.x && tile[x][y].x <= camera.x + screen_width )
	// if tile is horizontally in view
	{
		if ( tile[x][y].y + TILE_HEIGHT >= camera.y && tile[x][y].y <= camera.y + screen_height )
		// if tile is vertically in view
		{
			// draw tile
			tile[x][y].draw ( ( float ) ( tile[x][y].x - camera.x ), ( float ) ( tile[x][y].y - camera.y ) );

			// count drawn tiles
			tiles_drawn++;
		}
	}
}


For confirmation, this is "is_even ()":

bool is_even ( int value )
{
	if ( value % 2 == 0 )
		return true;
	else
		return false;
}


As you can see, I think (or thought) that all the tiles that have a mix of odd and even coordinate-pairs might be those extra unitialized ones, but using the code above (which should filter those out), it's still the same. Can anybody make any sense out of that? Any help is very much appreciated! Thanks ahead of time! [Edited by - d h k on July 16, 2007 7:56:38 AM]
Advertisement
If I may ask, how is your array laid out? Are, for instance, laid out with a tabbed row and an untabbed one interleaved? This seems to me to be what is implied by your evenness check, but the rest of the code doesn't look to me as if this is the case. Am I correct then in thinking that each row, whether tabbed or untabbed, is kept in a separate array row, and similarly each column in a separate array column?

If this is the case, then I don't think that you should be multiplying x by two, or running your "for" loop until TILES_X/2, but rather running to TILES_X, unless I'm missing something.

If they are interleaved, then I would think that tabbed rows should be accessed at x*2 + 1 - which, note, should always be odd for valid integer values of x.

(naturally, it may be that the interleaving goes the other way, with tabbed rows first; in this case, access untabbed rows with x*2 + 1 instead.)

On a similar note, this -
!is_even ( x * 2 )

- should never evaluate to "true" - any integer multiplied by 2 should be even.

Note that, if I am correct, it would appear that tabbed rows are always oddly-indexed, while untabbed rows are always evenly-indexed.

Either way, I'm not sure that the evenness check is really called for.

I hope that I explained myself clearly, and that this helps. ^_^

MWAHAHAHAHAHAHA!!!

My Twitter Account: @EbornIan

Quote:Original post by d h k
The game uses 2D graphics and simple isometric tiles. My tiles use the following index-system on the map:

As you can see, tiles always have either both coordinates even or both odd. However, this way of numbering (while being kinda a pain to set up in the first place) feels much more natural to me than most other common ways to number.


OK, you'll waste every other tile in your array, but ok.

Quote:
This is how tiles are initialized when the game starts:


You're making things much too complicated.

- Objects shouldn't know about the container they're in, or duplicate that information. Thus, tiles shouldn't have 'i' and 'j' members - that information is implicit by their location in the array.

- You don't need to handle odd and even rows separately with the row-index like that (and even then, why not use a boolean like "current_row_tabbed" or something?); you already check if the row value is even in order to see if the tile is inited at all. Further, the way your numbering works actually takes care of the "tabbing" *automatically*; try using an image editor to get the image coordinates of the center of each diamond in your picture, and comparing to the drawn "tile coordinates", if you don't believe me.

In fact, this strange complication of your logic was the problem: you were using x values 0, 1, 2, 3, 4, ... to TILES_X / 2, and then mapping each of those to only *one* of 'x * 2' or 'x * 2 + 1' according to the row_index. To make the existing approach work would have required looping over both values of row_index for each x value.

- Don't make comments that say exactly what the function name does.

- This is C++; use a real string type (std::string), and save yourself from worries elsewhere in the code.

- If you're writing stuff with leading underscores to keep variables separate, you really should be thinking more about the names. But as it turns out, we don't need the '_x' and '_y' at all...

- Simplify boolean logic: what you're interested in is if the *parity of the two values is equal*.

We should be looking at something more like this:

void init_tiles (const std::string& filename) {	tiles_drawn = 0;	tileset.init(filename, TILE_WIDTH, TILE_HEIGHT, 10 );	for (int j = 0; j < TILES_Y; ++j) {		for (int i = 0; i < TILES_X; ++i) {			if (is_even(x) == is_even(y)) {				tile[x][y].init((x - 1) * (TILE_WIDTH / 2),				                (y - 1) * (TILE_HEIGHT / 2),				                grass);			}		}	}}


Yes, it's really that simple.

Quote:And this is how I draw the tiles repeatedly:


And similarly:

- The tile should take care of the calculations involving its own coordinates. Have draw() accept the bounds of the rectangle, and let the tile decide whether or not it appears inside, and its relative position.

- Try using bail-out logic to avoid those ugly "arrows" while also keeping the conditionals simple.

bool Tile::draw(int left, int top, int width, int height) {	if (x + TILE_WIDTH < left) return false;	if (x > left + width) return false;	if (y + TILE_HEIGHT < top) return false;	if (y > top + height) return false;	// Again, this is C++; avoid C-style casts	doOldDrawing(static_cast<float>(x - left),	             static_cast<float>(y - top));	return true;}// And then we draw everything in a loop:// See, 'tile[x][y].i' is simply x, according to our init logic, and similarly// for j. So it's redundant, and we're jumping through hoops to get information// that's right in front of us...if (is_even(x) == is_even(y) &&    tile[x][y].draw(camera.x, camera.y, SCREEN_WIDTH, SCREEN_HEIGHT)) {	tiles_drawn++;}


Quote:For confirmation, this is "is_even ()":


AARGH AARGH AARGH.

bool is_even ( int value ) {	return value % 2 == 0;}


All you need or want. The comparison is already a boolean, and it's already exactly the boolean you want to return. Don't split things up into cases. It suggests a complexity that isn't there.

Simplify.
Thanks for both of your answers.

Following Zahlmans advise worked very well, thanks a lot for your help there and the additional in-depth analysis.

This topic is closed to new replies.

Advertisement