Jump to content

  • Log In with Google      Sign In   
  • Create Account


Isometric Zones and map optimization.


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
16 replies to this topic

#1 hustlerinc   Members   -  Reputation: 169

Like
0Likes
Like

Posted 22 February 2012 - 08:54 AM

Hi, I'm creating a game in Javascript and im stuck.
Im using 2D isometric tiles.

What i want to know is how to divide the map up in zones, and more importantly only draw the tiles visible on the canvas.

My current drawMap function looks like this:
function drawMap(){
var tileW = 64;
var tileH = 32;

	for(i=0;i<map.length;i++){
		for(j=0;j<map[i].length;j++){
			var drawTile= map[i][j];
			var xpos = (i-j)*tileH + mapX;
			var ypos = (i+j)*tileH/2 + mapY;
			ctx.drawImage(tileImg[drawTile],xpos,ypos);
		
		if(i == playerX && j == playerY){
		  ctx.drawImage(charImg[0],xpos,ypos-(charImg[0].height/2));
		}
		
		if(i == xmouse && j == ymouse){
			ctx.fillStyle = 'rgba(110, 160, 255, 0.5)';
			ctx.beginPath();
			ctx.moveTo(xpos, ypos+16);
			ctx.lineTo(xpos+32, ypos);
			ctx.lineTo(xpos+64, ypos+16);
			ctx.lineTo(xpos+32, ypos+32);
			ctx.fill();
			}
		}
	}
}

Any examples of how i can adjust my code to improve it are appreciated.

Sponsor:

#2 FLeBlanc   Crossbones+   -  Reputation: 3101

Like
0Likes
Like

Posted 22 February 2012 - 09:19 AM

Given that an isometric camera is fixed in angle, what you can do is pre-calculate a visible volume, or view frustum, at game start then translate that view volume to center on the visible map center when the map is drawn. The volume can be used to test intersection of tiles with the view, and cull those that are not visible.

Now, of course, you can simplifiy this process immensely for an isometric game. JTippetts on this site wrote a journal post some time back about writing an isometric game using SFML, where he details one of the commonly used approaches. You can read that post here. In essence, you calculate where the corners of the screen intersect the ground and roof planes (the minimum and maximum vertical extents of the world) and from those you derive a set of visible tiles.

#3 hustlerinc   Members   -  Reputation: 169

Like
0Likes
Like

Posted 22 February 2012 - 09:58 AM

Yeah i saw that post earlier while searching but i dont understand much, since my knowledge of sfml = 0.
I will read it through though and make the best of it, but if someone has a Javascript example of the above it would be awesome.

#4 menyo   Members   -  Reputation: 407

Like
0Likes
Like

Posted 22 February 2012 - 12:58 PM

What do you mean by dividing them up?

You could store each map in a separate array, or an array inside an array. Then:

for (int sectionY = 0 ; sectionY < Ysections; sectionY++)
{
  for (int sectionY = 0 ; sectionY < Ysections; sectionY++)
  {
	for (int tileY = 0; tileY < YtilesInThisSection; tileY++)
	{
	  for (int tileY = 0; tileY < YtilesInThisSection; tileY++)
	  {
		//now draw this tile and add it's X and Y position bysectionY * (amount of tiles in section * tilewidth). Same goes for X.
	  {
	}
  }
}

For drawing only the tiles on screen, get the center of the screen relative to the map, then with your tilepick method you could calculate what tiles need to be drawn. Then just take this into account in your draw loop.

Current Project: TechnoFlux read all about it on my

DEV BLOG


#5 hustlerinc   Members   -  Reputation: 169

Like
0Likes
Like

Posted 22 February 2012 - 01:23 PM

What do you mean by dividing them up?

You could store each map in a separate array, or an array inside an array.


Instead of loading everything on the full world (planning a big world) and having to send calls from every online player with every action. To save bandwith ill split it into section (as i understand is a way to optimize).
Which of your methods is the most efficient one?

And for drawing only inside the viewport it wouldve been easy if it was square tiles, but since its isometric it gets complicated, and im only just learning JS, so im little stuck.
I've learned the technique and teory, but i cant figure out how to translate it into javascript.
(Yeah I might have taken on a big first project but i find this is the best way for me to learn :P just wish there was more info and tutorials.)

#6 menyo   Members   -  Reputation: 407

Like
0Likes
Like

Posted 22 February 2012 - 01:53 PM

Drawing only the ones on the viewport is the most efficient, but you might want to buffer some way outward so they get loaded faster if you player scrolls through the map. So i'd say just draw the tiles necessary on screen and load tiles a couple of screens off.

Yes, isometric maps complicates things like this a bit. But you could just draw the "diamond" just as large to fit the screen (so the outer corners will be drawn further outside the screen), this is less efficient but i don't think it would matter that much.

You could also use this to just make the loop, then for each tile calculate it's position and check if it's within the screen bounds. If so draw it, if not leave it.

Otherwise, what i have seen browser games do, is just that array method i described above. This will be less efficient but might give better results for a browser type game. Just check what map array the screen is on and load all the adjacent tile arrays. Then when the screen switches to another array load the new adjacent arrays.

Current Project: TechnoFlux read all about it on my

DEV BLOG


#7 hustlerinc   Members   -  Reputation: 169

Like
0Likes
Like

Posted 22 February 2012 - 02:40 PM

Drawing only the ones on the viewport is the most efficient, but you might want to buffer some way outward so they get loaded faster if you player scrolls through the map. So i'd say just draw the tiles necessary on screen and load tiles a couple of screens off.

Yeah thats what i was thinking, and i found what seems to be the most efficiant way to do it on FLeBlanc's link: http://www.gamedev.net/blog/33/entry-2250273-isometric-map-in-sfml?pg=3 I just cant translate it into javascript.

And here's a picture describing wich tiles to draw: http://www.gamedev.net/index.php?app=core&module=attach&section=attach&attach_rel_module=blogentry&attach_id=3487

But how do i translate that code into javascript, and even better to fit my drawMap() function?

#8 menyo   Members   -  Reputation: 407

Like
0Likes
Like

Posted 22 February 2012 - 02:48 PM

Well how you would write any code. Cut it all down in little steps and start coding. Can't help you much on javascript let alone your drawmap function so i'm not sure what your asking. If your not able to translate pseudo code into the language your programming in then it might be a step to high. You might want to try all this on a regular tilegrid if you haven't done so.

Otherwise, ask a more specific language because nobody here is going to write the code for you.

Current Project: TechnoFlux read all about it on my

DEV BLOG


#9 hustlerinc   Members   -  Reputation: 169

Like
0Likes
Like

Posted 22 February 2012 - 03:05 PM

Otherwise, ask a more specific language because nobody here is going to write the code for you.

I get that nobody is going to write the code for me, but i cant understand what the SFML example does, so I dont even know where to start.
I get that on even rows I'm supposed to do 16 tiles (based on my sizes) and odd rows 15.
I understand where to do -1/+1 X/Y, but what i cant grasp is how do i calculate the first tile to draw? In my case map[0] is top right corner, and X-axis goes down-left from there.
Should i start drawing from there? Tried to figure it out in the SFML example but I just get dizzy.

And there is no use first trying it in a normal 2d up-down array since then the math gets really simple and i would learn nothing.

#10 menyo   Members   -  Reputation: 407

Like
0Likes
Like

Posted 22 February 2012 - 03:18 PM

Interesting link it is. But i don't have much time....

I believe he is linking nodes to each cell when building the map.

He makes nodes like this on a diamond isometric map.

xx0xx
x012x
01234
x012x
xx0xx

An he uses these nodes to draw the map. Read the complete link, not just "where the magic happens" and you should be able to figure it out. I will look into this later since it's pretty interesting.

Current Project: TechnoFlux read all about it on my

DEV BLOG


#11 menyo   Members   -  Reputation: 407

Like
0Likes
Like

Posted 22 February 2012 - 03:57 PM

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.

Posted Image

I hope this helps you out...

Current Project: TechnoFlux read all about it on my

DEV BLOG


#12 JTippetts   Moderators   -  Reputation: 8351

Like
2Likes
Like

Posted 22 February 2012 - 09:16 PM

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.

#13 hustlerinc   Members   -  Reputation: 169

Like
0Likes
Like

Posted 23 February 2012 - 10:30 AM

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. Posted Image

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?

#14 menyo   Members   -  Reputation: 407

Like
0Likes
Like

Posted 24 February 2012 - 08:20 AM

// 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.

Current Project: TechnoFlux read all about it on my

DEV BLOG


#15 hustlerinc   Members   -  Reputation: 169

Like
0Likes
Like

Posted 24 February 2012 - 11:28 AM

// 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"?

#16 FLeBlanc   Crossbones+   -  Reputation: 3101

Like
0Likes
Like

Posted 24 February 2012 - 01:21 PM

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.

#17 hustlerinc   Members   -  Reputation: 169

Like
0Likes
Like

Posted 24 February 2012 - 01:39 PM

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.




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS