How to rotate around a pivot point using Lerp

I am currently trying to make a door. I want to smoothly tween the door using Lerp. I can get it to tween but not the way I would like. I need it to rotate around the hinge but instead of rotating around the hinge it slides the door and rotates at the same time and the door ends up going through the door frame post. Here is a video of my problem

any idea how I can get lerp to just rotate around the pivot point? Here is my code so far, thanks

local startCFrame = workspace.Door.CFrame
local door = workspace.Door
local endCFrame = door.CFrame * CFrame.new(0, 0, door.Size.Z/2) *CFrame.Angles(0, 
math.rad(90), 0) *CFrame.new(0, 0, -door.Size.Z/2)
local desiredDuration = 2
local durationReciprocal = 1/desiredDuration
local startTime = tick()

local steppedEvent

steppedEvent = game["Run Service"].Stepped:Connect(function()
    local timeElapsed = tick() - startTime
    local alpha = timeElapsed * durationReciprocal
    local newCFrame = startCFrame:Lerp(endCFrame, alpha)
    workspace.Door.CFrame = newCFrame

    if alpha >= 1 then
       steppedEvent:Disconnect()
	   workspace.Door.CFrame = endCFrame
    end
end)
1 Like

The easiest way to do that is by editing the CFrame.Angles component from endCFrame to create your new door’s CFrame every frame:

local door = workspace.Door

-- you'll see why I use numbers later on,
local startAngle = 0
local endAngle = math.rad(90)
startCFrame = door.CFrame

local sizeZ = CFrame.new(0, 0, door.Size.Z/2)

local duration = 2

local steppedEvent
local alpha = 0
steppedEvent = game:GetService("RunService").Stepped(function(gameTime, timeElapsed)

	-- incrementing alpha by time passed / duration, the way I usually do it
	alpha = math.min(alpha + timeElapsed/duration, 1)
	-- also clamps at 1 so you don't have to worry about repositioning

	-- lerping to the new angle
	local newAngle = (endAngle - startAngle) * alpha + startAngle

	-- from the creation of your endCFrame
	local newCFrame = startCFrame
		* sizeZ -- CFrame.new(0, 0, door.Size.Z/2)
		* CFrame.Angles(0, newAngle, 0)
		* sizeZ:Inverse() -- CFrame.new(0, 0, -door.Size.Z/2)

	door.CFrame = newCFrame

	if alpha >= 1 then
		steppedEvent:Disconnect()
	end
end)

In theory, sizeZ and sizeZ:Inverse() should work in the newCFrame formula, but you can just use the commented ones if they do something weird.
If you want to make your tween even smoother, you can use TweenService:GetValue to get an alpha value in the inputted easing style and easing direction.

3 Likes

I seem to have a problem it only works if the script runs right when the game starts, if I disable and enabled it again it jumps to the next position without tweening.

1 Like

I just realized I made a couple errors:

  1. If you’re running this on the server, RunService wouldn’t have a RenderStepped event, you would have to use Stepped like your code or Heartbeat.
  2. I made a local alpha every frame instead of assigning to global alpha, which made the door stay at one angle the entire time. You can just take out the local in local alpha = math.min(alpha + elapsedTime/duration, 1)

I just tested this in my own place, it should work now.

2 Likes