Understanding CFrames

How CFrames are represented, created, and manipulated.

by Halalaluyafail3

Author Avatar

CFrames are represented as a 3x4 matrix (3x3 rotation matrix and a vector combined):

| r00  r01  r02  x |
| r10  r11  r12  y |
| r20  r21  r22  z |

Whenever a 4x4 matrix is needed, the last row is presumed to be 0,0,0,1

The 3x3 rotation matrix should be orthonormal (columns have a magnitude of 1, and each column is orthogonal to the others).

The x,y,z part (fourth column) of the CFrame represents the position, and the other components represent the rotation.

The first column [r00, r10, r20] represents the direction right relative to the orientation of the CFrame (.XVector). The second column [r01, r11, r21] represents the direction up relative to the orientation of the CFrame (.YVector). The third column [r02, r12, r22] represents the direction forward relative to the orientation of the CFrame (.ZVector).

For each column, roblox has a property to describe it (.XVector, .YVector, .ZVector, .Position (these are all mentioned later)).

When a CFrame is visualized, it makes more sense. In this picture the red arrow is for the right direction, the blue arrow is for the forward direction, the green arrow is for up direction, and the grey cube is for position.

img|55x45

The next sections will be about CFrame methods and properties, as well as methods to create a CFrame.

Creating a CFrame

There are many methods that create a CFrame.

CFrame.new()

Creates a CFrame which when you add the 4th row, becomes the identity matrix.

| 1  0  0  0 |
| 0  1  0  0 |
| 0  0  1  0 |

Position is 0,0,0 Rotation is the identity matrix

CFrame.new(number x,number y,number z)

Creates a CFrame with position x,y,z, rotation stays as the identity matrix.

| 1  0  0  x |
| 0  1  0  y |
| 0  0  1  z |

CFrame.new(Vector3 vector)

Creates a CFrame with the position being the components of vector.

| 1  0  0  vector.x |
| 0  1  0  vector.y |
| 0  0  1  vector.z |

CFrame.lookAt(Vector3 position,Vector3 lookAt[,Vector3 up])

The default for up is Vector3.new(0,1,0).

Creates a CFrame with position being the position argument, and the rotation being how it should be oriented to look at the lookAt argument. The up argument is used for what is "up".

This won't be accurate if the positions are the same, so in that case you will want to use the identity matrix.

CFrame.new(number x,number y,number z,number r00,number r01,number r02,number r10,number r11,number r12,number r20,number r21,number r22)

Creates a CFrame with all provided arguments.

| r00  r01  r02  x |
| r10  r11  r12  y |
| r20  r21  r22  z |

CFrame.new(number x,number y,number z,number qX,number qY,number qZ,number qW)

Creates a CFrame from position x,y,z and quaternion qX,qY,qZ,qW.

CFrame.fromMatrix(Vector3 pos,Vector3 vX,Vector3 vY[,Vector3 vZ])

The default for vZ is vX:Cross(vY).Unit.

Creates a new CFrame with Position pos, XVector vX,YVector vY, and ZVector vZ.

| vX.X  vY.X  vZ.X  pos.X |
| vX.Y  vY.Y  vZ.Y  pos.Y |
| vX.Z  vY.Z  vZ.Z  pos.Z |

CFrame.fromEulerAnglesXYZ(number x,number y,number z) or CFrame.Angles(number x,number y,number z)

Rotation is in radians.

Creates a new CFrame with position 0,0,0 and angles x,y,z.

Angles are applied in Z,Y,X order.

CFrame.fromEulerAnglesYXZ(number y,number x,number z) or CFrame.fromOrientation(number y,number x,number z)

Rotation is in radians.

Creates a new CFrame with position 0,0,0 and angles x,y,z.

Angles are applied in Z,X,Y order.

CFrame.fromAxisAngle(Vector3 v,number r)

Creates a CFrame with position 0,0,0 and angles defined by axis angle v,r.

CFrame Properties

CFrame.Position [Vector3]

The position of the CFrame as a vector.

The elements of the vector are the x,y,z elements of the CFrame.

CFrame.X [number]

Represents the X part of the position vector.

CFrame.Y [number]

Represents the Y part of the position vector.

CFrame.Z [number]

Represents the Z part of the position vector.

CFrame.XVector [Vector3]

Represents the 'rightward' direction of the CFrame.

Elements - r00,r10,r20

CFrame.YVector [Vector3]

Represents the 'upward' direction of the CFrame.

Elements - r10,r11,r12

CFrame.ZVector [Vector3]

Represents the 'forward' direction of the CFrame.

Element - r20,r21,r22

CFrame.RightVector [Vector3]

Represents the 'rightward' direction of the CFrame.

Elements - r00,r10,r20

CFrame.UpVector [Vector3]

Represents the 'upward' direction of the CFrame.

Elements - r01,r11,r21

CFrame.LookVector [Vector3]

Represents the negative 'forward' direction of the CFrame.

Elements - -r02,-r12,-r22

CFrame Math

Helpful link:

developer.roblox.com/en-us/articles/CFrame-Math-Operations

CFrame+Vector3 [returns: CFrame]

Adds the elements of the vector to the position part of the CFrame.

| r00  r01  r02  x+vector.X |
| r10  r11  r12  y+vector.Y |
| r20  r21  r22  z+vector.Z |

CFrame-Vector3 [returns: CFrame]

Subtracts the elements of the vector from the position part of the CFrame.

| r00  r01  r02  x-vector.X |
| r10  r11  r12  y-vector.Y |
| r20  r21  r22  z-vector.Z |

CFrame*Vector3 [returns: Vector3]

Moves the CFrame according to the rotation of the CFrame and the Vector3, which can be though of as an offset.

local function cftimesv3(cf,v3)
    return  cf.Position+
            cf.XVector*v3.X+
            cf.YVector*v3.Y+
            cf.ZVector*v3.Z
end

The linked article at the beginning of this section does a good job of explaining what is being done.

CFrame*CFrame [returns: CFrame]

This can be thought of as 4x4 rotation matrix multiplication. CFrames don't store the 4th row, but it assumed to be 0,0,0,1.

It is important to note that matrix multiplication isn't commutative. So a times b most likely won't equal b times a

The first cframe will be represented as

| r00  r01  r02  rX |
| r10  r11  r12  rY |
| r20  r21  r22  rZ |

and the second cframe as

| m00  m01  m02  mX |
| m10  m11  m12  mY |
| m20  m21  m22  mZ |

result:

| r00*m00+r01*m10+r02*m20  r00*m01+r01*m11+r02*m21  r00*m02+r01*m12+r02*m22  r00*mX+r01*mY+r02*mZ+rX |
| r10*m00+r11*m10+r12*m20  r10*m01+r11*m11+r12*m21  r10*m02+r11*m12+r12*m22  r10*mX+r11*mY+r12*mZ+rY |
| r20*m00+r21*m10+r22*m20  r20*m01+r21*m11+r22*m21  r20*m02+r21*m12+r22*m22  r20*mX+r21*mY+r22*mZ+rZ |

CFrame Functions

'Vector3s' refers to 1 or more Vector3s

'CFrames' refers to 1 or more CFrames

CFrame:GetComponents() [returns: 12 numbers]

Returns all 12 components of the CFrame in this order.

x,y,z,r00,r01,r02,r10,r11,r12,r20,r21,r22

The first three are the position. The fourth, seventh, and tenth numbers are the XVector. The fifth, eighth, and eleventh numbers are the YVector. The sixth, ninth, and twelfth numbers are the ZVector.

CFrame:Inverse() [returns: CFrame]

Returns the inverse of the CFrame, or returns the CFrame that when multiplied with the original CFrame will result in the identity matrix. The following expressions are equivalent:

A:Inverse()*A

A*A:Inverse()

CFrame.new()

CFrame:ToWorldSpace(CFrames cf) [returns: CFrames]

Equivalent to

CFrame*cf

For each argument

CFrame:ToObjectSpace(CFrames cf) [returns: CFrames]

Equivalent to:

CFrame:Inverse()*cf

For each argument

This will often be useful when you need to solve for B in something like this:

A*B = C

where A is the first CFrame and C is the second CFrame.

B will be A:Inverse()*C because

A*B = C
-- Multiply each side by A inverse
A^-1*A*B = A^-1*C
-- A^-1*A = I
-- I*B = B
B = A^-1*C
-- or with :Inverse()
B = A:Inverse()*C

CFrame:PointToWorldSpace(Vector3s v3) [returns: Vector3s]

Equivalent to:

CFrame*v3

For each argument

CFrame:PointToObjectSpace(Vector3s v3) [returns: Vector3s]

Equivalent to:

CFrame:Inverse()*v3

For each argument

CFrame:VectorToWorldSpace(Vector3s v3) [returns: Vector3s]

Equivalent to:

(CFrame-CFrame.Position)*v3

For each argument

CFrame:VectorToObjectSpace(Vector3s v3) [returns: Vector3s]

Equivalent to:

(CFrame:Inverse()-CFrame:Inverse().Position)*v3

For each argument

CFrame:ToEulerAnglesXYZ() [returns: 3 numbers]

Converts the rotation matrix to euler angles.

Angles returned are in radians.

Applying the angles returned in Z,Y,X order will create approximately the same the rotation.

The first result is the X component of the rotation. The second result is the Y component of the rotation. The third result is the Z component of the rotation.

CFrame:ToEulerAnglesYXZ() or CFrame:ToOrientation() [returns: 3 numbers]

Converts the rotation matrix to euler angles.

Angles returned are in radians.

Applying the angles returned in Z,X,Y order will create approximately the same the rotation.

The first result is the X component of the rotation. The second result is the Y component of the rotation. The third result is the Z component of the rotation.

CFrame:ToAxisAngle() [returns: Vector3, number]

Converts the rotation matrix to axis angle.

CFrame:Lerp(CFrame goal,number alpha) [returns: CFrame]

Linearly interpolates between CFrame and goal by amount alpha.

CFrame:Orthonormalize() [returns: CFrame]

Converts the CFrame to a CFrame which will be an orthonormal CFrame.

Additional links

How to think about CFrames (by AxisAngle)

devforum.roblox.com/t/how-to-think-about-cframes/11743

I mentioned quaternions briefly, but here is another article about quaternions and rotation (by suremark)

devforum.roblox.com/t/rotations-with-quaternions/13209

View in-game to comment, award, and more!