#CFrames are how ROBLOX represents the 3D position and orientation of objects in the game world.
Understanding how these work can lead to easier to write, easier to read, and overall more efficient CFrame code.
If you’ve ever done something like:
print(CFrame.new())
you know that it gives you 12 numbers. What do these numbers mean?
Well before we start, let’s think about CFrame.lookVector. lookVector is a unit vector (vector with a magnitude of 1) that points in the direction the CFrame is facing. A CFrame actually contains three more of these lookVectors, except instead of pointing in the forward direction, they point to the right, top and back of the CFrame (See picture below).
These vectors, the one positional vector, and the three directional vectors, the Right Vector, Top Vector and Back Vector, can be acquired through CFrame:components().
Keep in mind the Right, Top, and Back Vectors are NOT the positions of the centers of the faces on a Part. They are directions. Not positions.
Keep in mind that the three directional vectors are unit length.
Keep in mind that the Position is the same as CFrame.p, and if it’s in a Part, then it is also equal to Part.Position.
The three directional vectors make up something called a rotation matrix. The rotation matrix describes the 3D orientation (not position), of the CFrame.
And example of the numbers you might find in a CFrame are:
5,10,3,
1,0,0,
0,1,0,
0,0,1
A bit of code to get these into a nice format is:
local function DeconstructCFrame(CFrame)
local px,py,pz,xx,yx,zx,xy,yy,zy,xz,yz,zz=CFrame:components()
local Position=Vector3.new(px,py,pz)
local Right=Vector3.new(xx,xy,xz)
local Top=Vector3.new(yx,yy,yz)
local Back=Vector3.new(zx,zy,zz)
return Position,Right,Top,Back
end
local Pos,RightVec,TopVec,BackVec=DeconstructCFrame(CFrame.new())
##What does it mean to say CFrame * Vector3?
A Vector3 is made up of three components, x y and z. These determine where the Vector3 is in the game world coordinate grid, which is how the game world defines positions.
Changing the x component causes the Vector3 to move along the right/left axis of the coordinate grid.
Changing the y component causes the Vector3 to move along the up/down axis of the coordinate grid.
Changing the z component causes the Vector3 to move along the backward/forward axis of the coordinate grid.
If the Vector3 is <0, 0, 0>, then the Vector3 is at the origin of the coordinate grid.
When you say, CFrame * Vector3, you’re telling the Vector3 to take on a coordinate grid defined by the CFrame.
In other words, now that right/left axis is defined as the Right Vector of the CFrame.
The up/down axis is defined as the Top Vector of the CFrame
The backward/forward axis is defined as the Back Vector of the CFrame.
The origin is defined as the Position of the CFrame
CFrame0 * CFrame1 does exactly the same thing, except the orientation part of CFrame1 is also transformed.
You can accomplish this by hand without the use of CFrames with some code like this using the DeconstructCFrame function:
local function CFrameTimesVector3(CFrame,Vector3)
local Pos,RightVec,TopVec,BackVec=DeconstructCFrame(CFrame)
local TransformedVector3=Pos+RightVec*Vector3.x+TopVec*Vector3.y+BackVec*Vector3.z
return TransformedVector3
end
Or, a little more complexly, but waaay more efficiently, like:
local function CFrameTimesVector3(CFrame,Vector)
local px,py,pz,xx,yx,zx,xy,yy,zy,xz,yz,zz=CFrame:components()
local vx,vy,vz=Vector.x,Vector.y,Vector.z
local x=px+xx*vx+yx*vy+zx*vz
local y=py+xy*vx+yy*vy+zy*vz
local z=pz+xz*vx+yz*vy+zz*vz
return Vector3.new(x,y,z)
end
##What does it mean to say CFrame:inverse() * Vector3?
It really just does the opposite of CFrame * Vector3
If we say that CFrame * Vector3 multiplies a Vector3 out of a CFrame, then we can say that CFrame:inverse() * Vector3 divides a Vector3 into a CFrame.
Mathematically,
Vector3B = CFrame * Vector3A
Vector3A = CFrame:inverse() * Vector3B
We can use this formula when we want to get what a Vector3’s coordinates are relative to a coordinate grid defined by a CFrame.
With Vector3 in the game world’s coordinate grid:
The x element tells us how far right the Vector3 goes.
The y element tells us how far up the Vector3 goes.
The z element tells us how far back the Vector3 goes.
If we want to do the opposite of CFrame*Vector3, we need to figure out how far the Vector3 goes along each of the directional vectors of the CFrame.
In order to determine how far a VectorA goes along VectorB, we can make use of Dot Product.
How Far VectorA Goes Along VectorB = (VectorA) dot (Unit(VectorB))
Keep in mind that because the directional vectors of the CFrame are already unit length, we didn’t need to unitize them.
You can accomplish this by hand without the use of CFrames with some code like this using the DeconstructCFrame function:
local function CFrameTimesVector3(CFrame,Vector)
local Pos,RightVec,TopVec,BackVec=DeconstructCFrame(CFrame)
local RelativeVector3=Vector-Pos
local HowFarRight=RelativeVector3:Dot(RightVec)
local HowFarUp=RelativeVector3:Dot(TopVec)
local HowFarBack=RelativeVector3:Dot(BackVec)
local TransformedVector3=Vector3.new(HowFarRight,HowFarUp,HowFarBack)
return TransformedVector3
end
Or, a little more complexly, but waaay more efficiently, like:
local function CFrameTimesVector3(CFrame,Vector)
local px,py,pz,xx,yx,zx,xy,yy,zy,xz,yz,zz=CFrame:components()
local vx,vy,vz=Vector.x,Vector.y,Vector.z
local rx,ry,rz=vx-px,vy-py,vz-pz--r for Relative
local HowFarRight=rx*xx+ry*xy+rz*xz
local HowFarUp=rx*yx+ry*yy+rz*yz
local HowFarBack=rx*zx+ry*zy+rz*zz
return Vector3.new(HowFarRight,HowFarUp,HowFarBack)
end
If you understand this, then CFrame0:inverse()*CFrame1 does exactly as you would expect.
I’m sure you guys know what to do with this and what you can replace with this. At least you have a better understanding of what the numbers in the CFrame mean.
####If you want to share this with your non-RBX Dev buddies, I’ve compiled a big PNG of it here: AxisAngle's Guide to CFrames - Imgur