Find which tile was clicked in a isometric, staggered column system

2k views Asked by At

My grid

tileWidth = 64px
tileHeight = 128px

(the image itself is 128px, though the actual diamond is 32px tall and 64px wide)

enter image description here

As you can see, I have a staggered grid system; however, I have spent the last two hours trying to think of a system where I can take the mouse coordinates relative to the canvas and figure out what tile was clicked (obviously within a diamond-shaped field).

For example, if I clicked tile 21, 26 -- how would I figure that out in the program?

Any pointers to get working in the right direction would be greatly appreciated. Thanks!

1

There are 1 answers

2
AudioBubble On BEST ANSWER

If you know the center position of the cell, which of course you do since they are rendered, you simply normalize the coordinate against the cell to find out if it is inside:

var dx = Math.abs(x - cellCenterX),
    dy = Math.abs(y - cellCenterY);

if (dx / (cellWidth * 0.5) + dy / (cellHeight * 0.5) <= 1) { /* is inside */ };

Here is a full demo:

var cw = 64,
    ch = 32,
    cells = [],
    maxH = 10,
    maxV = 5,
    toggle = true,
    
    canvas = document.getElementById("canvas"),
    ctx = canvas.getContext('2d');

// Using a cell object for convenience here:
function Cell(posX, posY, x, y, w, h) {
  this.posX = posX;            // some id....
  this.posY = posY;            // some id....
  this.x = x;                  // cells top position
  this.y = y;
  this.w = w;                  // width and height of cell
  this.h = h;
  this.centerX = x + w * 0.5;  // abs. center of cell
  this.centerY = y + h * 0.5;
}

// draw this cell:
Cell.prototype.render = function(ctx, color) {
  ctx.beginPath();
  ctx.moveTo(this.centerX, this.y);
  ctx.lineTo(this.x + this.w, this.centerY);
  ctx.lineTo(this.centerX, this.y+this.h);
  ctx.lineTo(this.x, this.centerY);
  ctx.closePath();
  ctx.fillStyle = color;
  ctx.fill();
  ctx.strokeStyle = "#000";
  ctx.stroke();
};

// check if x/y is inside this cell
Cell.prototype.isInCell = function(x, y) {

  var dx = Math.abs(x - this.centerX),
      dy = Math.abs(y - this.centerY);

  return (dx / (this.w * 0.5) + dy / (this.h * 0.5) <= 1);
};

// generate cell map
for(var y = 0; y < maxV; y++) {
  var dltX = toggle ? 0 : -cw * 0.5,
      dltY = -ch * 0.5 * y;
  
  toggle = !toggle;
  
  for(var x = 0; x < maxH; x++) {
      var c = new Cell(x, y, x * cw + dltX, y * ch + dltY, cw, ch);
      cells.push(c);
      c.render(ctx, "#9f0"); // green bg
  }
}

// test by clicking in cell
canvas.onclick = function(e) {
  
  var r = canvas.getBoundingClientRect(),
      x = e.clientX - r.left,
      y = e.clientY - r.top,
      i = 0, cell;
  
  for(; cell = cells[i]; i++) {
    if (cell.isInCell(x, y)) {
      cell.render(ctx, "#f00"); // red bg if inside
      out.innerHTML = "Cell pos: (" + cell.posX + "," + cell.posY + ") X: " + cell.x + " Y: " + cell.y;
      break;
    }
  }
};
<canvas id=canvas width=500 height=100></canvas><br>
<output id=out></output>