Mouse Position to Isometric to Tiles?

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
nmttmn
Prole
Posts: 4
Joined: Sun Mar 30, 2025 7:42 am

Mouse Position to Isometric to Tiles?

Post by nmttmn »

Hello!,

I have been following this video (https://www.youtube.com/watch?v=04oQ2jOUjkU&t=181s) trying to make an isometric map. Everything is working fine. However, I do not understand how to get the mouse mx and my to translate into a selected tile :( .
This is what i have so far for the code. It is the direct inverse. I this the best way to do this? Should i look up how to do ray-cast things or is this cool?
local function tileToCartt(mx,my)

local a = ((1 / 2) * widthSprite)
local b = ((-1 / 2) * widthSprite)
local c = ((0.5 / 2) * heightSprite)
local d = ((0.5 / 2) * heightSprite)

local inv = 1 / (a * d - b * c)

local invA = inv * a
local invB = -(inv * b)
local invC = -(inv * c)
local invD = inv * d

--[[
A = [a, b c, d]
Ainv = 1/ad - bc * [d -b c a]

local i1 = a * invD + b * invC
local i2 = a * invB + b * invA
local i3 = c * invD + d * invC
local i4 = c * invB + d * invA

print('i1= ' ..i1..'i2= '..i2..'i3= '..i3..'i4= '..i4)
]]

local w = invD * my + invB * my
local b = invC * mx + invA * mx
return w,b

end
User avatar
darkfrei
Party member
Posts: 1262
Joined: Sat Feb 08, 2020 11:09 pm

Re: Mouse Position to Isometric to Tiles?

Post by darkfrei »

nmttmn wrote: Sun Mar 30, 2025 7:52 am Hello!,

I have been following this video (https://www.youtube.com/watch?v=04oQ2jOUjkU&t=181s) trying to make an isometric map. Everything is working fine. However, I do not understand how to get the mouse mx and my to translate into a selected tile :( .
This is what i have so far for the code. It is the direct inverse. I this the best way to do this? Should i look up how to do ray-cast things or is this cool?

Code: Select all

local function tileToCartt(mx,my)

    local a = ((1 / 2) * widthSprite)
    local b = ((-1 / 2) * widthSprite)
    local c = ((0.5 / 2) * heightSprite)
    local d = ((0.5 / 2) * heightSprite)

    local inv = 1 / (a * d - b * c)

    local invA = inv * a
    local invB = -(inv * b)
    local invC = -(inv * c)
    local invD = inv * d

    --[[
    A = [a, b      c, d]
    Ainv = 1/ad - bc * [d -b    c a]

    local i1 = a * invD + b * invC
    local i2 = a * invB + b * invA
    local i3 = c * invD + d * invC
    local i4 = c * invB + d * invA

    print('i1= ' ..i1..'i2= '..i2..'i3= '..i3..'i4= '..i4)
       ]]

    local w = invD * my + invB * my
    local b = invC * mx + invA * mx
    return w,b

end

Code: Select all

local tileSize = 16
local scale = 5
local offsetX = 0/scale -- initial offsets
local offsetY = 0/scale

local isoWidth = tileSize / 2 -- horizontal bias between tiles
local isoHeight = tileSize / 4 -- vertical bias between tiles

local isoYoffset = -0.5

local function screenToTile(screenX,screenY)
-- screenX,screenY - screen position, for example mouse cursor
-- scale - scaling factor for low-DPI pixelart images
-- offsetX, offsetY - offset for moved world
-- isoYoffset - small vertical offset for fine tuning
-- isoWidth, isoHeight - sizes of tile
    screenX = screenX / scale
    screenY = (screenY) / scale
    local tempX = (screenX - offsetX) / isoWidth
    local tempY = (screenY - offsetY-isoYoffset) / isoHeight
    local x = math.floor((tempX + tempY) / 2 + 0.5)+1 -- here +1 for Lua
    local y = math.floor((tempY - tempX) / 2 + 0.5)+1
    return x,y
end
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
RNavega
Party member
Posts: 465
Joined: Sun Aug 16, 2020 1:28 pm

Re: Mouse Position to Isometric to Tiles?

Post by RNavega »

Also checkout darkfrei's demos in here: viewtopic.php?p=261925&sid=ae1e665b855f ... 25#p261925
User avatar
pgimeno
Party member
Posts: 3713
Joined: Sun Oct 18, 2015 2:58 pm

Re: Mouse Position to Isometric to Tiles?

Post by pgimeno »

I like taking advantage of Löve built-in objects. Such a pity that Transform:transformPoint and Transform:inverseTransformPoint don't support 3D coordinates, even though the matrix is a complete 4x4 one. It's as if Löve is not made for isometric games.

Code: Select all

local map = {
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
  1,0,2,0,1,0,0,3,0,0,0,1,1,1,0,1,
  1,0,2,0,1,0,0,0,0,0,0,1,4,0,0,1,
  1,0,2,0,1,0,0,3,0,0,0,1,1,0,0,1,
  1,0,0,0,1,0,0,3,0,0,0,0,1,0,0,1,
  1,0,0,0,0,0,0,0,0,1,0,0,1,1,0,1,
  1,1,1,1,1,1,1,0,0,1,0,0,0,0,0,1,
  1,0,0,0,0,0,0,0,1,1,0,0,2,2,0,1,
  1,0,1,1,1,0,0,0,1,0,0,0,2,2,0,1,
  1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,
  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
}

local tf = love.math.newTransform()
local x0, y0 = 400, 300 -- draw position
local ts = 16 -- tile size - better if even
local hix = -1
local hiy = -1

function love.keypressed(k) return k == "escape" and love.event.quit() end

function love.mousemoved(x, y)
  hix, hiy = tf:inverseTransformPoint(x, y)
  hix, hiy = math.floor(hix), math.floor(hiy)
end

function love.update(dt)
  tf:setMatrix(   ts,   -ts,   0,    x0,
               .5*ts, .5*ts, -ts,    y0,
                   0,     0,   1,     0,
                   0,     0,   0,     1)
end

function love.draw()
  local p = 0
  for y = 0, 11 do
    for x = 0, 15 do
      p = p + 1
      local tile = map[p]

      -- Corners of a rectangle, for drawing the tiles. Drawing rectangles
      -- is much harder than drawing sprites in this way, because every
      -- corner needs to be transformed, and not just the tile coords.
      -- Tiles are drawn with a 2 pixel margin on each side (total 4 pixels
      -- between tiles) before projection.
      local left = x + 2/ts
      local top = y + 2/ts
      local right = x + 1 - 2/ts
      local bottom = y + 1 - 2/ts
      -- Transform them
      local p1x, p1y = tf:transformPoint(left, top)     -- top left corner
      local p2x, p2y = tf:transformPoint(right, top)    -- top right corner
      local p3x, p3y = tf:transformPoint(right, bottom) -- bottom right corner
      local p4x, p4y = tf:transformPoint(left, bottom)  -- bottom left corner

      if x == hix and y == hiy then
        love.graphics.setColor(1, 1, 1)
      elseif tile == 0 then
        love.graphics.setColor(0.7, 0.7, 0.7)
      elseif tile == 1 then
        love.graphics.setColor(0.5, 0.5, 0)
      elseif tile == 2 then
        love.graphics.setColor(0, 0.7, 0)
      elseif tile == 3 then
        love.graphics.setColor(0, 0.5, 0.6)
      elseif tile == 4 then
        love.graphics.setColor(1, 0.4, 0.4)
      else
        print("Unknown tile")
        love.event.quit()
      end

      love.graphics.polygon("fill", p1x, p1y,  p2x, p2y,  p3x, p3y,  p4x, p4y)
    end
  end
  love.graphics.setColor(1, 1, 1)
end
The transform is set in love.update with the idea that x0 and y0 can be scrolled at will, even if this short demo doesn't do it.
RNavega
Party member
Posts: 465
Joined: Sun Aug 16, 2020 1:28 pm

Re: Mouse Position to Isometric to Tiles?

Post by RNavega »

pgimeno wrote: Mon Mar 31, 2025 7:48 pm

Code: Select all

  tf:setMatrix(   ts,   -ts,   0,    x0,
               .5*ts, .5*ts, -ts,    y0,
                   0,     0,   1,     0,
                   0,     0,   0,     1)
Hi, how did you reach that formulation?
I can see the x0, y0 as the position elements, but the X basis vector and Y basis vector (with a Z component), those are wooshing over my head.
User avatar
pgimeno
Party member
Posts: 3713
Joined: Sun Oct 18, 2015 2:58 pm

Re: Mouse Position to Isometric to Tiles?

Post by pgimeno »

RNavega wrote: Tue Apr 01, 2025 1:22 am
pgimeno wrote: Mon Mar 31, 2025 7:48 pm

Code: Select all

  tf:setMatrix(   ts,   -ts,   0,    x0,
               .5*ts, .5*ts, -ts,    y0,
                   0,     0,   1,     0,
                   0,     0,   0,     1)
Hi, how did you reach that formulation?
I can see the x0, y0 as the position elements, but the X basis vector and Y basis vector (with a Z component), those are wooshing over my head.
They are column vectors. Only the Z vector has a Z component. The X basis vector is (1, 0.5, 0)*ts and the Y is (-1, 0.5, 0)*ts. I figured it would be good to use the isometric Z as actual Z, as if it went partially out of the screen plane. Not sure about the sign. I need to think better about how to derive the projected Z, in order to get something that can be depth-sortable. Probably the other basis vectors' Z components should be nonzero too; not sure what exactly yet, but since this approach isn't directly usable, there's no point for me to invest more time in it.

Edit: Meh, my head didn't want to stop. Setting them all to the same depth would work, I think:

Code: Select all

  tf:setMatrix(   ts,   -ts,   0,    x0,
               .5*ts, .5*ts, -ts,    y0,
                   1,     1,   1,     0,
                   0,     0,   0,     1)
RNavega
Party member
Posts: 465
Joined: Sun Aug 16, 2020 1:28 pm

Re: Mouse Position to Isometric to Tiles?

Post by RNavega »

pgimeno wrote: Thu Apr 03, 2025 6:18 pm They are column vectors. (...)
Thanks a lot. I need to brush up on algebra, I'm always bad at it.

That tf:setMatrix() overload that you're using expects a row-major input, just like you indented it in the code.

I was thinking that the matrix being row-major / column-major would affect the result of the multiplication, but it's just an implementation detail: how the matrix data is stored in memory.
But say, using 1-based indexing, requesting the element (3, 2) will always yield the element in the third row, column two, no matter the internal layout. That layout does not affect the result of a matrix-vector multiplication.

(Edit: the below are notes I took while trying to understand your code.)
The key point is that you can make one component in the input vector (say, the X coordinate) affect the other components in the result, based on the factors that you put in the first matrix column, as seen below:

Matrix-Vector Multiplication.png
Matrix-Vector Multiplication.png (63.42 KiB) Viewed 1515 times
The product M × v, which is v', will have the input X affecting the output Y because of the M_21 factor (which is .5*ts in your matrix).

Describing it makes sense too: "As the input X coordinate changes, the output Y coordinate should change too", like, if you move to the right (increase the input X) then you also go down (increase the output Y) in the result.
User avatar
steVeRoll
Party member
Posts: 142
Joined: Sun Feb 14, 2016 1:13 pm

Re: Mouse Position to Isometric to Tiles?

Post by steVeRoll »

pgimeno wrote: Mon Mar 31, 2025 7:48 pm Such a pity that Transform:transformPoint and Transform:inverseTransformPoint don't support 3D coordinates, even though the matrix is a complete 4x4 one. It's as if Löve is not made for isometric games.
Supporting 3D coordinates would imply more broad 3D support, which love will not provide as a mainly 2D framework.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot] and 6 guests