1

I'm just starting to learn LUA and I've got an issue that I'm unsure which way to "properly" solve. When I pass a Defold vmath.vector3 to my function it appears to be passed by reference and is therefore altered.
If I multiply it by anything this is solved however.
Is there another more correct way to solve this? I don't want to modify the original vector that I pass as an argument.

function M.get_nearest_tile(x, y)
    if y then -- if we've got 2 inputs, use x & y
        x = math.floor(x / M.TILE_SIZE)
        y = math.floor(y / M.TILE_SIZE)
        return x, y
    else -- if we've only got 1 input, use as vector
        local vec = x * 1 -- multiplying by 1 to avoid modifying the real vector
        vec.x = math.floor(vec.x / M.TILE_SIZE) 
        vec.y = math.floor(vec.y / M.TILE_SIZE) 
        return vec.x, vec.y
    end
end
Henri Menke
  • 10,705
  • 1
  • 24
  • 42
Ghork
  • 70
  • 8
  • Where does `vector3` come from? Is this Roblox or so? – Henri Menke Nov 13 '18 at 22:15
  • Ah, sorry, I feel stupid. I thought it was from "math". It's from "vmath" which is used by Defold (game engine). – Ghork Nov 13 '18 at 22:18
  • 1
    According to the docs you can copy a vector using `local new = vmath.vector3(old)` https://www.defold.com/ref/vmath/#vmath.vector3:v1 – Henri Menke Nov 13 '18 at 22:19
  • Wow... I really fooled myself this time... I was thinking I would probably have the same issue with a table so I told myself this was an issue with LUA in general and didn't bother checking their docs... Welp, I guess this is the real answer though what I was asking wasn't right to begin with. I need to try what happens if I do the same with a table. – Ghork Nov 13 '18 at 22:24
  • Regarding your original question, you cannot “turn off” passing by reference. If something is implemented as a reference type, it will be passed as such. Copying tables in Lua is non-trivial, because it really depends on how and what part of the contents you want to copy (recursive, metatables, etc.). – Henri Menke Nov 13 '18 at 22:30
  • I see, well thank you very much for explaining it! – Ghork Nov 13 '18 at 22:40
  • 1
    Nitpick: It is Lua, not LUA. – britzl Nov 14 '18 at 05:04
  • 1
    You would get the same kind of result when passing a table. As Henri Menke mentioned above it is non-trivial to copy a Lua table. – britzl Nov 14 '18 at 05:05

3 Answers3

3

Defold provides a number of special data structures that are all very useful in game development:

  • vector3 - vmath.vector3(x,y,z), useful for describing positions or direction in a 3D coordinate system
  • vector4 - vmath.vector4(x,y,z,w), used for color, tint etc (red, green, blue, alpha)
  • quat - vmath.quat() a quaternion describing a rotation
  • matrix4 - vmath.matrix4() a 4x4 matrix of values. Useful for view and projection matrices among other things

All of the above are used by the Defold game engine, but you'll find the same kind of data structures in other game engines as well.

The data structures above have one thing in common: They are of the Lua type userdata

print(type(vmath.vector3())) -- "userdata"

User data is passed by reference always and that is why you're seeing the behaviour that you describe. Defold does provide ways to make copies though:

local copy = vmath.vector3(original) -- copy the vector3 'original' local copy = vmath.vector4(original) -- copy the vector4 'original' local copy = vmath.quat(original) -- copy the quaternion 'original' local copy = vmath.matrix4(original) -- copy the matrix4 'original'

britzl
  • 10,132
  • 7
  • 41
  • 38
0

You already have a solution in your else branch: you'll have to create a copy of your vectors before applying "modifying" operations to them.

In terms of other options, it may be possible to come up with a way to do using proxy tables, but it's going to be much more complex than just creating a copy.

Paul Kulchenko
  • 25,884
  • 3
  • 38
  • 56
0

Since you're returning x and y as two values, you could implement both branches the same way without modifying any tables:

function M.get_nearest_tile(x, y)
    local newX, newY
    if y then -- if we've got 2 inputs, use x & y
        newX = math.floor(x / M.TILE_SIZE)
        newY = math.floor(y / M.TILE_SIZE)
    else -- if we've only got 1 input, use as vector
        newX = math.floor(x.x / M.TILE_SIZE)
        newY = math.floor(x.y / M.TILE_SIZE)
    end
    return newX, newY
end
luther
  • 5,195
  • 1
  • 14
  • 24