I’m currently making a first person gun and to achieve an ADS effect I’m using lerp. I can easily do this but the part that I’m having trouble with is perfectly timing the lerp. I have a variable called “aimSpeed” that is how long I want the lerp to take to finish in seconds but I can’t seem to make the lerp sync up with the variable time. Here was my shot at it:
function Aim(self, delta)
if aiming then
aimCountDown = 0
aimCFrame = CFrame.new():Lerp(aimOffset, aimCount/100) -- CFrame.new() so that it will lerp properly
aimCount += 1 + (delta * 100) -- multiply delta by 100 because we divided by 100 in RenderStepped() function because lerp is from 0 - 1
else
if aimCountDown == 0 then
aimCountDown = 100 - aimCount
end
aimCount = 0
aimCFrame = aimOffset:Lerp(CFrame.new() , aimCountDown/100)
aimCountDown += 1 + (delta * 100) -- multiply delta by 100 because we divided by 100 in Aim() function because lerp is from 0 - 1
end
aimCount = math.clamp(aimCount, 0, 100)
aimCountDown = math.clamp(aimCountDown, 0, 100)
end
function ViewModel:RenderSteppedUpdate()
self.Model.PrimaryPart.CFrame = camera.CFrame * aimCFrame
if tick() - deltaTime >= aimSpeed / 100 then
Aim(self, (tick() - deltaTime) - (aimSpeed / 100))
deltaTime = tick()
end
end
This code works fine when the variable is 1, finishing the lerp at approximately 1.01 seconds. But if I were to increase the variable to 2 the lerp finishes at approximately 1.433 seconds and that number gets more inaccurate the more I increase “aimSpeed”.
Part of the problem is that you’re always adding 1 every frame which is not scaled by the delta.
Also you’re using aimSpeed to check how long the aim has been going for, but also you’re subtracting it from the frame time or something?
Take a step back and simplify things for yourself—first, RunService | Roblox Creator Documentation (which I’m assuming you bind this function to) gives you the time since the last frame, so we can use that parameter instead of calculating it ourselves.
function ViewModel:RenderSteppedUpdate(dt)
Instead of keeping track of two variables for up/down like this, it will be cleaner to just keep track of one and add/subtract to it.
Also, we can just make that one variable always be in the range 0 and 1 so we can use it directly in the Lerp. All we have to add to (or subtract from!) it is aimSpeed * delta.
There’s some other issues like Aim should probably just be a method instead of explicitly taking in a self parameter, or the fact that you could do this with TweenService and save yourself some trouble, and that aimCount should really be a class property rather than a global variable, but we can save that for later
Putting it together:
function Aim(self, delta)
if aiming then
aimCount += delta * aimSpeed
if aimCount > 1 then aimCount = 1 end
else
-- reusing same aimCount variable for down, just subtracting
aimCount -= delta * aimSpeed
if aimCount < 0 then aimCount = 0 end
end
aimCFrame = CFrame.new():Lerp(aimOffset, aimCount) -- using aimCount directly
end
function ViewModel:RenderSteppedUpdate(delta) -- new parameter we will get from BindToRenderStep
Aim(self, delta)
self.Model.PrimaryPart.CFrame = camera.CFrame * aimCFrame
end
While the new code did clean up my code a little bit, it isn’t exactly what I want. I want the aimSpeed to be how long the lerp takes to complete in seconds. So for example if aimSpeed was 5 I want the alpha of the lerp to reach 0 to 1 in 5 seconds. That’s the part that I’m having trouble on.
“self” is a parameter of Aim() because I want to be able to use variables inside of the constructor function without Aim() appearing as a method when the module is required from a LocalScript. Is this bad practice? If so how can I use properties from the constructer function without it appearing as a method? Also, you mentioned
This module should be required from a LocalScript and not a ServerScript so if the variable is changed, it won’t be affected for every other script that requires the module. Is this ok? Or should I change aimCount to a class property like you mentioned?
Nah, it’s not bad practice. That reasoning makes sense to me!
Your reasoning makes sense for now—there will only ever be a single ViewModel per player—but it’s still a good idea to move it into the ViewModel instance (the self object).
Maybe later, you decide that it actually does make sense for there to be more than one ViewModel per client. Maybe you’re making a killcam or spectator cam or something. Now all of a sudden, all the ViewModels are going to fight over the same aimCount, and you’re going to get all kinds of unexpected bugs.
If you’re going with OOP, individual instances should not affect each other in unexpected ways. It helps you prevent bugs.