Question on Offset Door + Hinge CFrame Math Operators Article

Code from this article

local door = script.Parent.Door
local hinge = script.Parent.Hinge

local offset = hinge.CFrame:inverse() * door.CFrame;

game:GetService("RunService").Heartbeat:connect(function(dt)
	hinge.CFrame = hinge.CFrame * CFrame.Angles(0, math.rad(1)*dt*60, 0)
	
	door.CFrame = hinge.CFrame * offset
end)

Regarding the offset, how come it doesn’t have to be redifined everytime the hinge is rotated, for example wouldn’t it make more sense if it would only work if it was inside the heartbeat function as the CFrame of the hinge changes every heartbeat making it so the offset value also has to change?:

local door = script.Parent.Door
local hinge = script.Parent.Hinge

local offset

game:GetService("RunService").Heartbeat:connect(function(dt)
	hinge.CFrame = hinge.CFrame * CFrame.Angles(0, math.rad(1)*dt*60, 0)
	
	offset = hinge.CFrame:inverse() * door.CFrame;
	door.CFrame = hinge.CFrame * offset
end)

I think I have misunderstood the nature of CFrames, can someone explain how this works?

The offset would typically be a constant value

For example, if the door was 2 studs to the right of the hinge when the game began, offset would equal CFrame.new(2,0,0), and it should always be this value because you always want to keep the same offset, 2 studs to the right of the hinge, as the hinge rotates

When the code does:

door.CFrame = hinge.CFrame * offset

It applies the offset relative to the hinge, because multiplying two cframes will apply the second cframe relative to the first

So, basically, you find the offset at the start and maintain that offset as the hinge rotates by multiplying the constant offset onto the hinges cframe

If you defined the offset as the hinge rotated, you would be finding a new offset every frame, telling the code how to keep the door exactly where it is in global space, which isnt what you want, you want to keep the door exactly where it is relative to the hinge

You can see this through the cframe math

offset = hinge.CFrame:inverse() * door.CFrame;
door.CFrame = hinge.CFrame * offset

Is the same as

door.CFrame = hinge.CFrame * hinge.CFrame:inverse() * door.CFrame

Now, because inverse finds the opposite of a cframe, when you multiply the two together they simply cancel out, leaving you with

door.CFrame = door.CFrame

Which isnt really that useful in this case

note that this doesnt apply to the first, correct example because the hinge has been rotated from the original hinge cframe saved in the variable,
It is true that when the hinge in the variable is the same as the current hinge cframe, aka the hinge hasnt been rotated, the doors current cframe will also be equal to its original position
But as soon as its rotated, this rotation is accounted for and the door moves correctly

Hopefully that all makes sense, not all of it is relevant but just in case you wanted a more complete description
Im willing to clear anything up, Im bad at explaining things and tend to overcomplicate things before I undercomplicate them

1 Like

Thanks, gave this a good read and I appreciate the depth you put in and understood it clearly.

1 Like

However I still don’t understand this part as to why a new CFrame (offset) cannot be found as the hinge rotates.

When you do

hinge.CFrame:inverse() * door.CFrame

Youre getting the current cframe of the door relative to the current cframe of the hinge

This would happen after the hinge has moved, but before the door has moved
So basically youre getting the offset from the wrong door position to the hinge and setting the doors cframe to that offset, making it not work

1 Like