We use cookies on this site to enhance your user experience

Understanding CFrame

Understanding CFrame

Jul 30 2018, 2:12 PM PST 5 min

Inverse CFrames

Note that what we’re dealing with here is really Matrices, every DataType/CFrame|CFrame is just a rotation matrix + a position, but we’ll be calling them CFrames for simplicity.

You’re already familiar with the fact that for every mathematical operation there is a special quantity which is “neutral with respect to” that operation. For example:
For addition that number is 0, that is, every number plus zero will still be that number.
For multiplication that number is 1, that is, every number times one will just be that number.

Well, CFrames as you might already know, have multiplication defined on them in a very special way. However, this special form of multiplication also has a neutral element just like addition and multiplication of normal numbers. DataType/CFrame|CFrame.new() happens to return that value when you give it no arguments. And that makes sense, right? You wouldn’t want to get back some arbitrary CFrame from .new(), you want a CFrame that does nothing when you apply it to other CFrames. In the case of a Part’s CFrame that would mean not moving the part anywhere for example.

That is, we have the following identity for any CFrame “cf”:

cf * CFrame.new() == CFrame.new() * cf == cf

Now, what about inverse operations? Most mathematical operations have some sort of inverse. For example, the inverse with respect to addition is taking the negative of a number, and the inverse with respect to multiplication is taking 1/the value. But what exactly is an inverse operation doing? What any inverse is really doing when applied to an element x is satisfying the following identity:
x OPERATION inverse_with_respect_to_operation(x) == the neutral element

That is to say, if you apply the operation to an element and it’s inverse with respect to
that operation, you get the neutral element with respect to that operation. For example, with addition and multiplication:

x + (-x) == 0
x * (1 / x) == 1

But what if you apply this same logic to DataType/CFrame|CFrames? There is a slight complication with the fact that multiplication of CFrames is “not commutative”, that is to say if you take two frames “a” and “b”, “ab" won’t necessarily be the same thing as "ba”, that is, it matters what order you do the CFrame multiplication in. However, aside from needing to be more careful in keeping our order constant, that doesn’t change the way inverses work for CFrames: DataType/CFrame|CFrame:inverse() will still give you the inverse of a DataType/CFrame|CFrame. That value you get back from inverse will satisfy the same identity as before. If you take a CFrame “cf”:

cf * cf:inverse() == cf:inverse() * cf == CFrame.new()

Note that here commutativity does hold, the multiplication of the CFrames does work both ways for CFrames and their inverses, but ONLY IN THIS CASE, normally it won’t give you the same result if you switch the order of a CFrame multiplication.

So, now, what if we apply that to a common situation: Finding out what C0 or C1 you need for a weld to make it work. It turns out all you need to know is a bit of basic algebra, and this understanding of CFrame multiplication to do it. The first thing you need to know is that all joints in Roblox satisfy the following identity:

Part1.CFrame * C1 == Part0.CFrame * C0 * joint_effect()

Where joint_effect() is whatever the joint needs to do, for example in the case of welds it does nothing at all, but for motors it’s equal to an extra rotation DataType/CFrame|CFrame around a specific axis. Now suppose we know where to parts are, and we set the C0 to something specific, but now we want to find out what C1 to use to not move the parts, how do we do it? It turns out not to be that hard. We just start with the identity (I’ve just called Part0/1.CFrame Part0/1 here for brevity):

Part1 * C1 == Part0 * C0

Now, just like you would with normal algebra, we’re going to multiply something to both sides of the equation. The only thing to be careful of we have to multiply the thing to the same side of each half of the equation since order matters for CFrames. Remember, we’re trying to isolate the “C1” variable since that’s what we want to find. To do this we can use Part1:inverse():

Part1:inverse() * Part1 * C1 == Part1:inverse() * Part0 * C0

Now, remember what we said about inverses? If you multiply a DataType/CFrame|CFrame and it’s inverse then you get DataType/CFrame|CFrame.new() back every time, so we can replace Part1:inverse()*Part1 with just CFrame.new():

CFrame.new() *C1 == Part1:inverse() * Part0*C0

But then also remember what we said about the “neutral element” with respect to any operation. DataType/CFrame|CFrame.new() is the neutral element, so multiplying C1 by it should have no effect whatsoever. That means we can remove it without breaking the equality:

C1 == Part1:inverse() * Part0 * C0

And we’ve successfully isolated the required C1 just like that. You can now plug the known values for Part1, Part0 and C0 into the multiplication to get out the C1 that you need.

And it’s really as simple as that. By moving values around using inverse you can easily perform whatever algebra you need in order to find DataType/CFrame|CFrames. For example, rotating a bunch of parts around an origin becomes easy with this knowledge:

We have an origin, we need to know how far the part is displaced from the origin, so:

part.CFrame = origin * displacement


displacement = origin:inverse() * part.CFrame

and then we just need to apply the rotation to the origin before moving the part back:

part.CFrame = origin * rotation * displacement