Vector2 / Vector3 interoperability

As a roblox developer, it’s annoying to translate between Vector2 and Vector3 values. This is something you have to do rather often as a developer, especially when working with input since InputObjects use a Vector3 to describe a position on the screen.

I want an easy way to minimize this:

local pos = Vector2.new(input.Position.X, input.Position.Y)

My suggestion is to add some new functions:

Vector3.fromVector2(vector2)
which is the same as
Vector3.new(vector2.X, vector2.Y, 0)

and

Vector2.fromVector3(vector3)
which is the same as
Vector2.new(vector3.X, vector3.Y)

Or add overrides to Vector2.new and Vector3.new to accept Vector2s and Vector3s.

18 Likes

You… want to minimize something that’s already only a few words, into a few less words?..

I don’t really understand this at all. You’re taking a one-liner and turning it into a slightly shorter one-liner.

I mean literally, in your own demonstration/example, it is about a centimeter less of text… :expressionless:

1 Like

This isn’t the entire story. Consider the case where you have a long expression to evaluate. In the best case, it’s just a long property name, but in the worst case, it’s a complex bespoke expression:

local scaledInput = Vector2.new(
    ((input.Position - centrePoint) * inputScaling + centrePoint).X,
    ((input.Position - centrePoint) * inputScaling + centrePoint).Y
)

This is both wasteful computationally and wasteful visually. Right now, our best bet is to separate out the expressions:

local scaledInput3D = ((input.Position - centrePoint) * inputScaling + centrePoint)
local scaledInput2D = Vector2.new(scaledInput3D.X, scaledInput3D.Y)

I’ve done this lots of times in my codebase, and it’s getting more common as I start writing more generic code in my projects. I end up with a lot of boilerplate code this way, and it pollutes the local namespace with intermediary variables. Remember that this is not the only snippet of code present in a codebase - often, I’m doing a whole chain of mathematical processing and this is just one atomic step.

Whereas this feature request makes it meaningfully better - suppose Vector2.new could take a Vector3 to reduce down:

local scaledInput = Vector2.new((input.Position - centrePoint) * inputScaling + centrePoint)

I think it is worth considering this before inserting expressionless emojis into forum replies.

5 Likes

You could make the tool you want, which could be something like this:

--Swizzler
local function Swiz(source, members)
	local vectype
	
	if #members == 1 then
		return source[members]
	elseif #members == 2 then
		vectype = Vector2
	elseif #members == 3 then
		vectype = Vector3
	end
	
	local newargs = {}
	
	for i = 1, #members do
		local axis = string.upper(string.sub(members, i, i))
		
		newargs[i] = source[axis]
	end
	
	return vectype.new(table.unpack(newargs))
end

print(Swiz(Vector3.new(1,2,3), "xz")) --Vector2: 1, 3
print(Swiz(Vector2.new(1,2), "xyx"))  --Vector3: 1, 2, 1

You could get downright funky with this:

local function NewSwizvec(...)
	local args = table.pack(...)
	
	local vectype
	
	if #args == 2 then
		vectype = Vector2
	elseif #args == 3 then
		vectype = Vector3
	end
	
	local vec = vectype.new(...)
	local swizvec = {["xyz"] = vec}
	
	local swizmt = {}
	
	function swizmt.__index(t, k)
		--print("Index: " ..tostring(t).. " : " ..tostring(k))
		
		return Swiz(t.xyz, k)
	end
	
	setmetatable(swizvec, swizmt)
	
	
	return swizvec
end

print(NewSwizvec(1, 2, 3).xxx) --1, 1, 1
print(NewSwizvec(2, 3).yyx) --3, 3, 2
print(NewSwizvec(2, 3).y) --3 (just a number)
1 Like