Help with creating better weapon sway?

I have a very simple weapon script. It uses lerp for, you know, lerping the viewmodel to the camera. However, it sometimes gets very jittery and kind of breaks when the player moves too fast. Here’s my code:

runServ.RenderStepped:Connect(function()
	--viewer:SetPrimaryPartCFrame(viewer:GetPrimaryPartCFrame():Lerp(camera.CFrame * Main, 0.4))
	if PrimaryEquipped == true then
		viewer:SetPrimaryPartCFrame(viewer:GetPrimaryPartCFrame():Lerp(camera.CFrame * Main, 0.4))
		
		if aiming == true and PrimaryEquipped == true then
			Main = Main:Lerp(CFrameData.ads, 0.1)
			viewer:SetPrimaryPartCFrame(viewer:GetPrimaryPartCFrame():Lerp(camera.CFrame * Main, 0.85))
		elseif PrimaryEquipped == true then
			Main = Main:Lerp(CFrameData.main,.4)
		end
	end
end)

I’ve been looking into better weapon sway, but I’m not skilled enough to understand them completely.

Any help would be great!

You should look into TweenService

Would that completely stop the jitter?

(30 characterssssssssss)

It might be jiterry because you’re lerping it every RenderStepped, could you not Connect to an event instead?

Like Humanoid.Running

1 Like

Well, isn’t RenderStepped an event? Or have I been misinformed? The numbers like 0.6 on the lerp are like the delays. How fast it lerps to the camera.CFrame . If I change the 0.6 to 1, the jitter goes away completely, but looks very robotic.

RenderStepped is an Event.

RenderStepped runs on a variable frequency (see here), so if your FPS is 60, RenderStepped will run at 60hz, and thus will fire every 1/60th of a second.

If it is running every 1/60th of a second, I’m very certain that the sway will be overridden multiple times; which may produce jittery results.

1 Like

So why would Humanoid.Running be better? and wouldn’t that just lerp it once? Or should I use bind to renderStep or something like that? Not very good with anything other than .RenderStepped

If you don’t want it firing as much as upto every 1/60th of a second and the sway only happens when the character runs then Humanoid.Running may be a better option.

However, the solution that comes to mind is to simply setup a debounce till the lerp is over.
Also, I would suggest using tweenservice instead of lerping.

local t = game:GetService("TweenService")
local debounce = false

local tweenInfo = TweenInfo.new(0.1,Enum.EasingStyle.Linear,Enum.EasingDirection.Out,0,false,0)

local tween = t:Create(part,tweenInfo,goal)
tween.Completed:Connect(function()
       debounce = false
end)

runServ.RenderStepped:Connect(function()
   if not debounce then
      debounce = true
      -- run tween
      tween:Play()
   end
end)
1 Like

Thanks! I’ll make sure it works then mark it as a solution. I’ll look more into it!

So I’ve tried it out, and I get the error: Unable to cast to Dictionary

Here’s my code:

local TweenDb = false

local tweenInfo = TweenInfo.new(0.1,Enum.EasingStyle.Linear,Enum.EasingDirection.Out,0,false,0)

local part = viewer.PrimaryPart.CFrame
local goal = Camera.CFrame

local tween = TweenServ:Create(part,tweenInfo,goal)
tween.Completed:Connect(function()
       debounce = false
end)

runServ.RenderStepped:Connect(function()
   if not debounce then
      debounce = true
      -- run tween
      tween:Play()
   end
end)

Note that I have edited the names of stuff and moved the location of the TweenService variable.

EDIT: Did not mean to reply to myself lol.
@Ra_f

part would just be the part, the goal would be {CFrame = Camera.CFrame}

1 Like

I still get the same error :frowning_face:

(30 charactersssssssssssssssssss)

Could you paste the updated code?

1 Like

Here ya go:

local TweenDb = false

local tweenInfo = TweenInfo.new(0.1,Enum.EasingStyle.Linear,Enum.EasingDirection.Out,0,false,0)

local part = viewer.PrimaryPart
local goal = Camera.CFrame

local tween = TweenServ:Create(part,tweenInfo,goal)
tween.Completed:Connect(function()
       debounce = false
end)

runServ.RenderStepped:Connect(function()
   if not debounce then
      debounce = true
      -- run tween
      tween:Play()
   end
end)

Try this

local TweenDb = false

local tweenInfo = TweenInfo.new(0.1,Enum.EasingStyle.Linear,Enum.EasingDirection.Out,0,false,0)

local part = viewer.PrimaryPart
local goal = {CFrame = Camera.CFrame}

local tween = TweenServ:Create(part,tweenInfo,goal)
tween.Completed:Connect(function()
       debounce = false
end)

runServ.RenderStepped:Connect(function()
   if not debounce then
      debounce = true
      -- run tween
      tween:Play()
   end
end)
1 Like

So it works (in a way). The ViewModel goes to my camera position, but only does it once, meaning that it’s not running after the tween has been completed. Is there a way to fix this?

is debounce a local variable in your code?

local debounce = false
1 Like

Yes, it is.

(CHARACTERSSSSSSSSSSSSSSSSSSSSSSSSSSSSS)

Ah, I think I know what is happening, the Camera probably updates but because you haven’t updated the tween it will look like the tween is not playing.

So you will have to recreate the tween each RenderStepped, so something like the following:

local TweenDb = false

local tweenInfo = TweenInfo.new(0.1,Enum.EasingStyle.Linear,Enum.EasingDirection.Out,0,false,0)

local part = viewer.PrimaryPart
local goal = {CFrame = Camera.CFrame}

runServ.RenderStepped:Connect(function()
   if not debounce then
      debounce = true
      -- run tween

      local tween = TweenServ:Create(part,tweenInfo,goal)
      local t 
      t = tween.Completed:Connect(function()
         print("completed")
         debounce = false
         t:Disconnect()
       end)

      tween:Play()
   end
end)
1 Like

Nope. Still does the same thing. It does print completed though.

(30 charsssssssss)