Isometric tilled-map scrolling
I have a problem here. I'm s开发者_开发技巧earching answer for this for some time now, trying to get into solution by myself, but with no results. This question was probably asked many times, but everywhere I find anything about it, there is always something, that is not clear to me. So: I'm writing a 2D game using a SDL+OpenGL. The game is showed in isometric view. Player sees the map that consists from a tiles, let's say, a 15x15 tiles (2D array). It looks like this: http://i.imgur.com/PVJe4.png
X and Y are indexes of each tile, but beside this, those tiles have their own X and Y coordinates(north vertex, marked with green dot for [0][0] tile), in which they are beeing drawed.
My problem is, that I want to add scrolling for this. If this map would be 100x100 tiles, it would by drawn very slow, becouse all tiles drawed outside the screen. I don't want it to be done.
Let's look, for example, on this: http://imgur.com/vo3wg
Blue rectangle is my screen (800x600, not resizable). The overall map dimensions are 8000x6000, and tiles are drawed as on screen. I know, I need only draw those tiles that are marked on green, but my problem is:
How do I determine which tiles (I mean, with which indexes) have to be drawed?
I appreciate all attempts of helping me.
but whatever technique I use, it just... can't be left for rendering something, that is not showed on screen.
Why not? Entire triangles that are off-screen are culled, so all you lose is the vertex transfer and transformation time. Yes, you should take reasonable steps to ensure that you aren't sending stuff you could easily cull out, but since you're just drawing a simple 2D tilemap, it's not going to be a performance problem. Even for low-end hardware.
This is why premature optimizations are wrong. Because you're spending time worrying about something that quite simply is not a problem. Optimize when something is actually slow, not when it might be slow.
In general, you should pick groups of tiles to draw. If one is visible, you draw all of them. That way, you're not spending lots of time selecting which tiles specifically to draw. You can then optimize the data for transfer (ie: putting each group into a buffer object) and just draw the whole group in one go.
I'm answering my own, becouse I can't post more than 2 links (as I am new), and I cant post ANY images.
I made some tests:
for 100x100 it runs nice, for 150x150 too, for 200x200 it drops right down to ground, and for 500x500 its totally disaster: http://imgur.com/msAx4l&EJ9Qw&iKXFZ&ybFt0 (4 screens in gallery)
well, I was thinking on solution that doesnt require any checking, but just some mathematical equations. For example, in my other game (written long time ago) with squre tiles, not diamond, it is very simple to determine indexes of drawed tiles. It look's like this: http://i.imgur.com/y9KUv.png
Drawing procedure for it:
//CameraXposition and CameraYposition is position of left-top corner of camera on map.
//Single tile height and width is 100, so if CameraXposition is 1456, then I start drawing from
//tile with index of 14 ((int)(1456/100) == 14).
//CAMERA_WIDTH is 800 and CAMERA_HEIGHT is 600 (screen resolution).
for(int x = (int)CameraXposition/100; x < ((CameraXposition+CAMERA_WIDTH)/100); ++x)
{
for(int y = (int)CameraYposition/100; y < ((CameraYposition+CAMERA_HEIGHT)/100); y++)
{
//Draw(tiles[x][y]);
}
}
Here, in isometric view, it's not that simple, but I don't know how to determine this starting indexes and ranges of tiles.
Here is my current drawing code:
void GraphicEngine::Render(Map &m)
{
//camX and camY isn't coordinates of camera, they are coordinates of
//starting point (everything is drawed according to that point)
//it changes as I move mouse.
//zoomFactor is basically a zoom variable, it's float and its minimum is 0.5 and maximum is 1.5.
//It changes as I scroll my mousewheel. The smaller it is, the smaller is everything that's drawed,
//so more tiles can be drawed on screen (becouse they are smaller)
//Not very good, but works.
glTranslatef(camX*zoomFactor, camY*zoomFactor, 0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
glBegin(GL_QUADS);
for(int i = 0; i < m.get_width(); ++i)
{
for(int j = m.get_height()-1; j>=0; --j)
{
glTexCoord2f(0, 0);
glVertex3f((m.get_fields()[i][j]->get_pX()-30)*zoomFactor, (m.get_fields()[i][j]->get_pY()+15)*zoomFactor, 0);
glTexCoord2f(1, 0);
glVertex3f((m.get_fields()[i][j]->get_pX())*zoomFactor, (m.get_fields()[i][j]->get_pY())*zoomFactor, 0);
glTexCoord2f(1, 1);
glVertex3f((m.get_fields()[i][j]->get_pX()+30)*zoomFactor, (m.get_fields()[i][j]->get_pY()+15)*zoomFactor, 0);
glTexCoord2f(0, 1);
glVertex3f((m.get_fields()[i][j]->get_pX())*zoomFactor,( m.get_fields()[i][j]->get_pY()+30)*zoomFactor, 0);
}
}
glEnd();
glLoadIdentity();
}
I would choose a compromise solution between rendering all and checking every tile.
Find corner tiles in screen coords. That will give you minimum and maximum X:Y positions of visible tiles. Now render only those tiles within these bounds. GPU will clip unseen tiles, but you would not need to send all 8000x6000 tiles to render, but only a portion of it.
As noted above, premature optimizations are wrong, so unless your map is at least 800x600 tiles I would try to search a bottleneck elsewhere elsewhere first. E.g. batching tiles by texture into display-lists or VBO seems like a better idea.
精彩评论