Gun Sway Issue, Jitter/Lag/Stutter

Hello again

I’ve been struggling to get this issue to work for months now. Im trying to make my weapon sway in a realistic way to add some dynamic movements to my game. I don’t want to use animations because it doesn’t have the effect I want. Here are my scripts.

Springs Script------------------------------------------------:


local Spring = {};

function Spring.New(Position, Velocity, Target, K, Damper)
	local NewSpring = {
		Position = Position;
		Velocity = Velocity;
		Target = Target;
		K = K or 1;
		Damper = Damper or 1;
	};
	
	function NewSpring:Update(Target, K, Damper)
		NewSpring.Target = Target or NewSpring.Target;
		NewSpring.K = K or NewSpring.K;
		NewSpring.Damper = Damper or NewSpring.Damper;
		
		local Distance = (NewSpring.Target - NewSpring.Position);
		local Force = (Distance * NewSpring.K);
		NewSpring.Velocity = ((NewSpring.Velocity * (1 - NewSpring.Damper)) + Force);
		NewSpring.Position = (NewSpring.Position + NewSpring.Velocity);
		
		return NewSpring.Position;
	end;
	
	return NewSpring;
end;

return Spring;

Local Script:----------------------------------------------

function runFramework()
	CurrentEquipped = GunsFolder[Primary]:Clone()
	CurrentEquipped.Parent = camera
	
local SpringModule = require(workspace.Spring)
local Spring = SpringModule.New( CurrentEquipped:GetPrimaryPartCFrame().p, Vector3.new(), (camera.CFrame * CFrame.new(1,-1,-2)).p, 0.5, 1)

RunService.RenderStepped:Connect(function()

local springPosition = (camera.CFrame * CFrame.new(1,-1,-2)).Position
local update = Spring:Update(springPosition)
local springCFrame = CFrame.new(update) * CFrame.Angles(camera.CFrame:ToEulerAnglesXYZ())
	
--	CurrentEquipped:SetPrimaryPartCFrame(CurrentEquipped:GetPrimaryPartCFrame():lerp(camera.CFrame * CFrame.new(1,-1,-2), 0.3) )
	CurrentEquipped:SetPrimaryPartCFrame(springCFrame)
end)
			
end	


runFramework()
Players.PlayerAdded:Connect(runFramework)

WHAT DOESN’T WORK:
-Bind to renderstepped (putting it above and below the camera value
-Lerping ( Still jitters or moves too slowly for my liking)
-Accounting for time missed (delta) heres a link to what I mean

So thats pretty much it. I would really love assistance on this because i have absolutely no idea what to do.

Thanks :slight_smile:

2 Likes

I’ve writen an method on how to get nice and smooth sway here.
It has nothing to do with springs tho so it might not be exactly what you need but if you’re interrested you can check it out.

Also does the gun without sway stutter too or does it only stutter when sway is applied? (Disable sway and movearound to test it)

1 Like

Before i get into giving you some better spring code, I’d first recommend you do NOT use setprimarypartcframe for anything that’s going to be run on a frame to frame basis. It is not performant and has known floating point imprecision issues. You are much better off welding everything to a core part/primary part, and just setting the cframe of that object using .CFrame =

Now let me give you something from my fps.

Spring code

local spring = {}
 
function spring.new(position, velocity, target)
	local self = setmetatable({}, {__index = spring})
 
	self.position = position
	self.velocity = velocity
	self.target = target
	self.k = 0.1
	self.d = 0.4 -- friction constant
 
	return self
end
	 
function spring:update()
	local x = self.target - self.position
	local f = x * self.k
	self.velocity = (self.velocity * (1 - self.d)) + f
	self.position = self.position + self.velocity
end

Pseudo framework

local viewModel = {}

viewModel.SwayVector = Vector2.new(0,0)
viewModel.SwaySpring = spring.new(Vector3.new(0,0,0),Vector3.new(0,0,0),Vector3.new(0,0,0))
viewModel.NeckOffset = CFrame.new()

game:GetService('UserInputService').InputChanged:connect(function(inputObject)
	if inputObject.UserInputType == Enum.UserInputType.MouseMovement then
		local vec = Vector2.new(inputObject.Delta.X,inputObject.Delta.Y)
		viewModel.SwayVector = viewModel.SwayVector + vec
	end
end)

while game:GetService('RunService').RenderStepped:wait() do
	local MaxSway = 150
	if AIMING then
		MaxSway = 5
	end

    local x = math.clamp(viewModel.SwayVector.X, -MaxSway, MaxSway)
    local y = math.clamp(viewModel.SwayVector.Y, -MaxSway, MaxSway)

	viewModel.SwaySpring.target = Vector3.new(x,y,0)
	viewModel.SwaySpring:update()

	viewModel.NeckSway = CFrame.new() * CFrame.Angles(-math.rad(viewModel.SwaySpring.position.Y/5),-math.rad(viewModel.SwaySpring.position.X/5),0)

	viewModel.SwayVector = Vector2.new()
	if not game.Players.LocalPlayer.Character then
		break
	end
end

I’ve noticed what I provided was actually sway for moving the gun when you move your mouse left to right, and you wanted something for walk direction velocity. I’m happy to provide that as well if you need it, it uses the same spring system.

2 Likes

hey man, im sorry for the super late reply. Could you also help me with the walk direction velocity? That would be great.
Also what am i supposed to do with the

local viewModel = {}

i get the error that SwayVector isnt a part of model (which it isnt)

1 Like

Hey, sorry for the late reply. It doesn’t stutter when the sway isnt applied. I had a look at your code and i have no idea how to implement it. This is what i tried.

local mult = 1
local swayOffset = CFrame.new(1,-2,1)
function renderloop() --bind this camera render loop
local lastCameraCF = workspace.CurrentCamera.CFrame
local rotation = workspace.CurrentCamera.CFrame:toObjectSpace(lastCameraCF) --get cframe delta.
local x,y,z = rotation:ToOrientation() --I'm sure there are better ways to get rotation but this will work for now.
swayOffset = swayOffset:Lerp(CFrame.Angles(math.sin(x)*mult,math.sin(y)*mult,0), 0.1) --calculate the sway using SIN
hands.CFrame = hands.CFrame * swayOffset --apply the sway
lastCameraCF = workspace.CurrentCamera.CFrame --update the last cframe
end

i ended up teleporting under the map for some reason.

3 Likes

Are hands connected to the humanoid? You need to cframe the gun handle not the player hands. If you teleport the hands while they’re connected to the humanoid you’ll begin to teleport around.

1 Like

hands is the viewmodel and thats connected to the HumanoidRootPart

1 Like

Ah I see.
First off you’ll need to change
swayOffset = CFrame.new(1,-2,1)
into
swayOffset = hands.CFrame
Sorry my original code didn’t provide this for some reason.
Anyways the current code is teleporting the hands around and since the swayOffset was 1,-2,1 it teleported the humanoid inside of the baseplate.

I’ll tell you what my setup was so you’ll better understand the code:
I had a main part named gun. All of the gun parts were connected to this single part using motor6ds. This main part itself was constantly being cframed to the players head. Then I apply the sway onto the main part and because of the motor6ds all of the gun parts moved along.

3 Likes

+1 I found that out the hard way.

You can weld your gun model and move the primarypart CFrame in relation to the UpperTorso (R15) or Torso (R6). I’ve heard of some people using the head as a relative point but, I don’t see a reason why the Torso wouldn’t work.

1 Like