Isometric Tile Picking (C++)

This function represents a refusal to use any sort of trig to project the screen coordinates to tile coordinates. I am aware there are various ways to do this, but I was stubborn and was determined to figure it out on my own. The point is not to be the most efficient or legible, but to portray a level of problem solving ability.

point CMap::GetMouseTile(point& mouse)
{
  // make sure we account for any map offset right away:
  mouse.Offset(-m_nOSx, -m_nOSy);
  point ptCurrMouseTileID;
  const int halfWidth  = (g_TileWidth >> 1);
  const int halfHeight = (g_TileHeight >> 1);

  // first find which rectangle the point's in:
  int rectIDx, rectIDy;
  ptCurrMouseTileID.x = rectIDx = mouse.x / g_TileWidth;
  ptCurrMouseTileID.y = rectIDy = mouse.y / g_TileHeight;
  if (ptCurrMouseTileID.y > 0)	// does not apply to the first row
    ptCurrMouseTileID.y = (ptCurrMouseTileID.y << 1);

  // now find which quadrant it's in based upon the point's relative position in this rect
  int adjX, adjY;
  adjX = mouse.x - (rectIDx * g_TileWidth);
  adjY = mouse.y - (rectIDy * g_TileHeight);
  int quadrant = TOP_LEFT;
  if (adjX >= halfWidth)
    quadrant = TOP_RIGHT;
  if (adjY >= halfHeight)
  {
    if (quadrant > 0)
      quadrant = BTM_RIGHT;
    else
      quadrant = BTM_LEFT;
  }

  // now determine if the point is inside or outside the actual tile based upon which quadrant it's in
  int result = -1;
  switch (quadrant)
  {
    case BTM_RIGHT:
    // for bottoms, if result > 0, the point is outside the tile
    {
      float d = (float)(halfHeight - g_TileHeight) / (float)(g_TileWidth - halfWidth);
      result = adjY - g_TileHeight - (int)(d * (float)(adjX - halfWidth));
      if (result > 0)
	++ptCurrMouseTileID.y;
    }break;
    case BTM_LEFT:
    {
      float d = (float)(halfHeight - g_TileHeight) / (float)(-halfWidth);
      result = adjY - g_TileHeight - (int)(d * (float)(adjX - halfWidth));
      if (result > 0)
      { --ptCurrMouseTileID.x; ++ptCurrMouseTileID.y; }
    }break;
    case TOP_RIGHT:   // for tops, if result is < 0, the point is outside the tile
    {
      float d = (float)halfHeight / (float)(g_TileWidth - halfWidth);
      result = adjY - (int)(d * (float)(adjX - halfWidth));
      if (result < 0)
	--ptCurrMouseTileID.y;
    }break;
    case TOP_LEFT:
    {
      float d = (float)halfHeight / (float)(-halfWidth);
      result = adjY - (int)(d * (float)(adjX - halfWidth));
      if (result < 0)
      { --ptCurrMouseTileID.x; --ptCurrMouseTileID.y; }
    }break;
  }
    // clamps:
    if (ptCurrMouseTileID.x < 0)
      ptCurrMouseTileID.x = 0;
    if (ptCurrMouseTileID.y < 0)
      ptCurrMouseTileID.y = 0;
    if (ptCurrMouseTileID.x > m_nNumCols - 1)
      ptCurrMouseTileID.x = m_nNumCols - 1;
    if (ptCurrMouseTileID.y > m_nNumCols - 1)
      ptCurrMouseTileID.y = m_nNumCols - 1;

    // final result:
    return ptCurrMouseTileID;
}

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>