CFrame lerp support (mask function)

SO I am trying to use CFrame lerp in order to move a weld as CFrame lerp is the smoothest, I don’t want tweening or any other method and for loops don’t even seem to do the trick considering they just keep making the mask glitch.

You should use RenderStepped.

Another thing you should do is synchronize the loop so that the Lerp will factor in the time since the last renderstep (delta time)

I believe Heartbeat would work more effectively.

As Crazyman32 once said:

Well animating a physical object is technically “orienting” an object in the 3D world.

1 Like

I know your problem. You are declaring the starting point each time you loop it, meaning that it will keep getting slower and slower…

mask.C0 = STARTPOINT:Lerp(…, …)

Don’t change the startpoint argument or it will keep getting slower

Initialize it somewhere

Before the loop:

local startpoint = mask.whatever_this_is.C0

In the loop:

mask.whatever_this_is.C0 = startpoint:Lerp(…, …)

Does this help?

Not really because you’re just assigning the variable the same

The startpoint should be initialized, because that’s where it originally started… It’s the same for the first loop.

For what it’s worth TweenService is really good at this exact thing. You’re more or less just writing your own tween by using RunService like that (as a for loop). You’ll wanna be careful with connections like that too since you’re not cleaning them up after and they’ll just build up forever. The code snippet below is functionally the same as what you’re doing but with a little more control.

local tweenService = game:GetService("TweenService")

-- set these, they'll be the goals for the mask opening and closing
-- you can get better control over the look with tweenInfo too
-- https://developer.roblox.com/api-reference/datatype/TweenInfo
local upStyle = tweenInfo.new(...)
local upC0 = CFrame.new(...)

local downStyle = tweenInfo.new(...)
local downC0 = CFrame.new(...)

-- state and cleanup variables
local maskUp = false
local lastTween = nil;

local function toggleMask()
	local style = upStyle
	local goal = upC0

	if maskUp then
		style = downStyle
		goal = downC0
	end

	maskUp = not maskUp

	-- clean up the last tween
	-- pausing will freeze the old one if its playing
	if lastTween then
		lastTween:Pause()
		lastTween:Destroy()
	end

	-- create and play the new one
	local tween = tweenService:Create(maskJoint, style, {C0 = goal})
	tween:Play()

	-- for cleaning
	lastTween = tween
end
2 Likes

The problem is the connections you make every time the function runs never clean up, and those connections have no “end state” they can reach so they’ll always lerp the weld. It’s why it lerps right one, and then only goes part way, less and less, every other time.

-- this is just 1
local step = 0.01 / 0.01

-- these are never incremented
local loopCount = 5
local count = 0

local pos = CFrame.new(...)
maskUp = true

-- this never gets cleaned up, so it never stops trying to reach its goal
runService.Heartbeat:Connect(function()
	-- this is always true, you never increment count
	if count < loopCount then

		-- 1 / 0.01 is 100, that value should be between 0 and 1
		weld.C0 = weld.C0:lerp(pos, step / 0.01)
	end
end)

I’m assuming loopCount and count are designed up be used kind of like a for loop, and if that’s the case you can just do.

for count = 1, loopCount, 1 do
	runService.Heartbeat:Wait()
	weld.C0 = weld.C0:lerp(pos, step / 0.01)
end

Fixing up that lerp percentage value to represent the percent-done of the loop would be more ideal than the current value given.

for count = 1, loopCount, 1 do
	runService.Heartbeat:Wait()
	weld.C0 = weld.C0:lerp(pos, count / loopCount)
end

But again. You’re more or less remaking TweenService here, and I’d really recommend just using that instead. Making your own tweening can be beneficial in some cases but for simple things it’s best to just use TweenService.

2 Likes

If the start point is 0, and the end point is 1, and the alpha value is 0.5:

1st Loop: the point will get to 0.5
2nd Loop: the point will get to 0.75
3rd Loop: the point will get to 0.875
And so on…

Which shows why there is a problem with slowing down. I also recommend Tween Service for this particular case because there is a defined end goal with the intentions of moving at a linear speed.

Well anyways, I think I’m stuck with using Tween Service, thanks for your help.

1 Like

Well, it’s a simply fix if you initialize a start point CFrame. Then it will run at a linear speed.

Slowing Down:
Weld.C0 = Weld.C0:Lerp(Goal, Alpha)

Linear Speed:
Weld.C0 = StartCFrame:Lerp(Goal, Alpha)

It’s not as smooth as tweenService and gets ruined, but I think it’s better to use tweenservice now because now it’s working better than what I had created before with cframe lerp

You’re not wrong but that’s not what’s happening in these gifs. In the code snippets he posted there isn’t an issue with the “creeping lerp” approach of taking the current C0 and lerping it by a fixed amount. It’ll slow down but it won’t give that kind of behavior in the gif(s).

The problem is every time the function runs its creating a Heartbeat connection that never resolves and it’ll always attempt make the weld lerp to the goal. The next time the it runs, it does the same thing but in a new direction, and it doesn’t clean up the old connection.

Spam the function a bunch and you end up with the behavior seen in the gifs where the speed is super slow. You end up with X heartbeat threads fighting over a joint, pulling it back and forth.

1 Like
local c0 = Weld.C0
		local alpha = 0
		local duration = 0.5
		while true do
			renderstepped:wait()
			if alpha >= 1 then break end
			alpha = math.min(1, alpha + (1 / (run.framerate * duration)))
			local target = CFRAME GOAL
Weld.C0 = c0:Lerp(target, (math.min(1, alpha + (1 / run.framerate * duration))))
		end

local run = {}

runservice.RenderStepped:connect(function()
	local newtime = tick()
	run.dt = newtime - run.time
	run.time = newtime
	run.framerate = 1 / run.dt
	
			
end)

But I’m using it for a server script not local, so renderstepped in not in the equation, but thanks for
the effort, I guess tweenservice is pretty good for me right now.
I’ll try using that as well for my script, and I’ll give feedback right now.

Instead of RenderStep, you can replace it with Heartbeat.

Ok let me try that right now.
What’s the run.framerate for?
Because there’s no variable for that

1 Like

I took the code out of the game I am developing right now. The run.framerate is unnecessary, but it’s cool if you want to see the game’s framerate printed or something.