How to compare vector3s?

Hello folks,

Defold newbie here: I’m trying to compare Vector3s, and for some reason am failing at it, as you can see from the following debug:

DEBUG:SCRIPT: key	vmath.vector3(0, -1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(0, 1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(-1, 0, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(1, 0, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(-1, -1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(1, 1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(1, -1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(-1, 1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: updated the animation for input:	vmath.vector3(0, -1, 0)	 => 	nil

It seems to me the very first line should read “true” instead of “false”, what am I missing?

Here’s the code (I’m trying to select the correct sprite animation to play given the character’s direction):

function init(self)
	-- some other initialisation 
	self.input = vmath.vector3()
	-- some other initialisation
end

local function update_animation(input)
	local animations = {
		[vmath.vector3(-1, -1, 0)] = "player-down-left",
		[vmath.vector3(-1, 1, 0)] = "player-up-left",
		[vmath.vector3(1, -1, 0)] = "player-down-right",
		[vmath.vector3(1, 1, 0)] = "player-up-right",
		[vmath.vector3(1, 0, 0)] = "player-right",
		[vmath.vector3(-1, 0, 0)] = "player-left",
		[vmath.vector3(0, 1, 0)] = "player-up",
		[vmath.vector3(0, -1, 0)] = "player-down",
	}
	for key, value in pairs(animations) do
		print("key", key, "equals", input, "?", value == input)
	end
	print("updated the animation for input:", input, " => ", animations[input])
	return animations[input]
end

function on_input(self, action_id, action)
	local previous_input = vmath.vector3(self.input) -- clone self.input
	
	if action_id == hash("up") then
		self.input.y = 1
	elseif action_id == hash("down") then
		self.input.y = -1
	elseif action_id == hash("left") then
		self.input.x = -1
	elseif action_id == hash("right") then
		self.input.x = 1
	elseif action_id == hash("fire") and action.pressed then
		self.firing = true
	end

	if self.input ~= previous_input then
		self.animation = update_animation(vmath.vector3(self.input))
	end
end

I’m sure I’m missing something obvious, but I couldn’t find what, even after going through the documentation on tables and constructors, and it seems from my google searches that Vector3s should be comparable?

Shouldn’t you be comparing with key here?

1 Like

My bad, it was indeed a typo, and indeed if I change the code to read

	for key, value in pairs(animations) do
		print("key", key, "equals", input, "?", input == key)
	end

It does indeed print what’s expected:

DEBUG:SCRIPT: key	vmath.vector3(0, -1, 0)	equals	vmath.vector3(0, -1, 0)	?	true
DEBUG:SCRIPT: key	vmath.vector3(0, 1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(-1, 1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(-1, 0, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(1, 0, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(1, 1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(1, -1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: key	vmath.vector3(-1, -1, 0)	equals	vmath.vector3(0, -1, 0)	?	false
DEBUG:SCRIPT: updated the animation for input:	vmath.vector3(0, -1, 0)	 => 	nil

However I’m still getting a “nil” when calling “animations[input]”, which is, I believe, the real issue here.

So I modified the code slightly to give more debug information:

	for key, value in pairs(animations) do
		print("key", key, "equals", input, "?", input == key, animations[key], animations[input])
	end

And this gives me:

DEBUG:SCRIPT: key	vmath.vector3(-1, 1, 0)	equals	vmath.vector3(0, -1, 0)	?	false	player-up-left	nil
DEBUG:SCRIPT: key	vmath.vector3(0, -1, 0)	equals	vmath.vector3(0, -1, 0)	?	true	player-down	nil
DEBUG:SCRIPT: key	vmath.vector3(0, 1, 0)	equals	vmath.vector3(0, -1, 0)	?	false	player-up	nil
DEBUG:SCRIPT: key	vmath.vector3(-1, 0, 0)	equals	vmath.vector3(0, -1, 0)	?	false	player-left	nil
DEBUG:SCRIPT: key	vmath.vector3(-1, -1, 0)	equals	vmath.vector3(0, -1, 0)	?	false	player-down-left	nil
DEBUG:SCRIPT: key	vmath.vector3(1, 0, 0)	equals	vmath.vector3(0, -1, 0)	?	false	player-right	nil
DEBUG:SCRIPT: key	vmath.vector3(1, 1, 0)	equals	vmath.vector3(0, -1, 0)	?	false	player-up-right	nil
DEBUG:SCRIPT: key	vmath.vector3(1, -1, 0)	equals	vmath.vector3(0, -1, 0)	?	false	player-down-right	nil
DEBUG:SCRIPT: updated the animation for input:	vmath.vector3(0, -1, 0)	 => 	nil

So even when “ input == key”, then:

  • animations[key] -- returns "player-down"
    
  • animations[input] -- returns nil
    

I think vectors must be a special kind of data type (userdata? or maybe it’s own type vector?). Basically I suspect the vectors aren’t values (like a number) but more a reference (like a table). It doesn’t fully explain why outside of a table context vmath.vector3(0,0,0) does equal vmath.vector3(0,0,0).

In any case I think I am close to the answer because converting vectors into string representations does work:

local vec = vmath.vector3(1,2,3)
    
local t = {[vmath.vector3(1,2,3)] = "match"}
    
print(t[vec]) --nil
    
-----------------------------
    
local function create_vector_key(v)
	return v.x..","..v.y..","..v.z
end

local t = {[create_vector_key(vmath.vector3(1,2,3))] = "match"}
    
print(t[create_vector_key(vec)]) --"match"

Codepad illustrating the above code.

I don’t know if this is the route you want to go down, but in any case I think it illustrates the problem with using vectors as keys in a table.

1 Like

You could also do:

local function vector_to_animation(v)
	local anim = "player"
	if v.y > 0 then
		anim = anim .. "-up"
	elseif v.y < 0 then
		anim = anim .. "-down"
	end
	if v.x > 0 then
		anim = anim .. "-right"
	elseif v.x < 0 then
		anim = anim .. "-left"
	end
	return anim
end

I would not bother about performance and string concatenation at this point.

2 Likes

oh wow indeed this is looking like the reason why it’s failing in my case, i’ll change my code to instead use the string representation (or change altogether to be based on the angle of the vector or something).

Thank you so much for this insight!

Yeah, that’s a solution also (there are “if”s because there are 8 directions, with the diagonals).

It all started with me trying to find a way to implement some kind of switch/case in lua, and I guess my solution would work, if it wasn’t actually trying to index the table on vectors, which doesn’t seem to work for some reason.

Thank you all for your insights and tips

1 Like

Yes the moral of the story is don’t use a vector as a key…
vmath.vector3() is an object not a value. When called it should return a reference to the instantiation (like a table does), not a vector. Do this:

	local whattt = {}
	for i=1,5 do
		whattt[vmath.vector3(1,2,3)] = i
	end
	for k,v in pairs(whattt) do
		print("k,v:",k,v)
	end
	local i=0
	for k,v in pairs(whattt) do
		i=i+1
		k.x,k.y,k.z =i,i*2,i*3
	end
	for k,v in pairs(whattt) do
		print("yeah",v,k.x,k.y,k.z,k)
	end

And get this:

DEBUG:SCRIPT: k,v:	vmath.vector3(1, 2, 3)	5
DEBUG:SCRIPT: k,v:	vmath.vector3(1, 2, 3)	1
DEBUG:SCRIPT: k,v:	vmath.vector3(1, 2, 3)	4
DEBUG:SCRIPT: k,v:	vmath.vector3(1, 2, 3)	3
DEBUG:SCRIPT: k,v:	vmath.vector3(1, 2, 3)	2
DEBUG:SCRIPT: yeah	5	1	2	3	vmath.vector3(1, 2, 3)
DEBUG:SCRIPT: yeah	1	2	4	6	vmath.vector3(2, 4, 6)
DEBUG:SCRIPT: yeah	4	3	6	9	vmath.vector3(3, 6, 9)
DEBUG:SCRIPT: yeah	3	4	8	12	vmath.vector3(4, 8, 12)
DEBUG:SCRIPT: yeah	2	5	10	15	vmath.vector3(5, 10, 15)

Lots of fun trying to work this out.
Looks like, when printed out, the key is replaced with the vector the key is a reference to.

1 Like

Thanks for the analysis and investigation, very much appreciated!

1 Like