# Calculate max tiles for screen size?

This topic is 2174 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hey

I have a isometric map in a window which alters in size depending on user's settings.

But i want to limit only how ever many fit the screen. Lets say screen width and height is: 1200 by 600 pixels. Isometric tiles are 64 by 32 pixels.

Now the obvious way is width / 64 & height / 32 - then multiply the answers together. The above only works if its a birds eye view and not an isometric.

So i was wondering what is the correct way to calculate the total max tiles that can display for isometric view?

##### Share on other sites
Don't use integer math (or make sure you are rounding up) and double your result. Maybe add another row/column depending on your offset to be safe. Unless you want your result to be absolutely precise.

##### Share on other sites
I'm sure there's a sound mathematical correlation based off of the angle of the camera and height from the map, unfortunately I'm not aware of it.

The easiest way I can think of is to do ray-to-plane calculations from your view frustum edges to the ground any time your camera changes in height or angle.
From there it's fairly trivial to calculate the visible area, converting that into a number of visible tiles. Obviously you'd want to cache your results and do this as infrequently as possible, but the upside is that it allows you to compensate for a non-flat ground. I'd expect you could construe information such as camera-to-tile offsets that would allow you to move along the X/Z plane without having to recalculate the camera-to-tile offset and visible tile range.

Hopefully this helps, best of luck!

##### Share on other sites

Don't use integer math (or make sure you are rounding up) and double your result. Maybe add another row/column depending on your offset to be safe. Unless you want your result to be absolutely precise.

Im not sure this really answers or explains much =/

@SeiryEnder

Thankfully my camera is always a fixed angle its a fixed isometric game so that saves some of the complications! But the maths is hard

##### Share on other sites
It seems like it would be more complicated, but it's simply:

[source]
rows = (screen.height / tile.height) + 1
cols = (screen.width / tile.width) + 1

max_tiles = 2 * r * c
[/source]

The more important question is what exactly you hope to achieve here -- In a single-player game the screen size doesn't really matter if the player isn't compared to others -- if its multiplier, or there is a competitive element, then showing more tiles is more information, giving some players a competitive edge. To balance that out you'd need to ensure that all players only see the same number of tiles, and also have the same screen shape -- but you get to choose those details, you wouldn't need to calculate them.

Its an interesting math exercise, but I don't see how you intend to leverage this information.

##### Share on other sites
Loading more tiles that you can visually see on you're screen is a waste of resources. As screens vary in size this is the only way to improve performance.

Your solution doesn't work as it only loads a full diamond but there is still 4 corners of the map that does not cover the screen =/.

##### Share on other sites
I hope you didn't expect that calculating the number of visible tiles and then loading that number of tiles would do any good and result in anything meaningful. How to only LOAD the visible tiles would first require to know how you are indexing your tiles and is a completely different beast than just calculating how many tiles fit into a rectangle.

The best way to understand iso is to grab a piece of paper and a pen or at the very least, draw the x/y coordinates on every tile.

You should also carefully ask yourself if you REALLY want disc activity every single time someone is scrolling around the map. Unless you have a really huge world, not loading the whole thing is just wasted effort. How are you going to update the parts that aren't visible? How are enemies that are off the screen doing their movement and AI? If the view jumps to a different part of the map, are you only THEN starting to actually load it?

##### Share on other sites
Well we have gone a bit off topic here but as your curious. HTML5 and JavaScript simply is not as powerful as C++ etc... these optimizations are v.much needed.

I have my game engine complete i just need to work out how to limit the tiles loading based on screen size. A formula which i have not been able to work out - nor has any one else according to google.

(Saying that ... no one has thought to do it ) which seems bonkers because it would mean you could have unlimited sized map with no drop in performance.

##### Share on other sites
I have my game engine complete i just need to work out how to limit the tiles loading based on screen size.

I think that there is some confusion from when you say "loading".
You may instead mean "render culling" which is to say that you only draw tiles that are within visible range of the camera.

Disk reads are among the slowest operations you can do on a computer due to physical hardware limitations. You want to avoid them as much as possible. In this respect, it is advantageous to load all but the largest tile maps into memory ahead of time. Having your tilemap sitting in RAM doesn't really hurt you very much, and is fairly language independent. It is just as advantageous in Java as it is in C++. The only time this is an exception is in a very memory-constrained system, such as a gameboy.

What you gain from render culling is processing speed. You don't want to render tiles that you can't see. That's just wasted FPS.

##### Share on other sites

Loading more tiles that you can visually see on you're screen is a waste of resources. As screens vary in size this is the only way to improve performance.

It makes a bit more sense now -- it sounds like you want to do something like streaming tiles into view, like google-maps does when you scroll around? In any event, like I said earlier, this has *nothing* to do with the exact number of tiles that you will see on screen -- if you wanted to determine it programmatically (say, so that the user can resize the screen freely -- though you have to be careful that this doesn't break the competetive balance, as I said), then you can get the number as a result of the necessary calculations, but the number of tiles is not the goal itself.

Your solution doesn't work as it only loads a full diamond but there is still 4 corners of the map that does not cover the screen =/.
[/quote]

I'm pretty certain that it works, actually -- if you want a rough order of magnitude sanity check, think about it like this: Each 64x32 pixel area that contains 1 isometric tile has enough area to support 2isometric tiles of identical size -- that is, the empty corners of a single tile add up to the area of a whole tile -- and further, it's not 2 because your tile is twice as wide as it is tall, but because of the diamond shape -- it would hold for 32x32 "iso" tiles, as well as it would for 48x32, 96x32, or 427x9. If we imagine this occurring in-game (that is, repeated in a large grid), we can say that the average number of tiles contained per 64x32 area is, in fact, 2 since tiles that partially occupy one of these imaginary rectangles are shared with the neighboring imaginary rectangles. If we shift the view slightly up/down or left/right we need an additional row.length+1 or col.length+1 to cover areas that would otherwise be empty due to the move, but if we continue to shift the screen, the opposite edge will go off-screen before we need a new row or column -- that's why its rows+1 and columns+1, and not +2.

I think the more likely error you are making is that you are taking this number and using that to define the rectangular extents of your map data that you will draw, which is being mapped into a diamond-like shape in your isometric renderer -- if you do that, you would end up clipping the corners of your view. What you need to do is map the extents of the screen backwards into your map data -- so you'd end up sampling your map data in a diamond-like shape, not rectangular.

Rectangular sampling of the map data is actually a fairly complex geometry problem due to the fact that the screen could take on any shape (say, tall and narrow, or short and wide) -- it's certainly do-able, but those kinds of pathological cases result in overdraw that will be several times larger than what's drawn on screen. If you're really concerned about performance, you want to only sample the map tiles that will be drawn.

Do you get what I mean by that?

##### Share on other sites
Actually, I've thought of a way to calculate a rectangular sampling that's fairly easy, although it doesn't address the overdraw problem. Here's the method:

[source]
x = screen.width
y = screen.height

steps = 0

while(x >= 0 || y >= 0)
{
x -= tile.width
y -= tile.height

++steps
}
[/source]

steps is the number of tiles you need to sample on either side of the center tile(wherever the view is focused) -- so, you'd sample the rectangle from (focus.x - steps, focus.y - steps) to (focus.x + steps, focus,y + steps).

I still recommend the above though, if performance is really of great concern.

##### Share on other sites
Wait wait...that code snippet confuses me..

Isn't that loop going to go on infinitely ?

I tried:

 var cx = Math.round(canvas.width / 2); var cy = Math.round(canvas.height / Math.round(canvas.width/canvas.height)); i = cx-offset_x - Math.round(canvas.width/2) - curleft; j = cy-offset_y - curtop; ix = Math.round((i + j * 2) / grid); jy = Math.round((j * 2 - i) / grid); rows = Math.round((canvas.height / grid) + 1 ); cols = Math.round((canvas.width / grid) + 1); for (i=ix-rows; i < ix+rows; i++){ //horizontal for (j=jy-cols; j < jy+cols; j++){ // vertical //draw it all } } 

But i believe the maths is incorrect =/ As it does not totally cover the canvas.

##### Share on other sites
No, it'll break when both x and y are less than 0 (aka somewhere *both* above and left of the upper-left corner of the screen.) -- there is potential for a bug that I overlooked though, it relies on x and y being signed values -- if they are unsigned it would wrap around and indeed go on forever.

As for your code, I have no idea what's going on because you're using 5 symbols that aren't defined in your code (grid, curleft, curtop, offset_x and offset_y)

[source]
var cx = canvas.width;
var cy = canvas.height;

var ct = 0;

while(cx >= 0 || cy >= 0)
{
cx = cx - tile_width; // assuming tile_width is defined
cy = cy - tile_height; // assuming tile_height is defined

ct = ct + 1;
}

ix = [tile_with_focus.x]; // I don't know how you calculate this
jy = [tile_with_focus.y]; // I don't know how you calculate this

for (i=ix-ct; i < ix+ct; i++) //horizontal
{
for (j=jy-ct; j < jy+ct; j++) // vertical
{
//draw it all
}
}
[/source]

My javascript is rusty so their might be minor tweaks needed, and you only actually need to calculate 'ct' when the canvas changes shape.

##### Share on other sites
What is meant to be stored in [color=#000000][font=Consolas,]tile_with_focus.y ?[/font]

[color=#000000][font=Consolas,][font=arial,helvetica,sans-serif]That way i can set it to what it needs to be.[/font][/font]
[color=#000000][font=arial, helvetica, sans-serif]

### On my example grid is 64 curleft and curtop is a hotfix to make calculations relative to canvas and not the entire website page. offset_x and offset_y was pixel offset (for scrolling purposes).[/font]

##### Share on other sites
tile with focus is whatever tile appears in the center of the canvas.

##### Share on other sites
Ah i see... ok that means my calculation is wrong =/

Because using these numbers:

ix = 5;
jy = 55;
ct = 19;

Lets say in my loop i got:

 for (i=ix-ct; i < ix+ct; i++){ //horizontal for (j=jy-ct; j < jy+ct; j++){ //vertical 

So on i as 0 and j as 36 (which is the first render-able tile the pixel position is calculated like this:

The isometric tiles are 64by64 but the graphics in the image tiles are only 64 by 32.
 var x = (i-j)*(img[0].height/2) + (canvas.width/2)-(img[0].width/2); var y = (i+j)*(img[0].height/4); 

0:36 for I:J how ever causes their position to be:
-584:572 (pixels)....this is well off screen for X so you don't seen anything load...

That is before i add the offset_x and y (scroll offset is 0 on initial load so thats why don't have to worry about it).