I will be listing some methods for doing math with Vector3s, and explaining a bit about them. I am making this as a reference guide, so that people can quickly look up how to do something instead of giving themselves a headache trying to derive it lol. I’ll be writing them out in the form of functions for clarity, but you will mostly likely just want to copy paste what’s inside the function to use it.
This list is short for now, but I plan to add more methods as I think of them. Let me know if there’s anything you would like to know how to do and I’ll add them
How to create a vector that points along the direction of some vector
function vec(length, referenceVec) return length * referenceVec.Unit end
Explanation: Returns a vector that points in the direction of
referenceVec and has a magnitude of
math.abs(length). A negative value of
length will result in a vector pointing in the parallel but opposite direction of the reference vector.
How it works:
referenceVec.Unit provides the same vector but with a magnitude of 1. It is then multiplied by
length to stretch it to the desired magnitude (and possibly reflected).
Example use case: You want to launch a bullet out of the barrel of a gun at 1000 studs per second. The bullet’s velocity vector needs to point along the gun barrel and assuming the barrel is a cylinder part, its long axis is its X axis. Your velocity vector would be
velocityVec = vec(1000, barrel.CFrame.XVector).
How to get the part of a vector that points parallel to some other vector
function vec_para(vec, referenceVec) return vec:Dot(referenceVec.Unit) * referenceVec.Unit end
Explanation: Given some reference vector, every vector can be split into the sum of a vector lying entirely along the reference vector, and a vector lying entirely perpendicular to the reference vector:
vec = vec_para + vec_perp. This function returns the part parallel to the reference vector,
vec_para. In math this would be called 'the projection of
How it works:
v1:Dot(v2) is called the dot product of
v2. It multiplies the length of
v2 by the length of the part of
v1 that is parallel to
vec:Dot(referenceVec.Unit) multiplies the length of
referenceVec.Unit (which is 1) by the length of
vec which is along that vector. Then multiplying this to
referenceVec.Unit creates a vector which points along
referenceVec with the length of the part of
vec lying along
referenceVec, which is exactly the projection of
Example use case: You are manually simulating a brick sliding down a ramp due to gravity (you don’t trust roblox physics). Only the part of gravity parallel to the surface of the ramp will work towards accelerating the brick so you need to find that part of of the gravity vector. The total acceleration of gravity is
gravityVec = workspace.Gravity * Vector3.yAxis, and let’s call the vector pointing down the ramp
rampVec. The brick’s net acceleration vector due to gravity is
netAccelerationVec = vec_para(gravityVec, rampVec)
How to get the part of a vector that points perpendicular to some other vector
function vec_perp(vec, referenceVec) return vec - vec:Dot(referenceVec.Unit) * referenceVec.Unit end
Explanation: As in the previous section,
vec is treated as the sum of its parts parallel and perpendicular to
vec = vec_para + vec_perp. This function provides the perpendicular part,
vec_perp. In math this would be called 'the rejection of
How it works: Rearranging the above sum gives
vec_perp = vec - vec_para. Then
vec_para is replaced by its expression from the previous section:
vec_para = vec:Dot(referenceVec.Unit) * referenceVec.Unit
Example use case: You want to do the same ramp simulation as in the previous example, but instead of a vector pointing along the surface of the ramp, you have a vector pointing out of the surface of the ramp,
rampNormalVec. In this case the part of gravity along the ramp would be the rejection of the total gravity vector from this normal vector.
netAccelerationVec = vec_perp(gravityVec, rampNormalVec)
How to get the normal vector of a plane
function normal(vec1, vec2) return vec1:Cross(vec2).Unit end
Explanation: A plane is a 2 dimensional surface which can be defined by a vector called the ‘normal vector’ which is perpendicular to the plane and thus to every vector that lies in the plane. This function returns a unit normal vector of the plane when supplied with 2 independent vectors,
vec2, that lie in the plane. Independent means the vectors can’t be parallel. Keep in mind that the order of
vec2 matters: reversing the order will reverse the direction of the normal (in the below image, vec1 then vec2 gives the + direction, and vec2 then vec1 gives the - direction. Right hand rule).
How it works: For any 2 vectors
v1:Cross(v2) is called the cross product of
v2 and provides a vector which is perpendicular to both of them. The length of this vector is equal to the length of the rejection of
v2. For this function since
vec2 are vectors in the plane,
vec1:Cross(vec2) is perpendicular to both of them and is therefore a normal vector to the plane. Only the direction of the normal matters, not the length, so it’s converted into a unit vector with
Example use case: To find the part of a vector which lies in a plane, take the rejection of the vector from the plane’s normal. This is the part of the vector perpendicular to the normal so it is the part that lies in the plane. If the normal is found as
norm = normal(vec1, vec2) then the part of some vector
vec in the plane is
vec_plane = vec - vec:Dot(norm) * norm
Similarly, the part of
vec which is perpendicular to the plane is found as the projection of
vec onto the normal.
vec_normal = vec:Dot(norm) * norm
How to rotate a vector around a plane/about an axis
function rotatedVec(vec, normalVec, angle) return CFrame.FromAxisAngle(normalVec.Unit, angle) * vec end
Explanation: This takes the part of
vec lying in the plane defined by the normal vector
normal and rotates it counterclockwise (
normal pointing out of the clock) around the plane by
angle, while leaving the part of
vec perpendicular to the plane alone. Equivalently, you can think of
normalVec as the axis of rotation.
How it works:
CFrame.fromAxisAngle(axis, angle) creates a rotation matrix that rotates about the vector
axis (in this application we used
normalVec) by an amount given by
angle. Multiplying this to
vec gives the resultant rotated vector.
Example use case: You have a bunch of players sitting around a table and you want to shoot a random one with a ceiling mounted laser (using Raycast). To get the direction vector you start off with a vector pointing from the ceiling laser to one of the players
defaultDirection. Then to randomize which seat the laser shoots at you rotate it about the Y axis by a random angle to get a randomized direction which still points towards a player.
randomDirection = rotatedVec(defaultDirection, Vector3.Yaxis, 2*math.pi*math.random()).
This vector would be used for the raycast direction vector.
How to find the angle between a vector’s projection onto a plane and some other vector’s projection onto that plane
function angle(vec, referenceVec, normalVec) local unitNorm = normalVec.Unit local vec_p = (vec - vec:Dot(unitNorm) * unitNorm).Unit local refVec_p = (referenceVec - referenceVec:Dot(unitNorm) * unitNorm).Unit local cosine = vec_p:Dot(refVec_p) local sine = vec_p:Dot(unitNorm:Cross(refVec_p)) return sine >= 0 and math.acos(cosine) or -math.acos(cosine)
Explanation: This function projects both vectors onto the plane defined by
normalVec and determines the angle that the
referenceVec projection needs to be rotated around the plane by to point along the projection of
How it works: First,
normalVec is converted to a unit vector for simpler calculation. Then the projections of
referenceVec onto the plane are found as the rejection from
unitNorm. Since angle only depends on direction, the vectors are turned into unit vectors for simpler calculation. The result is
vec_p is decomposed into a right angle triangle with it’s adjacent side pointing along
refVec_p. The cosine of this triangle is just the amount of
refVec_p and that is found with the dot product (if these were not unit vectors, the dot product would need to be divided by their magnitudes). The sine of the triangle is the length of
vec_p perpendicular to
refVec_p and that is found by taking the dot product of
vec_p with the perpendicular vector found with
unitNorm:Cross(refVec_p). (The magnitude of rejection of
refVec_p can’t be used because the information of which side of
refVec_p the sine is on is needed).
math.acos(cosine) is used to extract the angle out of
cosine. However there are 2 possible angles (one positive, one negative) which can return the same cosine. Which one is which depends on whether
sine is negative or not, so the sign of
sine is used to determine the correct angle.
Example use case: You have a player character,
patient, on a surgery table and you need to amputate his left leg. Therefore you need to calculate the angle of his leg relative to the table so you know how to orient your scalpel to make the most precise cut. Assuming the scalpel is normally aligned with the -X axis of the table’s CFrame, that can be used as the reference vector
referenceVec. Then the Y axis of the legs CFrame can be used as the target vector
vec. And finally, the normal to the table is of course the Y axis of it’s CFrame. Then the angle needed to rotate the scalpel around the table surface is
targetAngle = angle(patient.LeftUpperLeg.CFrame.YVector, -table.CFrame.XVector, table.CFrame.YVector)
(Also amputate right leg for good measure)