0
\$\begingroup\$

I am making a top-down view tiled map in canvas, where player is always in the middle of the screen, and map scrolls depending on where you go, like in moba games. I also want it to loop infinitely in every direction.

That means that when I cross the top border of the map I should end up at the bottom of the map, when I cross the left side of the map I should end up on the right side, etc. At the same time I only want the part visible on screen to be rendered.

Normaly I would just use for loop, but in cases when I am at the border, or corner of a map, and I need to display the same map, but the other end of it, it gets complicated with lots of nested loops.

I could make the map as math.matrix, and do multiple operations to fragment it and arrange so that it represents only the part visible on screen, like so:

enter image description here

So when I am at one of bottom tiles for example and I am at the center of the screen, I will need to fill the bottom with top tiles, and since the map is bigger than what is displayed on screen I would need to also extract only part of it to render.

But that would require a few math functions each draw, and I wonder if it is ok to do it.

It is hard to explain, especially in a language that is not my native, but I hope that someone will help me.

What is the right way to do it?

\$\endgroup\$
1
\$\begingroup\$

My answer contains the following assumptions:

  1. You want to get the the x,y-coordinates in order from the upper left to the bottom right, even if your viewport overlaps the map. If it overlaps, you want to look at the repeated map.
  2. Your viewport does not exceed the size of the map.

var mapDimensions = {x: 20, y: 20}; // Width of the map
var viewportDimensions = {x: 10, y: 10};

function iterateOverMap(viewportCenterPosition) {
"use strict";

var halfWidth = viewportDimensions.x / 2.0;
var halfHeight = viewportDimensions.y / 2.0;

var right = (viewportCenterPosition.x + halfWidth) % mapDimensions.x; // Positive overlap is solved using modulo
var left = (mapDimensions.x + (viewportCenterPosition.x - halfWidth)) % mapDimensions.x; // Modulo using negative numbers is strange sometimes, thats why I add mapDimensions.x before.

var up = (mapDimensions.y + (viewportCenterPosition.y - halfHeight)) % mapDimensions.y;
var down = (viewportCenterPosition.y + halfHeight) % mapDimensions.y;


for(var y = up; y != down; y = (y + 1) % mapDimensions.y) {
  for(var x = left; x != right; x = (x + 1) % mapDimensions.x) {
    //Here you got your x,y position  
    console.log(x+" "+y);
  }
  console.log("\n");
}
}

var center = {x: 19, y: 19};
iterateOverMap(center);

Does this make sense to you?

\$\endgroup\$
  • \$\begingroup\$ This is not the best solution as you might want to have a viewport bigger than your map. In this case you will need to adjust the code a little bit. (you would basically loop over the viewport coordinates and map them onto the map coordinates using modulo operations) \$\endgroup\$ – TOAOGG May 31 '16 at 7:27
  • \$\begingroup\$ This is really clever, I honestly didn't even know you could do that with for loop, genious... but... Maybe I am not getting something, but this loop does not store the current position on screen. What I mean is, that it will reset to the first tile in map, when it reaches the end of the map, which is what I am aiming for, but it will also reset position on screen and start to overwrite tiles in the beginning, if I use code like this: createTile(map[ym][xm], (xm * tileSize), (ym * tileSize)); \$\endgroup\$ – user2701428 May 31 '16 at 17:24
  • \$\begingroup\$ Nevermind, figured it out. Just added more variables to the loop. \$\endgroup\$ – user2701428 May 31 '16 at 19:19
  • \$\begingroup\$ Ahh nice, sorry I was not available at this time :) \$\endgroup\$ – TOAOGG Jun 1 '16 at 7:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.