0

I'm making an isometric game and I'd like to be able to determine the selected tile by the mouse position.

I've done this before using regular tiles, but isometric is much more complicated. I've had many attempts at this before and I've had moderate success, though my code wasn't pleasant to look at. And I'd imagine anyone seeing the code would think to themselves that I was just 'winging it' or 'brute-forcing it'.

My earlier attempts look something like this.

public static Vector2 ToIsometric( int x , int y )
{
    float selectedTileX = ( y * 32.0f ) + ( x * 16.0f );
    float selectedTileY = ( y * 16.0f / 2 ) - ( x * 16.0f / 2 );

    return new Vector2 ( selectedTileX , selectedTileY );
}

Where x and y represent the mouse position on screen. 16.0f represents the height of each tile and 32.0f represents the width of each tile. The graphic below doesn't use these values but hopefully gives helps explain my goal of determining the index of the jagged isometric tile being moused over.

enter image description here

ElChiniNet
  • 2,778
  • 2
  • 19
  • 27
Anthony
  • 301
  • 1
  • 2
  • 13
  • See https://stackoverflow.com/questions/9658980/how-can-i-convert-x-y-position-to-tile-x-y-for-isometric-tile?noredirect=1&lq=1 : looking at your code it looks like you need to divide rather than multiple in your brackets ? – auburg Mar 13 '19 at 14:54
  • I'll have to get back to you on that. Until then, thanks for sharing that link, I'm eager to try their solution. – Anthony Mar 13 '19 at 15:53

1 Answers1

0

A long time since this question was asked, but it was not answered. So I'll try to bring a possible solution to it.

I know that you are not working with JavaScript, but in this case, your question is related to the maths needed to make that calculation, and due that SO provides good JavaScript snippets, this language is perfect to see the results of the calculations.

Check this snippet, it is a basic tile-grid, and the tiles are selected depending on the mouse coordinates (I'll use this snippet as a base to show you the final calculations):

const floor = Math.floor;
const divs = document.querySelectorAll('.container div');
const tileSize = 40;

const tiles = Array.prototype.reduce.call(divs, (a, t, i) => {
  const ai = floor(i / 3);
  return ((a[ai] = a[ai] || []), (a[ai][i % 3] = t), a);
}, []);

const unselect = () => divs.forEach(d => d.style.background = '');
const select = (r, c) => tiles[r] && tiles[r][c] && (tiles[r][c].style.background = 'red');

document.addEventListener('mousemove', (e) => {  
  const row = floor(e.pageY / tileSize);
  const col = floor(e.pageX / tileSize);
  unselect();
  select(row, col);  
});
body {
  margin: 0;
  padding: 0;
}

.container {
  border: 1px solid black;
  display: flex;
  flex-wrap: wrap;
  height: 120px;
  width: 120px;
}

.container * {
  background: white;
  border: 1px solid black;
  box-sizing: border-box;
  height: 40px;
  width: 40px;
}
<div class="container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

Before starting with the calculations, take a look at this post that I wrote some time ago, so you will be able to check all the maths involved in an isometric projection. As you can see, in an isometric projection, taking a as the width and height of the tiles before projection, the width of the isometric tile is equal to √3 * a, and the height is equal to a.

Having said that, first of all, let's try to do the opposite of what you want to: giving an isometric point, let's try to convert it to the real x and y coordinates on the screen. Take a look at the next image:

isometric projection

It is possible to assure several things:

  1. In the isometric coordinates (Ix and Iy), the point a{1, 1} and the point b{2, 2} have the same y position on the screen. So, with equal Ix and Iy, the y coordinates on the screen remain invariable.
  2. Ix coordinates decrease the y coordinates and Iy coordinates increase them.
  3. The x position of the point b{2, 2} is two times the x position of the point a{1, 1}.
  4. Ix and Iy coordinates always increase the x coordinates.

So, with all this information, let's try to create a conversion formula from {Ix, Iy} to {x, y}:

x = (Ix + Iy) * √3 / 2 
y = (Ix - Iy) / 2

So, taking this formula as a starting point, we can try to calculate the isometric coordinates (Ix and Iy) from the x and a y coordinates:

x = (Ix + Iy) * √3 / 2 
2 * x = (Ix + Iy) * √3
2 * x / √3 = Ix + Iy
Ix = 2 * x / √3 - Iy

And on the other hand:

y = (Ix - Iy) / 2
2 * y = Ix - Iy
Iy = Ix - 2 * y

So, it is possible to say:

Ix = 2 * x / √3 - Iy
Ix = 2 * x / √3 - (Ix - 2 * y)
Ix = 2 * x / √3 - Ix + 2 * y
2 * Ix = 2 * x / √3 + 2 * y
Ix = x / √3 + y

So, let's see these calculations in live using the same base snippet, but this time with the tiles in isometric mode:

In the next snippet, the isometric coordinates are moved 60px down in the y coordinates to make the entire graphics visible on the screen, so, it is needed to subtract that amount from the result.

const floor = Math.floor;
const divs = document.querySelectorAll('.container div');
const tileSize = 40;

const tiles = Array.prototype.reduce.call(divs, (a, t, i) => {
  const ai = floor(i / 3);
  return ((a[ai] = a[ai] || []), (a[ai][i % 3] = t), a);
}, []);

const unselect = () => divs.forEach(d => d.style.background = '');
const select = (r, c) => tiles[r] && tiles[r][c] && (tiles[r][c].style.background = 'red');

document.addEventListener('mousemove', (e) => {
  const x = e.pageX;
  const y = e.pageY;
  const Ix = x / Math.sqrt(3) + y - 60;
  const Iy = Ix - 2 * (y - 60);  
  const row = floor(Ix / tileSize);
  const col = floor(Iy / tileSize);
  unselect();
  select(row, col);
});
body {
  margin: 0;
  padding: 0;
}

.container {
  border: 1px solid black;
  display: flex;
  flex-wrap: wrap;
  height: 120px;
  transform: translateY(60px) rotate(-60deg) skewY(30deg) scaleX(.866025);
  transform-origin: top left;
  width: 120px;
}

.container * {
  background: white;
  border: 1px solid black;
  box-sizing: border-box;
  height: 40px;
  width: 40px;
}
<div class="container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>
ElChiniNet
  • 2,778
  • 2
  • 19
  • 27