Isometric Zones and map optimization.

Started by
15 comments, last by hustlerinc 12 years, 1 month ago
I have drawn a little example here. On the left you have your way of coordinating your sprites. On the right it's the way your link suggested. I was in a little haste to draw this and accidentally switched the X,Y on the right one.

isoexample.png

I hope this helps you out...
Advertisement
The heart of drawing the map in that post is this bit of code:

void IsometricMap::draw(sf::RenderWindow *win)
{
// Set view
sf::View view=win->GetView();
// Reverse project center
sf::Vector2f center=WorldToScreen(sf::Vector2f(m_centerx,m_centery));
view.SetCenter(center);
win->SetView(view);

// Reverse-project top-left corner
sf::Vector2f viewsize=view.GetSize();
sf::Vector2f topleft=ScreenToWorld(sf::Vector2f(center.x-viewsize.x/2.0f, center.y-viewsize.y/2.0f));
int sx=(int)(topleft.x/(float)m_nodesize);
int sy=(int)(topleft.y/(float)m_nodesize);

// Move start location up and left two nodes to get a little padding. (subtract 2 from sx
sx-=2;

// Calculate how many nodes across to draw
// A node's total width on-screen is calculated as 4*nodesize
int num_nodes_across=(int)viewsize.x / (m_nodesize*4) + 4; // Pad out the end by drawing 4 extra nodes

// Calculate how many rows to draw
// A node's total height on screen is calculated as 2*nodesize. Also, need to fudge it
// a little bit by adding a value that approximates the maximum cell height to the size of the
// viewport.
int num_rows=(((int)viewsize.y+512) / (m_nodesize*2))*2;

// Update lighting
m_lightmap.updateRegion(sx,sy,num_nodes_across,num_rows);

// Drawing proceeds as thus:
// We begin at some starting node and proceed across the row. At each step, we increment x and decrement y
// to move to the next node.
// When a row is done, we move to the next row. This is done by:
// If the current row is "even", then we move to the next row by incrementing x. If the current row is odd, we
// move to the next row by incrementing y instead.
// On even rows, we draw num_nodes+1 nodes, else we draw num_nodes nodes.
int rowincx=1, rowincy=0;
int drawnodes=num_nodes_across+1;

int nodex=sx, nodey=sy;
for(int row=0; row<num_rows; ++row)
{
if (row & 1)
{
// Odd row
rowincx=0;
rowincy=1;
drawnodes=num_nodes_across;
}
else
{
rowincx=1;
rowincy=0;
drawnodes=num_nodes_across+1;
}


for(int node=0; node<drawnodes; ++node)
{
// Calculate cell coords
int cellx=nodex+node;
int celly=nodey-node;
if(cellx>=0 && cellx<m_width && celly>=0 && celly<m_height)
{
sf::Color color=m_lightmap.getLightValue(cellx,celly);
m_nodes[celly*m_width+cellx].drawFloors(win,color);
}
}

nodex=nodex+rowincx;
nodey=nodey+rowincy;
}


It's not difficult to understand. The heart of it relies on the two functions, ScreenToWorld and WorldToScreen that can convert coordinates between the two different coordinate spaces. By converting the coordinates representing the 4 corners of the screen to world coordinates the result you end up with is the box defining the visible space. This box will be oriented at 45 degrees relative to the cells. Once you have the box, you start iterating rows, starting at the top-left corner of the visible box and proceeding across and down to the bottom right corner. It's very much language and platform agnostic, meaning that the fact I used SFML to draw stuff with is very irrelevant. I do use the SFML views in there, but that is just SFML's way of specifying screen coordinate projection. Basically, a SFML View is just a rectangle in Screen Coord space that defines what sub-rectangle of Screen Coord space is visible on the screen.

I have drawn a little example here. On the left you have your way of coordinating your sprites. On the right it's the way your link suggested. I was in a little haste to draw this and accidentally switched the X,Y on the right one.

I hope this helps you out...

Does this mean i have to rearrange my coordinates? Or is it possible to do with my way of doing it if i change the theory a little?


The heart of drawing the map in that post is this bit of code:

It's not difficult to understand. The heart of it relies on the two functions, ScreenToWorld and WorldToScreen that can convert coordinates between the two different coordinate spaces. By converting the coordinates representing the 4 corners of the screen to world coordinates the result you end up with is the box defining the visible space. This box will be oriented at 45 degrees relative to the cells. Once you have the box, you start iterating rows, starting at the top-left corner of the visible box and proceeding across and down to the bottom right corner. It's very much language and platform agnostic, meaning that the fact I used SFML to draw stuff with is very irrelevant. I do use the SFML views in there, but that is just SFML's way of specifying screen coordinate projection. Basically, a SFML View is just a rectangle in Screen Coord space that defines what sub-rectangle of Screen Coord space is visible on the screen.

I will read through all pages of your tutorial and try to get a better understanding.
Right now it's pretty much like trying to read about the french revolution in chinese to be able to explain it in english. smile.png

But I think I get the theory part and what im trying to do.
When you talk about different coordinate spaces, I guess you just have 1 saved, like the complete map, then with code you translate it to the viewport? where it gets new coordinates?
// Calculate how many nodes across to draw
// A node's total width on-screen is calculated as 4*nodesize
int num_nodes_across=(int)viewsize.x / (m_nodesize*4) + 4; // Pad out the end by drawing 4 extra nodes

// Calculate how many rows to draw
// A node's total height on screen is calculated as 2*nodesize. Also, need to fudge it
// a little bit by adding a value that approximates the maximum cell height to the size of the
// viewport.
int num_rows=(((int)viewsize.y+512) / (m_nodesize*2))*2;
You could save these coordinates with the tile class but that would be less efficient. You can also use the formula on the fly just before you start drawing each loop.



Seriously, i have done some quite some testing with regular and normal maps and never touched C++ and the code is pretty clear. As with all, expiriment and be comfortable with the basics first. So like i sugested a view posts above, go do some regular isometric maps and just drawing them a bit less ineficient since i don't think efficiency should be your priority now but getting this game finished. Then later when you understand isometric maps better you this article should be easier to understand.

// Calculate how many nodes across to draw
// A node's total width on-screen is calculated as 4*nodesize
int num_nodes_across=(int)viewsize.x / (m_nodesize*4) + 4; // Pad out the end by drawing 4 extra nodes

// Calculate how many rows to draw
// A node's total height on screen is calculated as 2*nodesize. Also, need to fudge it
// a little bit by adding a value that approximates the maximum cell height to the size of the
// viewport.
int num_rows=(((int)viewsize.y+512) / (m_nodesize*2))*2;
You could save these coordinates with the tile class but that would be less efficient. You can also use the formula on the fly just before you start drawing each loop.

Seriously, i have done some quite some testing with regular and normal maps and never touched C++ and the code is pretty clear. As with all, expiriment and be comfortable with the basics first. So like i sugested a view posts above, go do some regular isometric maps and just drawing them a bit less ineficient since i don't think efficiency should be your priority now but getting this game finished. Then later when you understand isometric maps better you this article should be easier to understand.

I get what you're saying but in order to even continue my game i need to be able to increase the map size.
I can walk around, edit map, collision detection etc, but since i cant even load 10k tiles without severly dropping the FPS on my laptop i dont see the point in moving on.
Editing the code later on will just be harder once its filled with other code.

But im thinking maybe i dont have to do 2 different coordinate spaces, what if i start on one coordinate, then with simple maths (-1x +1y for so and so many tiles while rows are less than...) and store those tiles in a new array, and use this array to draw?
This would remove the tiles i dont need while still using the same coordinates.

Would this be doable? If so is it reasonable? Maybe this is what you meant by "using the formula on the fly"?
The idea of 2 different coordinate spaces is simple. You have the map space that is the "world" everything lives in. The entrance to the Cave of Dark Doom lies at (65,37). The Mountain of Cliched Madness is at 86,119. And so on.

Then you have screen space which is what is drawn on the screen. When the video mode is first set in SFML with a default view, the screen is rendering the area of screen space from (0,0) to (640,480) (or whatever video resolution you set). This view can be changed to display any screen-sized rectangle of the theoretically infinite screen space. Think of screen space as a big, flat bulletin board of which you can only see a 640x480 (or whatever) sized chunk at any given time, by way of a window that you can move around to anywhere on the bulletin board.

Somehow, you need to convert world coordinates to screen coordinates for every object and sprite in the world. If you set up routines to easily convert from Screen to World and back again, it becomes trivial to determine the sub-region of the world to draw for any particular view. Just take the current View, or visible region of the Screen space, and convert the corners of that view to World space, make note of the converted coordinates, and use those to iterate the sections of map that are visible, converting those visible objects to their Screen-space coordinates in order to draw them on-screen.
I've found a Javascript game with the same theory as the one JTippets done, with cells, map and screen coordinates and everything in it.
Even the same words for the functions as JTippets used only that its Javascript.
Its a few 1000 lines so it will take some time to dissect the code, but i think it will help me get a better understanding.

This topic is closed to new replies.

Advertisement