Isometric Screen to Map

2019-03-30 02:04发布

问题:

I'm trying to figure out how I can get the correct "active" tile under the mouse when I have "ramp" and +1 height tiles (see picture below).


When my world is flat, everything works no problem. Once I add a tile with a height of say +1, along with a ramp going back to +0, my screen -> map routine is still looking as if everything is "flat".

In the picture above, the green "ramp" is the real tile I want to render and calculate mouse -> map, however the blue tile you see "below" it is the area which gets calculated. So if you move your mouse into any of the dark green areas, it thinks you're on another tile.

Here is my map render (very simple)

canvas.width = canvas.width; // cheap clear in firefox 3.6, does not work in other browsers
for(i=0;i<map_y;i++){
    for(j=0;j<map_x;j++){
        var xpos = (i-j)*tile_h + current_x;
        var ypos = (i+j)*tile_h/2+ current_y;

      context.beginPath();
      context.moveTo(xpos, ypos+(tile_h/2));
      context.lineTo(xpos+(tile_w/2), ypos);
      context.lineTo(xpos+(tile_w), ypos+(tile_h/2));
      context.lineTo(xpos+(tile_w/2), ypos+(tile_h));
      context.fill();

    }
}    

And here is my mouse -> map routine:

ymouse=( (2*(ev.pageY-canvas.offsetTop-current_y)-ev.pageX+canvas.offsetLeft+current_x)/2 );
xmouse=( ev.pageX+ymouse-current_x-(tile_w/2)-canvas.offsetLeft );
ymouse=Math.round(ymouse/tile_h);
xmouse=Math.round(xmouse/(tile_w/2));

current_tile=[xmouse,ymouse];

I have a feeling I'll have to start over and implement a world based map system rather than a simple screen -> map routine.

Thanks.

回答1:

Your assumption is correct. In order to "pick" against world geometry, your routine needs to be aware of the world (and not just the base-level tile configuration). That is, without any concept of the height of the tiles near the one that is currently picked (by your current algorithm), there's no way to determine whether a neighboring tile (or one even further away, depending on the permitted height) should be intercepted by picking ray.

You've got the final possible point of your picking ray, already. What remains is to define the remainder of the ray, in world-space, and to check that ray for intersections with world geometry.



回答2:

If, like the picture, your view angle is always 45 degrees and always from the same direction, your mouse -> map routine could use an algorithm something like:

  1. calculate i,j of tile as you're doing currently (your final value of xmouse, ymouse)
  2. look up height and angle of tile at i,j
  3. given the height and angle, does this tile intersect the picking ray? If so, set lasti, lastj = i, j
  4. increment/decrement i,j one step diagonally toward viewer
  5. have we fallen off the edge of the map? If so, return lasti, lastj. Otherwise go back to 2.

Depending on the maximum height of a tile, you might have to check only 2 tiles, rather than going all the way to the edge of the map.

3 is the tricky part, and depends on your world geometry. Draw some triangles and you should be able to figure it out. Or you might try looking at the function intersect_quadrilateral_ray() here.