Understanding Vectors

I have a very basic understanding of what vectors are and I would like to learn more. Does anyone know of any tutorials of any form that goes in depth of what vectors are in a way that relates to how they’re used in Roblox?

24 Likes

What type of vectors are you referring to?
Position vectors control location, but I assume you mean rotational vectors.

8 Likes

Easier tutorial:

Complex tutorial:

4 Likes

I spent an hour writing this so don’t you DARE skim it. You have to read it all the way through and make an effort to understand it! I can’t guarantee I’ll be able to respond after this.

A vector looks like (1, 2, 3).

Basically, before we see this vector, we all agree that the first number corresponds to taking that many steps to the right, the second number corresponds to taking that many steps up, and the last one how many steps backward. We also agree that there’s a point called the origin at which we all start.

So in the example above, if we start at the origin (where we say it has the position vector (0, 0, 0)) and we move 1 to the right, 2 up, and 3 back, that location we end up is the location to which the vector above corresponds.

A vector has something called a magnitude. Consider the vector (2, -1, 2). If you move 2 to the right, 1 down, and 2 back, you will have taken 5 steps. You walked in a zigzag, but instead if you went in a straight line towards the end point, the number of steps you take is called the vector’s magnitude. The magnitude can be computed with sqrt(x^2 + y^2 + z^2) = sqrt(2^2 + (-1)^2 + 2^2) = sqrt(9) = 3. This is the length of the vector.

Concerning right, up and back, you can use any standard. But a vector will only do what you expect it to if you use the same standard as the program you are working with.

But, you could have chosen any directions to form the “basis” of your coordinate system (as long as they do not lie on the same line or plane). You could say that the vector (1, 2, 3) means to start at the moon, move 1 step kinda down and to the left, move 2 steps a little forward and to the left, and move 3 steps up.

You can define your own coordinate system in terms of the one ROBLOX defines (right, up, back), and origin, by using something called a CFrame.

The CFrame which describes the same exact coordinate system as the default (makes no change) is known as the identity CFrame. If you wanted to “change your basis” such that instead of (right, up, back), it was (right, up, forward), and where you started at some other point, you could do something like this:

local newPos = Vector3.new(0, 3, 0)--start 3 higher at the origin
local newX = Vector3.new(1, 0, 0)--unchanged, x still goes to the right
local newY = Vector3.new(0, 1, 0)--also unchanged, y still goes up
local newZ = Vector3.new(0, 0, -1)--reverse, z becomes forward

local basisCF = CFrame.new(
    newPos.x, newPos.y, newPos.z,
    newX.x, newY.x, newZ.x,
    newX.y, newY.y, newZ.y,
    newX.z, newY.z, newZ.z,
)

And if you wanted to translate from your new coordinate system back into ROBLOX’s coordinate system, all you must do is.

local vector = Vector3.new(0, 0, 5)--5 steps forward in your new coordinate system
local robloxVector = basisCF*vector
print(robloxVector)
>(0, 3, -5)

Since this process goes from your new coordinate system to roblox’s coordinate system, we can go from roblox’s coordinate system to your new coordinate system using something called an inverse.

--NOTE: Roblox's inverse function is NOT A TRUE INVERSE. This only works if
--the direction vectors of the matrix are unit and 90 degrees to each other!
--If you get a Part's CFrame, you are safe to use that.
local originalVector = basisCF:inverse()*robloxVector
print(originalVector)
>(0, 0, 5)

Back to taking steps. Let’s consider a vector (3, 0, 0). If we want to perform the steps described by this vector 4 times, we can simply perform the operation 4*(3, 0, 0) and get the result (12, 0, 0). Something to ponder: If we have the vector (12, 0, 0), and we wanted to know how many times we have to perform the (3, 0, 0) step operation, how could we compute that? Keep this question in mind.

There is a concept in vectors known as the dot product. (1, 2, 3) dot (2, -1, 2) basically just tells you how much 2 vectors agree. Think about it like politics. Person 1 has a strong positive opinion on policy A, and has a bit of a negative opinion about policy B, but doesn’t really care about C. Maybe their policy opinion vector looks like (3, -1, 0). Person 2 has a bit of a strong positive opinion on policy A, a bit of a positive opinion on policy B, but has a really strong positive opinion on policy C. Their policy opinion vector looks like (2, 1, 5).

If we want to know how much they agree, we can dot these vectors together. We define dot product as (ax, ay, az) dot (bx, by, bz) = ax*bx + ay*by + az*bz. The result of the dot product of these two political people is (3, -1, 0) dot (2, 1, 5) = 3*2 + -1*1 + 0*5 = 6 - 1 + 0 = 5. They agree a lot with each other on policy A, they don’t agree on policy B but it’s not a big point of contention, and since Person 1 doesn’t care about policy C, Person 2’s strong opinion doesn’t bother Person 1. In other words, they generally agree with each other. If the dot product were negative, they would generally disagree.

You can use dot product to tell if 2 vectors are generally facing in the same direction, or generally in the opposite direction. If the dot product is negative, they are generally opposite, if positive, they are generally the same direction.

In a kind of hand wavy way, we can use this concept of agreement to get the length of a vector. Because dot product gives you a single value that tells you how much a vector agrees with another vector. Think back to political vectors. If a longer vector, or a vector with a bigger magnitude, tells you how strong of feelings one has on policy, we can essentially see how much a vector agrees with itself to get the square how how strong it is. (or its magnitude). So by taking the square root of the dot product of a vector with itself, sqrt(a dot a), we get the magnitude. In code this looks like:

local a = Vector3.new(3, -1, 0)
local b = Vector3.new(2, 1, 5)
local agreement = a:Dot(b)--gets the dot product

local aMagnitude = math.sqrt(a:Dot(a))--gets the magnitude of the vector
local bMagnitude = b.magnitude--this is the faster easier way to get magnitude

This is all very formal but what does it mean?
Well I have to write this fast because I have a workout to get to, but basically, if you want to set the position of a part such that it is always in front of your camera, you could try something like this:

local cam = game.Workspace.CurrentCamera
local part = game.Workspace.Part

while true do
    wait()
    part.Position = cam.CFrame*Vector3.new(0, 0, -5)
    --do this if you want to conserve the rotation of the camera in the part
    --part.CFrame = cam.CFrame*CFrame.new(0, 0, -5)
end

Okay gotta go!

136 Likes

No idea how you were able to explain that so well and work in analogies
—but, wow

and is the sqrt(x^2 + y^2 + z^2) like a modified distance formula?

.

I still don’t understand how a vector can correspond to a rotation though. Like what is it based on? I don’t get how (1, 0, 0) can be a rotation.

5 Likes

Assume we’re in empty space with no obstacles and can freely move anywhere.
It calculates the (shortest) distance between the origin (0, 0, 0) and the position (x, y, z).

Explanation:
If I take x steps to the right, y steps up and z steps forward from the origin… I now end up at a new position but I didn’t necessarily take the fastest route to get there. The position I’m now at is (x, y, z).

So what is the fastest route?

It requires taking repeated steps in only a single direction without having to change direction like before. That direction is Vector3.new(x, y, z).Unit which points directly to the position (x, y, z) [from the origin]. Save the thought for now about what .Unit does. This is faster than first going to the right, then up, then forwards because you’re instead going directly towards it.

So if I continuously move in that direction from the origin, how many steps will it take to reach the goal?

You will take exactly [the square root of (x^2 + y^2 + z^2)] steps.
This will also be the shortest number of steps required to reach (x, y, z) from the origin as it follows the fastest possible route. As this represents the smallest number of steps required to reach the goal the quickest, it also represents the shortest distance between your starting point [the origin] and your end point [which is (x,y,z) ].

If you are familiar with Pythagorean theorem, this is how that formula is derived.

h9

(Small sidenote, here, z is going up and y is going forwards… This is just an alternative convention where some people choose to use z as the up direction. On Roblox, y is used as the up direction. Regardless, you can swap all the z’s and the y’s in that picture and it will still make sense.)

What does Vector3.new(x, y, z).Unit calculate exactly and what does it represent?

This attemps to get what is known as the “unit vector” of (x, y, z).
It calculates the new vector,
Vector3.new(x, y, z)*(1 / Vector3.new(x, y, z).Magnitude) which is the same as Vector3.new(x/Vector3.new(x, y, z).Magnitude, y/Vector3.new(x, y, z).Magnitude, z/Vector3.new(x, y, z).Magnitude).
This represents a new version of Vector3.new(x, y, z) where it’s magnitude (or distance from the origin) is no longer sqrt(x^2 + y^2 + z^2) but instead just equal to 1. You can check this by applying the square root formula to the new vector which I wrote out above.

Why is that useful?

Each “step” that I was referring to earlier really just represented a movement of distance 1 in some specified direction. For example, when I said, “move x units to the right”, if x was 3 then this would mean incrementally moving a distance of 1 each time to the right three times [three incremental steps in the direction (1, 0, 0)]. However, suppose I no longer want to take these “incremental distance of 1” steps to the right and instead in a different direction. I can choose any (x, y, z) position I want to move directly towards and add a .Unit at the end to specify that I only want to take a “step of distance 1” towards that position. Hence, Vector3.new(x, y, z).Unit gives the new position which was arrived at by moving a distance of 1 from the origin in the direction towards Vector3.new(x, y, z).

6 Likes

In the strictest sense, (1, 0, 0) can represent a rotation.

Specifically, let’s think about what a rotation does. If you have an object that has a rotation, rotation A, and you want to perform some action on it such that it is in a different rotation, rotation B, in the most straightforward way, you can represent this with 3 angles.

First, rotate the object about its x axis by angle x, then rotate the object about its new y axis by angle y, then rotate the object about its new z axis by angle z. In ROBLOX, this looks like:

local angles = Vector3.new(1, 2, 3)
local part = game.Workspace.Part
part.CFrame = part.CFrame*CFrame.Angles(1, 2, 3)--applies a rotation first about its x, then about its y, then about its z.

By applying these 3 rotations, it is possible to describe the change from any rotation A into any rotation B. If you tried to do this with 1 number, you could only make rotations on the plane, and even with 2 numbers, you can only reach 0% of all possible B rotations.

However, the description of rotations is much more natural as a matrix.

If we think about what a rotation is used for, it’s really just to transform some set of vertices (vectors which define the sharp points on a model) into a different position. We have actually already covered this.

If we wanted to move an object to the right, we could give it a property that tells the engine what to do with the vertices before it draws it on the screen. So if by default the object has a vertex at (1, 2, 3), and we want the object to be more to the right, we could call this property position, and set it to (5, 0, 0). Then, the engine when it’s going to draw the object, would look at the position property and just add it onto all of the objects vertices before drawing it. the result of that position applied to the example vertex would be (6, 2, 3).

This can be done with rotation as well. If we wanted to make an object look to the right, we could give the object a new property we call a CFrame. The CFrame makes the position property superfluous as it deals with both origin and rotation.

Let’s get things straight. By default, the back face, which in ROBLOX’s choice of basis vectors, looks in the direction (0, 0, 1), then that means that the front face points in the direction (0, 0, -1). The right face points in the direction (1, 0, 0), and the top face looks in the direction (0, 1, 0).

Okay, so we want to make the object look to the right, we need to define a basis CFrame (with rotation only, so no position) such that after we do basisCF*(the directions above) they result in the directions we want them to be.

We want the front face to look to the right. So we want (0, 0, -1) to become (1, 0, 0).
We want the right face to look back, So we want (1, 0, 0) to become (0, 0, 1).
The top face should stay in the same direction, so (0, 1, 0) stays as (0, 1, 0).

In the math this looks more like:
BasisCF*(0, 0, -1) = (1, 0, 0)
BasisCF*(1, 0, 0) = (0, 0, 1)
BasisCF*(0, 1, 0) = (0, 1, 0)

So, looking at the top vector transformation we want, the question we should ask is, what should we make our new x axis, y axis and z axis such that when you take the forward direction, (0, 0, -1), and apply it in our new basis, it becomes (1, 0, 0) in roblox coordinates? Let’s look at the equation real quick.

forward.x*newXAxis + forward.y*newYAxis + forward.z*newZAxis = newForward
0*newXAxis + 0*newYAxis + -1*newZAxis = (1, 0, 0)
newZAxis = (-1, 0, 0)

What about our new X axis?

right.x*newXAxis + right.y*newYAxis + right.z*newZAxis = newRight
1*newXAxis + 0*newYAxis + 0*newZAxis = (0, 0, 1)
newXAxis = (0, 0, 1)

And the new Y axis? Well that will just stay the same.

This is a trivial example but there’s still a lot to it. In code this will look like:

local newPos = Vector3.new(5, 0, 0)
local newX = Vector3.new(0, 0, 1)--makes the x direction become the z direction
local newY = Vector3.new(0, 1, 0)--unchanged
local newZ = Vector3.new(-1, 0, 0)--makes the -z direction become the x direction

local basisCF = CFrame.new(
    newPos.x, newPos.y, newPos.z,
    newX.x, newY.x, newZ.x,
    newX.y, newY.y, newZ.y,
    newX.z, newY.z, newZ.z,
)

anyway you could try this in code by setting part.CFrame = basisCF.

The easier way to do this is with a half pi (quarter turn) rotation applied through CFrame.Angles.

local basisCF = CFrame.Angles(0, math.pi/2, 0) + Vector3.new(5, 0, 0)

The thing about rotations in 3D is to remember this. rotation about the x axis makes the y axis turn toward the z axis. Think: (x: y -> z) This is a cyclical pattern, so the same is true about a rotation about the y axis and z axis: (y: z -> x) and (z: x -> y)

17 Likes

Okay, let’s say we have a vector of (0,1,0). This is a directional vector. Why? Let’s say you want someone looking upwards. The Y axis is the upwards axis. Thus that person is looking in the direction of (0,1,0). The Y axis of the vector is 1 because the person is looking upwards. If the person was looking downwards the Y axis would be -1.
image
image
A thing to note is that directions are always in the range of -1 and 1. For example if a vector is (0,2,0) it isn’t really a direction vector anymore.

Now why is this usefull? Let’s say you want to spawn a brick 5 studs infront of player and you know the player directio vector is (0.5,0,-0.5). If you know that, solving this problem gets really simple, all you have to do is multiply the direction vector times 5 and add the player position and there you go! The brick is now positioned 5 studs infront of the player.

Say we have a brick positioned at (0,5,0) with a direction vector of (0,1,0). This means that the brick is rotated upwards. It’s looking upwards. With (1,0,0) it’s looking to it’s right. And with (0,0,1) it’s looking backwards. Use all 3 of axis together and you get directional vector.

The best way to get a directional vector is by using .Unit. In roblox we can use (aimAtVector-startingVector).Unit to get a direction vector which is ‘looking at’ aimAtVector.
Let’s say you have a gun at (0,0,0) and you want it to place a part aimed at a player who is at (0,10,0) but the range of the gun is only 5 studs. How would you do that? You can use directional vectors for this!

local start = Vector3.new(0,0,0)
local endpos = Vector3.new(0,10,0)
local directional_vector = (endpos-start).Unit
local range = 5

local new_brick_position = start + directional_vector * range

That’s the best I can explain things for you.
I’m sorry if you didn’t understand me, I’m quit bad at explaining things xd.

7 Likes

so why are the values restricted to between 1 and -1?

Cause they’re directions. If you make them bigger then one they’ll scale up the part. If I tell you to take three (1, 0, 0) steps, you’ll end up 3 units away from me. If I tell you to take 3 (0, 1, 0) steps, you’ll still end up 3 units away from me. But if I tell you to take 3 (2, 0, 0) steps, you will be 6 units away from me. The scaling changes.

2 Likes

Sorry, I have only just found and started reading this article until now. I am very confused as to how this formula works: sqrt(x^2 + y^2 + z^2) = sqrt(2^2 + (-1)^2 + 2^2) = sqrt(9) = 3. Could someone please help?